diff options
18 files changed, 743 insertions, 384 deletions
diff --git a/api/current.txt b/api/current.txt index c45706e..bd7fffd 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23462,6 +23462,17 @@ package android.util { method public void set(T, V); } + public class PropertyValueModel extends android.util.ValueModel { + method public T get(); + method public H getHost(); + method public android.util.Property<H, T> getProperty(); + method public java.lang.Class<T> getType(); + method public static android.util.PropertyValueModel<H, T> of(H, android.util.Property<H, T>); + method public static android.util.PropertyValueModel<H, T> of(H, java.lang.Class<T>, java.lang.String); + method public static android.util.PropertyValueModel of(java.lang.Object, java.lang.String); + method public void set(T); + } + public class SparseArray implements java.lang.Cloneable { ctor public SparseArray(); ctor public SparseArray(int); @@ -23610,6 +23621,14 @@ package android.util { field public int type; } + public abstract class ValueModel { + ctor protected ValueModel(); + method public abstract T get(); + method public abstract java.lang.Class<T> getType(); + method public abstract void set(T); + field public static final android.util.ValueModel EMPTY; + } + public class Xml { method public static android.util.AttributeSet asAttributeSet(org.xmlpull.v1.XmlPullParser); method public static android.util.Xml.Encoding findEncodingByName(java.lang.String) throws java.io.UnsupportedEncodingException; @@ -27995,10 +28014,12 @@ package android.widget { method public abstract void onSelectedDayChange(android.widget.CalendarView, int, int, int); } - public class CheckBox extends android.widget.CompoundButton { + public class CheckBox extends android.widget.CompoundButton implements android.widget.ValueEditor { ctor public CheckBox(android.content.Context); ctor public CheckBox(android.content.Context, android.util.AttributeSet); ctor public CheckBox(android.content.Context, android.util.AttributeSet, int); + method public android.util.ValueModel<java.lang.Boolean> getValueModel(); + method public void setValueModel(android.util.ValueModel<java.lang.Boolean>); } public abstract interface Checkable { @@ -28171,14 +28192,16 @@ package android.widget { method public void setSize(int, int); } - public class EditText extends android.widget.TextView { + public class EditText extends android.widget.TextView implements android.widget.ValueEditor { ctor public EditText(android.content.Context); ctor public EditText(android.content.Context, android.util.AttributeSet); ctor public EditText(android.content.Context, android.util.AttributeSet, int); method public void extendSelection(int); + method public android.util.ValueModel<java.lang.CharSequence> getValueModel(); method public void selectAll(); method public void setSelection(int, int); method public void setSelection(int); + method public void setValueModel(android.util.ValueModel<java.lang.CharSequence>); } public abstract interface ExpandableListAdapter { @@ -29204,11 +29227,13 @@ package android.widget { method public abstract java.lang.Object[] getSections(); } - public class SeekBar extends android.widget.AbsSeekBar { + public class SeekBar extends android.widget.AbsSeekBar implements android.widget.ValueEditor { ctor public SeekBar(android.content.Context); ctor public SeekBar(android.content.Context, android.util.AttributeSet); ctor public SeekBar(android.content.Context, android.util.AttributeSet, int); + method public android.util.ValueModel<java.lang.Integer> getValueModel(); method public void setOnSeekBarChangeListener(android.widget.SeekBar.OnSeekBarChangeListener); + method public void setValueModel(android.util.ValueModel<java.lang.Integer>); } public static abstract interface SeekBar.OnSeekBarChangeListener { @@ -29781,6 +29806,11 @@ package android.widget { method public android.widget.TextView getText2(); } + public abstract interface ValueEditor { + method public abstract android.util.ValueModel<T> getValueModel(); + method public abstract void setValueModel(android.util.ValueModel<T>); + } + public class VideoView extends android.view.SurfaceView implements android.widget.MediaController.MediaPlayerControl { ctor public VideoView(android.content.Context); ctor public VideoView(android.content.Context, android.util.AttributeSet); diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 3314dc3..71e840e 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -31,6 +31,7 @@ static struct { { AID_MEDIA, "media.player" }, { AID_MEDIA, "media.camera" }, { AID_MEDIA, "media.audio_policy" }, + { AID_MEDIA, "android.media.IAAHMetaDataService" }, { AID_DRM, "drm.drmManager" }, { AID_NFC, "nfc" }, { AID_BLUETOOTH, "bluetooth" }, diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5f65f08..c9a2f91 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -112,7 +112,6 @@ import java.util.TimeZone; import java.util.regex.Pattern; import libcore.io.DropBox; -import libcore.io.EventLogger; import libcore.io.IoUtils; import dalvik.system.CloseGuard; @@ -4959,13 +4958,6 @@ public final class ActivityThread { } } - private static class EventLoggingReporter implements EventLogger.Reporter { - @Override - public void report (int code, Object... list) { - EventLog.writeEvent(code, list); - } - } - private class DropBoxReporter implements DropBox.Reporter { private DropBoxManager dropBox; @@ -4995,9 +4987,6 @@ public final class ActivityThread { Environment.initForCurrentUser(); - // Set the reporter for event logging in libcore - EventLogger.setReporter(new EventLoggingReporter()); - Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 5ef86b1..200b57b 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -411,9 +411,6 @@ public class Time { * @throws android.util.TimeFormatException if s cannot be parsed. */ public boolean parse(String s) { - if (s == null) { - throw new NullPointerException("time string is null"); - } if (nativeParse(s)) { timezone = TIMEZONE_UTC; return true; diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java new file mode 100755 index 0000000..eb9c47d --- /dev/null +++ b/core/java/android/util/PropertyValueModel.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +/** + * A value model for a {@link Property property} of a host object. This class can be used for + * both reflective and non-reflective property implementations. + * + * @param <H> the host type, where the host is the object that holds this property + * @param <T> the value type + * + * @see Property + * @see ValueModel + */ +public class PropertyValueModel<H, T> extends ValueModel<T> { + private final H mHost; + private final Property<H, T> mProperty; + + private PropertyValueModel(H host, Property<H, T> property) { + mProperty = property; + mHost = host; + } + + /** + * Returns the host. + * + * @return the host + */ + public H getHost() { + return mHost; + } + + /** + * Returns the property. + * + * @return the property + */ + public Property<H, T> getProperty() { + return mProperty; + } + + @Override + public Class<T> getType() { + return mProperty.getType(); + } + + @Override + public T get() { + return mProperty.get(mHost); + } + + @Override + public void set(T value) { + mProperty.set(mHost, value); + } + + /** + * Return an appropriate PropertyValueModel for this host and property. + * + * @param host the host + * @param property the property + * @return the value model + */ + public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) { + return new PropertyValueModel<H, T>(host, property); + } + + /** + * Return a PropertyValueModel for this {@code host} and a + * reflective property, constructed from this {@code propertyType} and {@code propertyName}. + * + * @param host + * @param propertyType the property type + * @param propertyName the property name + * @return a value model with this host and a reflective property with this type and name + * + * @see Property#of + */ + public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType, + String propertyName) { + return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName)); + } + + private static Class getNullaryMethodReturnType(Class c, String name) { + try { + return c.getMethod(name).getReturnType(); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static Class getFieldType(Class c, String name) { + try { + return c.getField(name).getType(); + } catch (NoSuchFieldException e) { + return null; + } + } + + private static String capitalize(String name) { + if (name.isEmpty()) { + return name; + } + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + /** + * Return a PropertyValueModel for this {@code host} and and {@code propertyName}. + * + * @param host the host + * @param propertyName the property name + * @return a value model with this host and a reflective property with this name + */ + public static PropertyValueModel of(Object host, String propertyName) { + Class clazz = host.getClass(); + String suffix = capitalize(propertyName); + Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix); + if (propertyType == null) { + propertyType = getNullaryMethodReturnType(clazz, "is" + suffix); + } + if (propertyType == null) { + propertyType = getFieldType(clazz, propertyName); + } + if (propertyType == null) { + throw new NoSuchPropertyException(propertyName); + } + return of(host, propertyType, propertyName); + } +} diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java new file mode 100755 index 0000000..4789682 --- /dev/null +++ b/core/java/android/util/ValueModel.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +/** + * A ValueModel is an abstraction for a 'slot' or place in memory in which a value + * may be stored and retrieved. A common implementation of ValueModel is a regular property of + * an object, whose value may be retrieved by calling the appropriate <em>getter</em> + * method and set by calling the corresponding <em>setter</em> method. + * + * @param <T> the value type + * + * @see PropertyValueModel + */ +public abstract class ValueModel<T> { + /** + * The empty model should be used in place of {@code null} to indicate that a + * model has not been set. The empty model has no value and does nothing when it is set. + */ + public static final ValueModel EMPTY = new ValueModel() { + @Override + public Class getType() { + return Object.class; + } + + @Override + public Object get() { + return null; + } + + @Override + public void set(Object value) { + + } + }; + + protected ValueModel() { + } + + /** + * Returns the type of this property. + * + * @return the property type + */ + public abstract Class<T> getType(); + + /** + * Returns the value of this property. + * + * @return the property value + */ + public abstract T get(); + + /** + * Sets the value of this property. + * + * @param value the new value for this property + */ + public abstract void set(T value); +}
\ No newline at end of file diff --git a/core/java/android/view/SimulatedTrackball.java b/core/java/android/view/SimulatedTrackball.java new file mode 100644 index 0000000..1878e28 --- /dev/null +++ b/core/java/android/view/SimulatedTrackball.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.Message; +import android.os.SystemClock; +import android.os.SystemProperties; + +/** + * This class creates trackball events from touchpad events. + * + * @see ViewRootImpl + */ +class SimulatedTrackball { + + // Maximum difference in milliseconds between the down and up of a touch + // event for it to be considered a tap + // TODO:Read this value from a configuration file + private static final int MAX_TAP_TIME = 250; + private static final int FLICK_MSG_ID = 313; + + // The position of the previous touchpad event + private float mLastTouchpadXPosition; + private float mLastTouchpadYPosition; + // Where the touchpad was initially pressed + private float mTouchpadEnterXPosition; + private float mTouchpadEnterYPosition; + // When the most recent ACTION_HOVER_ENTER occurred + private long mLastTouchPadStartTimeMs = 0; + // When the most recent direction key was sent + private long mLastTouchPadKeySendTimeMs = 0; + // When the most recent touch event of any type occurred + private long mLastTouchPadEventTimeMs = 0; + + // How quickly keys were sent; + private int mKeySendRateMs = 0; + private int mLastKeySent; + // Last movement in device screen pixels + private float mLastMoveX = 0; + private float mLastMoveY = 0; + // Offset from the initial touch. Gets reset as direction keys are sent. + private float mAccumulatedX; + private float mAccumulatedY; + + // Change in position allowed during tap events + private float mTouchSlop; + private float mTouchSlopSquared; + // Has the TouchSlop constraint been invalidated + private boolean mAlwaysInTapRegion = true; + + // Most recent event. Used to determine what device sent the event. + private MotionEvent mRecentEvent; + + // TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to + // read this from a config file instead + private int mDistancePerTick; + private int mDistancePerTickSquared; + // Highest rate that the flinged events can occur at before dying out + private int mMaxRepeatDelay; + // The square of the minimum distance needed for a flick to register + private int mMinFlickDistanceSquared; + // How quickly the repeated events die off + private float mFlickDecay; + + public SimulatedTrackball() { + mDistancePerTick = SystemProperties.getInt("persist.vr_dist_tick", 64); + mDistancePerTickSquared = mDistancePerTick * mDistancePerTick; + mMaxRepeatDelay = SystemProperties.getInt("persist.vr_repeat_delay", 300); + mMinFlickDistanceSquared = SystemProperties.getInt("persist.vr_min_flick", 20); + mMinFlickDistanceSquared *= mMinFlickDistanceSquared; + mFlickDecay = Float.parseFloat(SystemProperties.get( + "persist.sys.vr_flick_decay", "1.3")); + mTouchSlop = ViewConfiguration.getTouchSlop(); + mTouchSlopSquared = mTouchSlop * mTouchSlop; + } + + private final Handler mHandler = new Handler(new Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what != FLICK_MSG_ID) + return false; + + final long time = SystemClock.uptimeMillis(); + ViewRootImpl viewroot = (ViewRootImpl) msg.obj; + // Send the key + viewroot.enqueueInputEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, msg.arg2, 0, mRecentEvent.getMetaState(), + mRecentEvent.getDeviceId(), 0, + KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource())); + viewroot.enqueueInputEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, msg.arg2, 0, mRecentEvent.getMetaState(), + mRecentEvent.getDeviceId(), 0, + KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource())); + Message msgCopy = Message.obtain(msg); + // Increase the delay by the decay factor + msgCopy.arg1 = (int) Math.ceil(mFlickDecay * msgCopy.arg1); + if (msgCopy.arg1 <= mMaxRepeatDelay) { + // Send the key again in arg1 milliseconds + mHandler.sendMessageDelayed(msgCopy, msgCopy.arg1); + } + return false; + } + }); + + public void updateTrackballDirection(ViewRootImpl viewroot, MotionEvent event) { + // Store what time the touchpad event occurred + final long time = SystemClock.uptimeMillis(); + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + mLastTouchPadStartTimeMs = time; + mAlwaysInTapRegion = true; + mTouchpadEnterXPosition = event.getX(); + mTouchpadEnterYPosition = event.getY(); + mAccumulatedX = 0; + mAccumulatedY = 0; + mLastMoveX = 0; + mLastMoveY = 0; + // Clear any flings + mHandler.removeMessages(FLICK_MSG_ID); + break; + case MotionEvent.ACTION_HOVER_MOVE: + // Determine whether the move is slop or an intentional move + float deltaX = event.getX() - mTouchpadEnterXPosition; + float deltaY = event.getY() - mTouchpadEnterYPosition; + if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) { + mAlwaysInTapRegion = false; + } + + // Find the difference in position between the two most recent + // touchpad events + mLastMoveX = event.getX() - mLastTouchpadXPosition; + mLastMoveY = event.getY() - mLastTouchpadYPosition; + mAccumulatedX += mLastMoveX; + mAccumulatedY += mLastMoveY; + float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX; + float mAccumulatedYSquared = mAccumulatedY * mAccumulatedY; + // Determine if we've moved far enough to send a key press + if (mAccumulatedXSquared > mDistancePerTickSquared || + mAccumulatedYSquared > mDistancePerTickSquared) { + float dominantAxis; + float sign; + boolean isXAxis; + int key; + int repeatCount = 0; + // Determine dominant axis + if (mAccumulatedXSquared > mAccumulatedYSquared) { + dominantAxis = mAccumulatedX; + isXAxis = true; + } else { + dominantAxis = mAccumulatedY; + isXAxis = false; + } + // Determine sign of axis + sign = (dominantAxis > 0) ? 1 : -1; + // Determine key to send + if (isXAxis) { + key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_RIGHT : + KeyEvent.KEYCODE_DPAD_LEFT; + } else { + key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; + } + // Send key until maximum distance constraint is satisfied + while (dominantAxis * dominantAxis > mDistancePerTickSquared) { + repeatCount++; + dominantAxis -= sign * mDistancePerTick; + viewroot.enqueueInputEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, key, 0, event.getMetaState(), + event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK, event.getSource())); + viewroot.enqueueInputEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, key, 0, event.getMetaState(), + event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK, event.getSource())); + } + // Save new axis values + mAccumulatedX = isXAxis ? dominantAxis : 0; + mAccumulatedY = isXAxis ? 0 : dominantAxis; + + mLastKeySent = key; + mKeySendRateMs = (int) ((time - mLastTouchPadKeySendTimeMs) / repeatCount); + mLastTouchPadKeySendTimeMs = time; + } + break; + case MotionEvent.ACTION_HOVER_EXIT: + if (time - mLastTouchPadStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) { + // Trackball Down + MotionEvent trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time, + MotionEvent.ACTION_DOWN, 0, 0, 0, 0, event.getMetaState(), + 10f, 10f, event.getDeviceId(), 0); + trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL); + viewroot.enqueueInputEvent(trackballEvent); + // Trackball Release + trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time, + MotionEvent.ACTION_UP, 0, 0, 0, 0, event.getMetaState(), + 10f, 10f, event.getDeviceId(), 0); + trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL); + viewroot.enqueueInputEvent(trackballEvent); + } else { + float xMoveSquared = mLastMoveX * mLastMoveX; + float yMoveSquared = mLastMoveY * mLastMoveY; + // Determine whether the last gesture was a fling. + if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared && + time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME && + mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) { + Message message = Message.obtain(mHandler, FLICK_MSG_ID, + mKeySendRateMs, mLastKeySent, viewroot); + mRecentEvent = event; + mHandler.sendMessageDelayed(message, mKeySendRateMs); + } + } + break; + } + // Store touch event position and time + mLastTouchPadEventTimeMs = time; + mLastTouchpadXPosition = event.getX(); + mLastTouchpadYPosition = event.getY(); + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 91df4b5..d02745c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -146,6 +146,8 @@ public final class ViewRootImpl implements ViewParent, final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); + final SimulatedTrackball mSimulatedTrackball = new SimulatedTrackball(); + int mLastJoystickXDirection; int mLastJoystickYDirection; int mLastJoystickXKeyCode; @@ -3379,7 +3381,6 @@ public final class ViewRootImpl implements ViewParent, if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); } - if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching generic motion " + event + " to " + mView); @@ -3406,12 +3407,17 @@ public final class ViewRootImpl implements ViewParent, private void deliverGenericMotionEventPostIme(QueuedInputEvent q) { final MotionEvent event = (MotionEvent) q.mEvent; - final boolean isJoystick = (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + final int source = event.getSource(); + final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + final boolean isTouchPad = (source & InputDevice.SOURCE_CLASS_POSITION) != 0; // If there is no view, then the event will not be handled. if (mView == null || !mAdded) { if (isJoystick) { updateJoystickDirection(event, false); + } else if (isTouchPad) { + //Convert TouchPad motion into a TrackBall event + mSimulatedTrackball.updateTrackballDirection(this, event); } finishInputEvent(q, false); return; @@ -3421,6 +3427,9 @@ public final class ViewRootImpl implements ViewParent, if (mView.dispatchGenericMotionEvent(event)) { if (isJoystick) { updateJoystickDirection(event, false); + } else if (isTouchPad) { + //Convert TouchPad motion into a TrackBall event + mSimulatedTrackball.updateTrackballDirection(this, event); } finishInputEvent(q, true); return; @@ -3431,6 +3440,10 @@ public final class ViewRootImpl implements ViewParent, // those. updateJoystickDirection(event, true); finishInputEvent(q, true); + } else if (isTouchPad) { + //Convert TouchPad motion into a TrackBall event + mSimulatedTrackball.updateTrackballDirection(this, event); + finishInputEvent(q, true); } else { finishInputEvent(q, false); } diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java index f1804f8..41ab5f2 100644 --- a/core/java/android/widget/CheckBox.java +++ b/core/java/android/widget/CheckBox.java @@ -20,6 +20,7 @@ import android.content.Context; import android.util.AttributeSet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.util.ValueModel; /** @@ -55,7 +56,9 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#View View Attributes} * </p> */ -public class CheckBox extends CompoundButton { +public class CheckBox extends CompoundButton implements ValueEditor<Boolean> { + private ValueModel<Boolean> mValueModel = ValueModel.EMPTY; + public CheckBox(Context context) { this(context, null); } @@ -79,4 +82,22 @@ public class CheckBox extends CompoundButton { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CheckBox.class.getName()); } + + @Override + public ValueModel<Boolean> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<Boolean> valueModel) { + mValueModel = valueModel; + setChecked(mValueModel.get()); + } + + @Override + public boolean performClick() { + boolean handled = super.performClick(); + mValueModel.set(isChecked()); + return handled; + } } diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 57e51c2..ec81214 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.graphics.Rect; import android.text.Editable; import android.text.Selection; import android.text.Spannable; @@ -24,6 +25,7 @@ import android.text.TextUtils; import android.text.method.ArrowKeyMovementMethod; import android.text.method.MovementMethod; import android.util.AttributeSet; +import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -47,7 +49,9 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#TextView TextView Attributes}, * {@link android.R.styleable#View View Attributes} */ -public class EditText extends TextView { +public class EditText extends TextView implements ValueEditor<CharSequence> { + private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY; + public EditText(Context context) { this(context, null); } @@ -128,4 +132,21 @@ public class EditText extends TextView { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(EditText.class.getName()); } + + @Override + public ValueModel<CharSequence> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<CharSequence> valueModel) { + mValueModel = valueModel; + setText(mValueModel.get()); + } + + @Override + void sendAfterTextChanged(Editable text) { + super.sendAfterTextChanged(text); + mValueModel.set(text); + } } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 4918e48..6cfeb15 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -470,6 +470,11 @@ public class NumberPicker extends LinearLayout { private final PressedStateHelper mPressedStateHelper; /** + * The keycode of the last handled DPAD down event. + */ + private int mLastHandledDownDpadKeyCode = -1; + + /** * Interface to listen for changes of the current value. */ public interface OnValueChangeListener { @@ -936,6 +941,31 @@ public class NumberPicker extends LinearLayout { case KeyEvent.KEYCODE_ENTER: removeAllCallbacks(); break; + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_DPAD_UP: + if (!mHasSelectorWheel) { + break; + } + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) + ? getValue() < getMaxValue() : getValue() > getMinValue()) { + requestFocus(); + mLastHandledDownDpadKeyCode = keyCode; + removeAllCallbacks(); + if (mFlingScroller.isFinished()) { + changeValueByOne(keyCode == KeyEvent.KEYCODE_DPAD_DOWN); + } + return true; + } + break; + case KeyEvent.ACTION_UP: + if (mLastHandledDownDpadKeyCode == keyCode) { + mLastHandledDownDpadKeyCode = -1; + return true; + } + break; + } } return super.dispatchKeyEvent(event); } diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java index 2737f94..a6486a8 100644 --- a/core/java/android/widget/SeekBar.java +++ b/core/java/android/widget/SeekBar.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -33,7 +34,7 @@ import android.view.accessibility.AccessibilityNodeInfo; * * @attr ref android.R.styleable#SeekBar_thumb */ -public class SeekBar extends AbsSeekBar { +public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> { /** * A callback that notifies clients when the progress level has been @@ -69,8 +70,9 @@ public class SeekBar extends AbsSeekBar { void onStopTrackingTouch(SeekBar seekBar); } + private ValueModel<Integer> mValueModel = ValueModel.EMPTY; private OnSeekBarChangeListener mOnSeekBarChangeListener; - + public SeekBar(Context context) { this(context, null); } @@ -89,9 +91,23 @@ public class SeekBar extends AbsSeekBar { if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser); + if (fromUser) { + mValueModel.set(getProgress()); + } } } + @Override + public ValueModel<Integer> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<Integer> valueModel) { + mValueModel = valueModel; + setProgress(mValueModel.get()); + } + /** * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also * provides notifications of when the user starts and stops a touch gesture within the SeekBar. diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java new file mode 100755 index 0000000..2b91abf --- /dev/null +++ b/core/java/android/widget/ValueEditor.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.util.ValueModel; + +/** + * An interface for editors of simple values. Classes implementing this interface are normally + * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable + * user interface to display and edit values of the specified type. This interface is + * intended to describe editors for simple types, like {@code boolean}, {@code int} or + * {@code String}, where the values themselves are immutable. + * <p> + * For example, {@link android.widget.CheckBox CheckBox} implements + * this interface for the Boolean type as it is capable of providing an appropriate + * mechanism for displaying and changing the value of a Boolean property. + * + * @param <T> the value type that this editor supports + */ +public interface ValueEditor<T> { + /** + * Return the last value model that was set. If no value model has been set, the editor + * should return the value {@link android.util.ValueModel#EMPTY}. + * + * @return the value model + */ + public ValueModel<T> getValueModel(); + + /** + * Sets the value model for this editor. When the value model is set, the editor should + * retrieve the value from the value model, using {@link android.util.ValueModel#get()}, + * and set its internal state accordingly. Likewise, when the editor's internal state changes + * it should update the value model by calling {@link android.util.ValueModel#set(T)} + * with the appropriate value. + * + * @param valueModel the new value model for this editor. + */ + public void setValueModel(ValueModel<T> valueModel); +} diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp index aa2c5f39..0a59ae7 100644 --- a/core/jni/android_text_format_Time.cpp +++ b/core/jni/android_text_format_Time.cpp @@ -23,7 +23,6 @@ #include "jni.h" #include "utils/misc.h" #include "android_runtime/AndroidRuntime.h" -#include "ScopedStringChars.h" #include "TimeUtils.h" #include <nativehelper/JNIHelp.h> #include <cutils/tztime.h> @@ -72,10 +71,11 @@ static inline bool java2time(JNIEnv* env, Time* t, jobject o) t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField); bool allDay = env->GetBooleanField(o, g_allDayField); if (allDay && - ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "allDay is true but sec, min, hour are not 0."); - return false; + ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) { + char msg[100]; + sprintf(msg, "allDay is true but sec, min, hour are not 0."); + jniThrowException(env, "java/lang/IllegalArgumentException", msg); + return false; } return true; } @@ -313,7 +313,7 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, static jstring android_text_format_Time_toString(JNIEnv* env, jobject This) { Time t; - if (!java2time(env, &t, This)) return env->NewStringUTF(""); + if (!java2time(env, &t, This)) return env->NewStringUTF("");; ACQUIRE_TIMEZONE(This, t) String8 r = t.toString(); @@ -365,30 +365,32 @@ static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis // ============================================================================ // Just do this here because it's not worth recreating the strings -static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul, - bool* thrown) +static int get_char(JNIEnv* env, const jchar *s, int spos, int mul, + bool *thrown) { jchar c = s[spos]; if (c >= '0' && c <= '9') { return (c - '0') * mul; } else { if (!*thrown) { - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Parse error at pos=%d", spos); + char msg[100]; + sprintf(msg, "Parse error at pos=%d", spos); + jniThrowException(env, "android/util/TimeFormatException", msg); *thrown = true; } return 0; } } -static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected) +static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected) { jchar c = s[spos]; if (c != expected) { - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Unexpected character 0x%02x at pos=%d. Expected %c.", - c, spos, expected); - return false; + char msg[100]; + sprintf(msg, "Unexpected character 0x%02x at pos=%d. Expected %c.", c, spos, + expected); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; } return true; } @@ -397,19 +399,20 @@ static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj) { jsize len = env->GetStringLength(strObj); - if (len < 8) { - jniThrowException(env, "android/util/TimeFormatException", - "String too short -- expected at least 8 characters."); - return false; - } + const jchar *s = env->GetStringChars(strObj, NULL); + bool thrown = false; + int n; jboolean inUtc = false; - ScopedStringChars s(env, strObj); + if (len < 8) { + char msg[100]; + sprintf(msg, "String too short -- expected at least 8 characters."); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; + } // year - int n; - bool thrown = false; n = get_char(env, s, 0, 1000, &thrown); n += get_char(env, s, 1, 100, &thrown); n += get_char(env, s, 2, 10, &thrown); @@ -456,7 +459,7 @@ static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstrin if (len > 15) { // Z if (!check_char(env, s, 15, 'Z')) return false; - inUtc = true; + inUtc = true; } } else { env->SetBooleanField(This, g_allDayField, JNI_TRUE); @@ -469,7 +472,8 @@ static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstrin env->SetIntField(This, g_ydayField, 0); env->SetIntField(This, g_isdstField, -1); env->SetLongField(This, g_gmtoffField, 0); - + + env->ReleaseStringChars(strObj, s); return inUtc; } @@ -478,19 +482,19 @@ static jboolean android_text_format_Time_parse3339(JNIEnv* env, jstring strObj) { jsize len = env->GetStringLength(strObj); + const jchar *s = env->GetStringChars(strObj, NULL); + + bool thrown = false; + int n; + jboolean inUtc = false; + if (len < 10) { jniThrowException(env, "android/util/TimeFormatException", - "String too short --- expected at least 10 characters."); + "Time input is too short; must be at least 10 characters"); return false; } - jboolean inUtc = false; - - ScopedStringChars s(env, strObj); - // year - int n; - bool thrown = false; n = get_char(env, s, 0, 1000, &thrown); n += get_char(env, s, 1, 100, &thrown); n += get_char(env, s, 2, 10, &thrown); @@ -521,28 +525,28 @@ static jboolean android_text_format_Time_parse3339(JNIEnv* env, // T if (!check_char(env, s, 10, 'T')) return false; - env->SetBooleanField(This, g_allDayField, JNI_FALSE); + env->SetBooleanField(This, g_allDayField, JNI_FALSE); // hour n = get_char(env, s, 11, 10, &thrown); n += get_char(env, s, 12, 1, &thrown); if (thrown) return false; - int hour = n; + int hour = n; // env->SetIntField(This, g_hourField, n); + + // : + if (!check_char(env, s, 13, ':')) return false; - // : - if (!check_char(env, s, 13, ':')) return false; - - // minute + // minute n = get_char(env, s, 14, 10, &thrown); n += get_char(env, s, 15, 1, &thrown); if (thrown) return false; - int minute = n; + int minute = n; // env->SetIntField(This, g_minField, n); - // : - if (!check_char(env, s, 16, ':')) return false; + // : + if (!check_char(env, s, 16, ':')) return false; - // second + // second n = get_char(env, s, 17, 10, &thrown); n += get_char(env, s, 18, 1, &thrown); if (thrown) return false; @@ -562,63 +566,64 @@ static jboolean android_text_format_Time_parse3339(JNIEnv* env, if (len > tz_index) { char c = s[tz_index]; - // NOTE: the offset is meant to be subtracted to get from local time - // to UTC. we therefore use 1 for '-' and -1 for '+'. - switch (c) { - case 'Z': - // Zulu time -- UTC - offset = 0; - break; - case '-': + // NOTE: the offset is meant to be subtracted to get from local time + // to UTC. we therefore use 1 for '-' and -1 for '+'. + switch (c) { + case 'Z': + // Zulu time -- UTC + offset = 0; + break; + case '-': offset = 1; - break; - case '+': + break; + case '+': offset = -1; - break; - default: - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Unexpected character 0x%02x at position %d. Expected + or -", - c, tz_index); - return false; - } + break; + default: + char msg[100]; + sprintf(msg, "Unexpected character 0x%02x at position %d. Expected + or -", + c, tz_index); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; + } inUtc = true; - if (offset != 0) { - if (len < tz_index + 6) { - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Unexpected length; should be %d characters", - tz_index + 6); - return false; - } - - // hour - n = get_char(env, s, tz_index + 1, 10, &thrown); - n += get_char(env, s, tz_index + 2, 1, &thrown); - if (thrown) return false; - n *= offset; - hour += n; - - // : - if (!check_char(env, s, tz_index + 3, ':')) return false; - - // minute - n = get_char(env, s, tz_index + 4, 10, &thrown); - n += get_char(env, s, tz_index + 5, 1, &thrown); - if (thrown) return false; - n *= offset; - minute += n; - } - } - env->SetIntField(This, g_hourField, hour); + if (offset != 0) { + if (len < tz_index + 6) { + char msg[100]; + sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; + } + + // hour + n = get_char(env, s, tz_index + 1, 10, &thrown); + n += get_char(env, s, tz_index + 2, 1, &thrown); + if (thrown) return false; + n *= offset; + hour += n; + + // : + if (!check_char(env, s, tz_index + 3, ':')) return false; + + // minute + n = get_char(env, s, tz_index + 4, 10, &thrown); + n += get_char(env, s, tz_index + 5, 1, &thrown); + if (thrown) return false; + n *= offset; + minute += n; + } + } + env->SetIntField(This, g_hourField, hour); env->SetIntField(This, g_minField, minute); - if (offset != 0) { - // we need to normalize after applying the hour and minute offsets - android_text_format_Time_normalize(env, This, false /* use isdst */); - // The timezone is set to UTC in the calling Java code. - } + if (offset != 0) { + // we need to normalize after applying the hour and minute offsets + android_text_format_Time_normalize(env, This, false /* use isdst */); + // The timezone is set to UTC in the calling Java code. + } } else { - env->SetBooleanField(This, g_allDayField, JNI_TRUE); + env->SetBooleanField(This, g_allDayField, JNI_TRUE); env->SetIntField(This, g_hourField, 0); env->SetIntField(This, g_minField, 0); env->SetIntField(This, g_secField, 0); @@ -628,7 +633,8 @@ static jboolean android_text_format_Time_parse3339(JNIEnv* env, env->SetIntField(This, g_ydayField, 0); env->SetIntField(This, g_isdstField, -1); env->SetLongField(This, g_gmtoffField, 0); - + + env->ReleaseStringChars(strObj, s); return inUtc; } diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java index 4480151..4480151 100644 --- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java +++ b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java diff --git a/services/java/com/android/server/updates/CertPinInstallReceiver.java b/services/java/com/android/server/updates/CertPinInstallReceiver.java deleted file mode 100644 index c03fbc3..0000000 --- a/services/java/com/android/server/updates/CertPinInstallReceiver.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.updates; - -public class CertPinInstallReceiver extends ConfigUpdateInstallReceiver { - - public CertPinInstallReceiver() { - super("/data/misc/keychain/", "pins", "metadata/", "version"); - } -} diff --git a/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java b/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java deleted file mode 100644 index b6742a1..0000000 --- a/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.updates; - -import android.content.Context; -import android.content.Intent; -import android.test.AndroidTestCase; -import android.provider.Settings; -import android.util.Base64; -import android.util.Log; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.security.cert.CertificateFactory; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.Signature; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.KeyFactory; -import java.util.HashSet; -import java.io.*; -import libcore.io.IoUtils; - -/** - * Tests for {@link com.android.server.CertPinInstallReceiver} - */ -public class CertPinInstallReceiverTest extends AndroidTestCase { - - private static final String TAG = "CertPinInstallReceiverTest"; - - private static final String PINLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/"; - - public static final String PINLIST_CONTENT_PATH = PINLIST_ROOT + "pins"; - public static final String PINLIST_METADATA_PATH = PINLIST_CONTENT_PATH + "metadata"; - - public static final String PINLIST_CONTENT_URL_KEY = "pinlist_content_url"; - public static final String PINLIST_METADATA_URL_KEY = "pinlist_metadata_url"; - public static final String PINLIST_CERTIFICATE_KEY = "config_update_certificate"; - public static final String PINLIST_VERSION_KEY = "pinlist_version"; - - private static final String EXTRA_CONTENT_PATH = "CONTENT_PATH"; - private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH"; - private static final String EXTRA_SIGNATURE = "SIGNATURE"; - private static final String EXTRA_VERSION_NUMBER = "VERSION"; - - public static final String TEST_CERT = "" + - "MIIDsjCCAxugAwIBAgIJAPLf2gS0zYGUMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYDVQQGEwJVUzET" + - "MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEPMA0GA1UEChMGR29v" + - "Z2xlMRAwDgYDVQQLEwd0ZXN0aW5nMRYwFAYDVQQDEw1HZXJlbXkgQ29uZHJhMSEwHwYJKoZIhvcN" + - "AQkBFhJnY29uZHJhQGdvb2dsZS5jb20wHhcNMTIwNzE0MTc1MjIxWhcNMTIwODEzMTc1MjIxWjCB" + - "mDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZp" + - "ZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHdGVzdGluZzEWMBQGA1UEAxMNR2VyZW15IENv" + - "bmRyYTEhMB8GCSqGSIb3DQEJARYSZ2NvbmRyYUBnb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA" + - "A4GNADCBiQKBgQCjGGHATBYlmas+0sEECkno8LZ1KPglb/mfe6VpCT3GhSr+7br7NG/ZwGZnEhLq" + - "E7YIH4fxltHmQC3Tz+jM1YN+kMaQgRRjo/LBCJdOKaMwUbkVynAH6OYsKevjrOPk8lfM5SFQzJMG" + - "sA9+Tfopr5xg0BwZ1vA/+E3mE7Tr3M2UvwIDAQABo4IBADCB/TAdBgNVHQ4EFgQUhzkS9E6G+x8W" + - "L4EsmRjDxu28tHUwgc0GA1UdIwSBxTCBwoAUhzkS9E6G+x8WL4EsmRjDxu28tHWhgZ6kgZswgZgx" + - "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3" + - "MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB3Rlc3RpbmcxFjAUBgNVBAMTDUdlcmVteSBDb25k" + - "cmExITAfBgkqhkiG9w0BCQEWEmdjb25kcmFAZ29vZ2xlLmNvbYIJAPLf2gS0zYGUMAwGA1UdEwQF" + - "MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYiugFDmbDOQ2U/+mqNt7o8ftlEo9SJrns6O8uTtK6AvR" + - "orDrR1AXTXkuxwLSbmVfedMGOZy7Awh7iZa8hw5x9XmUudfNxvmrKVEwGQY2DZ9PXbrnta/dwbhK" + - "mWfoepESVbo7CKIhJp8gRW0h1Z55ETXD57aGJRvQS4pxkP8ANhM="; - - - public static final String TEST_KEY = "" + - "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKMYYcBMFiWZqz7SwQQKSejwtnUo" + - "+CVv+Z97pWkJPcaFKv7tuvs0b9nAZmcSEuoTtggfh/GW0eZALdPP6MzVg36QxpCBFGOj8sEIl04p" + - "ozBRuRXKcAfo5iwp6+Os4+TyV8zlIVDMkwawD35N+imvnGDQHBnW8D/4TeYTtOvczZS/AgMBAAEC" + - "gYBxwFalNSwZK3WJipq+g6KLCiBn1JxGGDQlLKrweFaSuFyFky9fd3IvkIabirqQchD612sMb+GT" + - "0t1jptW6z4w2w6++IW0A3apDOCwoD+uvDBXrbFqI0VbyAWUNqHVdaFFIRk2IHGEE6463mGRdmILX" + - "IlCd/85RTHReg4rl/GFqWQJBANgLAIR4pWbl5Gm+DtY18wp6Q3pJAAMkmP/lISCBIidu1zcqYIKt" + - "PoDW4Knq9xnhxPbXrXKv4YzZWHBK8GkKhQ0CQQDBQnXufQcMew+PwiS0oJvS+eQ6YJwynuqG2ejg" + - "WE+T7489jKtscRATpUXpZUYmDLGg9bLt7L62hFvFSj2LO2X7AkBcdrD9AWnBFWlh/G77LVHczSEu" + - "KCoyLiqxcs5vy/TjLaQ8vw1ZQG580/qJnr+tOxyCjSJ18GK3VppsTRaBznfNAkB3nuCKNp9HTWCL" + - "dfrsRsFMrFpk++mSt6SoxXaMbn0LL2u1CD4PCEiQMGt+lK3/3TmRTKNs+23sYS7Ahjxj0udDAkEA" + - "p57Nj65WNaWeYiOfTwKXkLj8l29H5NbaGWxPT0XkWr4PvBOFZVH/wj0/qc3CMVGnv11+DyO+QUCN" + - "SqBB5aRe8g=="; - - private void overrideSettings(String key, String value) throws Exception { - assertTrue(Settings.Secure.putString(mContext.getContentResolver(), key, value)); - Thread.sleep(1000); - } - - private void overrideCert(String value) throws Exception { - overrideSettings(PINLIST_CERTIFICATE_KEY, value); - } - - private String readPins() throws Exception { - return IoUtils.readFileAsString(PINLIST_CONTENT_PATH); - } - - private String readCurrentVersion() throws Exception { - return IoUtils.readFileAsString("/data/misc/keychain/metadata/version"); - } - - private String getNextVersion() throws Exception { - int currentVersion = Integer.parseInt(readCurrentVersion()); - return Integer.toString(currentVersion + 1); - } - - private static String getCurrentHash(String content) throws Exception { - if (content == null) { - return "0"; - } - MessageDigest dgst = MessageDigest.getInstance("SHA512"); - byte[] encoded = content.getBytes(); - byte[] fingerprint = dgst.digest(encoded); - return IntegralToString.bytesToHexString(fingerprint, false); - } - - private static String getHashOfCurrentContent() throws Exception { - String content = IoUtils.readFileAsString("/data/misc/keychain/pins"); - return getCurrentHash(content); - } - - private PrivateKey createKey() throws Exception { - byte[] derKey = Base64.decode(TEST_KEY.getBytes(), Base64.DEFAULT); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(derKey); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return (PrivateKey) keyFactory.generatePrivate(keySpec); - } - - private X509Certificate createCertificate() throws Exception { - byte[] derCert = Base64.decode(TEST_CERT.getBytes(), Base64.DEFAULT); - InputStream istream = new ByteArrayInputStream(derCert); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - return (X509Certificate) cf.generateCertificate(istream); - } - - private String makeTemporaryContentFile(String content) throws Exception { - FileOutputStream fw = mContext.openFileOutput("content.txt", mContext.MODE_WORLD_READABLE); - fw.write(content.getBytes(), 0, content.length()); - fw.close(); - return mContext.getFilesDir() + "/content.txt"; - } - - private String createSignature(String content, String version, String requiredHash) - throws Exception { - Signature signer = Signature.getInstance("SHA512withRSA"); - signer.initSign(createKey()); - signer.update(content.trim().getBytes()); - signer.update(version.trim().getBytes()); - signer.update(requiredHash.getBytes()); - String sig = new String(Base64.encode(signer.sign(), Base64.DEFAULT)); - assertEquals(true, - verifySignature(content, version, requiredHash, sig, createCertificate())); - return sig; - } - - public boolean verifySignature(String content, String version, String requiredPrevious, - String signature, X509Certificate cert) throws Exception { - Signature signer = Signature.getInstance("SHA512withRSA"); - signer.initVerify(cert); - signer.update(content.trim().getBytes()); - signer.update(version.trim().getBytes()); - signer.update(requiredPrevious.trim().getBytes()); - return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT)); - } - - private void sendIntent(String contentPath, String version, String required, String sig) { - Intent i = new Intent(); - i.setAction("android.intent.action.UPDATE_PINS"); - i.putExtra(EXTRA_CONTENT_PATH, contentPath); - i.putExtra(EXTRA_VERSION_NUMBER, version); - i.putExtra(EXTRA_REQUIRED_HASH, required); - i.putExtra(EXTRA_SIGNATURE, sig); - mContext.sendBroadcast(i); - } - - private String runTest(String cert, String content, String version, String required, String sig) - throws Exception { - Log.e(TAG, "started test"); - overrideCert(cert); - String contentPath = makeTemporaryContentFile(content); - sendIntent(contentPath, version, required, sig); - Thread.sleep(1000); - return readPins(); - } - - private String runTestWithoutSig(String cert, String content, String version, String required) - throws Exception { - String sig = createSignature(content, version, required); - return runTest(cert, content, version, required, sig); - } - - public void testOverwritePinlist() throws Exception { - Log.e(TAG, "started testOverwritePinList"); - assertEquals("abcde", runTestWithoutSig(TEST_CERT, "abcde", getNextVersion(), getHashOfCurrentContent())); - Log.e(TAG, "started testOverwritePinList"); - } - - public void testBadSignatureFails() throws Exception { - Log.e(TAG, "started testOverwritePinList"); - String text = "blahblah"; - runTestWithoutSig(TEST_CERT, text, getNextVersion(), getHashOfCurrentContent()); - assertEquals(text, runTest(TEST_CERT, "bcdef", getNextVersion(), getCurrentHash(text), "")); - Log.e(TAG, "started testOverwritePinList"); - } - - public void testBadRequiredHashFails() throws Exception { - runTestWithoutSig(TEST_CERT, "blahblahblah", getNextVersion(), getHashOfCurrentContent()); - assertEquals("blahblahblah", runTestWithoutSig(TEST_CERT, "cdefg", getNextVersion(), "0")); - Log.e(TAG, "started testOverwritePinList"); - } - - public void testBadVersionFails() throws Exception { - String text = "blahblahblahblah"; - String version = getNextVersion(); - runTestWithoutSig(TEST_CERT, text, version, getHashOfCurrentContent()); - assertEquals(text, runTestWithoutSig(TEST_CERT, "defgh", version, getCurrentHash(text))); - Log.e(TAG, "started testOverwritePinList"); - } - - public void testOverrideRequiredHash() throws Exception { - runTestWithoutSig(TEST_CERT, "blahblahblah", getNextVersion(), getHashOfCurrentContent()); - assertEquals("blahblahblah", runTestWithoutSig(TEST_CERT, "cdefg", "NONE", "0")); - Log.e(TAG, "started testOverwritePinList"); - } - -} diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 77168f9..9c2e1b9 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1946,7 +1946,7 @@ static status_t writeTextLayoutClasses( const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; fprintf(fp, - "int styleable %s_%s %d\n", + "int styleable.%s_%s %d\n", nclassName.string(), String8(name).string(), (int)pos); } |