OSDN Git Service

am c660c608: Merge "add Toshiba usb driver link delete Fujitsu-Toshiba bug: 9755017...
authorScott Main <smain@google.com>
Fri, 12 Jul 2013 01:05:44 +0000 (18:05 -0700)
committerAndroid Git Automerger <android-git-automerger@android.com>
Fri, 12 Jul 2013 01:05:44 +0000 (18:05 -0700)
* commit 'c660c608e75791d5b4b286f34a9ec335f9e95ba6':
  add Toshiba usb driver link delete Fujitsu-Toshiba bug: 9755017

37 files changed:
api/current.txt
cmds/servicemanager/service_manager.c
core/java/android/accessibilityservice/AccessibilityService.java
core/java/android/app/Activity.java
core/java/android/app/ActivityThread.java
core/java/android/content/pm/ActivityInfo.java
core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
core/java/android/inputmethodservice/IInputMethodWrapper.java
core/java/android/net/EthernetDataTracker.java
core/java/android/preference/Preference.java
core/java/android/preference/PreferenceManager.java
core/java/android/server/search/SearchManagerService.java
core/java/android/service/wallpaper/WallpaperService.java
core/java/android/text/format/Time.java
core/java/android/util/PropertyValueModel.java [new file with mode: 0755]
core/java/android/util/ValueModel.java [new file with mode: 0755]
core/java/android/view/SimulatedDpad.java [new file with mode: 0644]
core/java/android/view/ViewRootImpl.java
core/java/android/view/inputmethod/InputMethodManager.java
core/java/android/widget/CheckBox.java
core/java/android/widget/EditText.java
core/java/android/widget/ImageView.java
core/java/android/widget/NumberPicker.java
core/java/android/widget/SeekBar.java
core/java/android/widget/ValueEditor.java [new file with mode: 0755]
core/java/com/android/internal/os/HandlerCaller.java
core/jni/android_text_format_Time.cpp
core/res/AndroidManifest.xml
data/etc/platform.xml
services/java/com/android/server/InputMethodManagerService.java
services/java/com/android/server/am/ActivityManagerService.java
services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java [moved from services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java with 100% similarity]
services/java/com/android/server/updates/CertPinInstallReceiver.java [deleted file]
services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java [deleted file]
tools/aapt/Command.cpp
tools/aapt/Resource.cpp
wifi/java/android/net/wifi/p2p/WifiP2pService.java

index 6b893d5..10c6e2c 100644 (file)
@@ -2683,6 +2683,7 @@ package android.app {
     method public final boolean isChild();
     method public boolean isDestroyed();
     method public boolean isFinishing();
+    method public boolean isImmersive();
     method public boolean isTaskRoot();
     method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public boolean moveTaskToBack(boolean);
@@ -2770,6 +2771,7 @@ package android.app {
     method public final void setFeatureDrawableResource(int, int);
     method public final void setFeatureDrawableUri(int, android.net.Uri);
     method public void setFinishOnTouchOutside(boolean);
+    method public void setImmersive(boolean);
     method public void setIntent(android.content.Intent);
     method public final void setProgress(int);
     method public final void setProgressBarIndeterminate(boolean);
@@ -6330,6 +6332,7 @@ package android.content.pm {
     field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100
     field public static final int FLAG_FINISH_ON_TASK_LAUNCH = 2; // 0x2
     field public static final int FLAG_HARDWARE_ACCELERATED = 512; // 0x200
+    field public static final int FLAG_IMMERSIVE = 2048; // 0x800
     field public static final int FLAG_MULTIPROCESS = 1; // 0x1
     field public static final int FLAG_NO_HISTORY = 128; // 0x80
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
@@ -23429,6 +23432,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);
@@ -23577,6 +23591,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;
@@ -27967,10 +27989,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 {
@@ -28143,14 +28167,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 {
@@ -29175,11 +29201,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 {
@@ -29767,6 +29795,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);
index 3314dc3..71e840e 100644 (file)
@@ -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" },
index b0bad07..9d6ee80 100644 (file)
@@ -556,7 +556,7 @@ public abstract class AccessibilityService extends Service {
         public IAccessibilityServiceClientWrapper(Context context, Looper looper,
                 Callbacks callback) {
             mCallback = callback;
-            mCaller = new HandlerCaller(context, looper, this);
+            mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
         }
 
         public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
index e779746..e96b475 100644 (file)
@@ -4818,8 +4818,8 @@ public class Activity extends ContextThemeWrapper
      * <code>android:immersive</code> but may be changed at runtime by
      * {@link #setImmersive}.
      *
+     * @see #setImmersive(boolean)
      * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
-     * @hide
      */
     public boolean isImmersive() {
         try {
@@ -4831,7 +4831,7 @@ public class Activity extends ContextThemeWrapper
 
     /**
      * Adjust the current immersive mode setting.
-     * 
+     *
      * Note that changing this value will have no effect on the activity's
      * {@link android.content.pm.ActivityInfo} structure; that is, if
      * <code>android:immersive</code> is set to <code>true</code>
@@ -4840,9 +4840,8 @@ public class Activity extends ContextThemeWrapper
      * always have its {@link android.content.pm.ActivityInfo#FLAG_IMMERSIVE
      * FLAG_IMMERSIVE} bit set.
      *
-     * @see #isImmersive
+     * @see #isImmersive()
      * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
-     * @hide
      */
     public void setImmersive(boolean i) {
         try {
index d880817..d62372c 100644 (file)
@@ -113,7 +113,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;
@@ -4981,13 +4980,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;
@@ -5017,9 +5009,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();
index e2ca1dd..8f3b62d 100644 (file)
@@ -161,7 +161,6 @@ public class ActivityInfo extends ComponentInfo
      */
     public static final int FLAG_SHOW_ON_LOCK_SCREEN = 0x0400;
     /**
-     * @hide
      * Bit in {@link #flags} corresponding to an immersive activity
      * that wishes not to be interrupted by notifications.
      * Applications that hide the system notification bar with
@@ -174,7 +173,14 @@ public class ActivityInfo extends ComponentInfo
      * {@link #FLAG_IMMERSIVE} set, however, will not be interrupted; the
      * notification may be shown in some other way (such as a small floating
      * "toast" window).
-     * {@see android.app.Notification#FLAG_HIGH_PRIORITY}
+     *
+     * Note that this flag will always reflect the Activity's
+     * <code>android:immersive</code> manifest definition, even if the Activity's
+     * immersive state is changed at runtime via
+     * {@link android.app.Activity#setImmersive(boolean)}.
+     *
+     * @see android.app.Notification#FLAG_HIGH_PRIORITY
+     * @see android.app.Activity#setImmersive(boolean)
      */
     public static final int FLAG_IMMERSIVE = 0x0800;
     /**
index 5324f81..d78262b 100644 (file)
@@ -70,7 +70,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
     
     public IInputMethodSessionWrapper(Context context,
             InputMethodSession inputMethodSession) {
-        mCaller = new HandlerCaller(context, this);
+        mCaller = new HandlerCaller(context, null,
+                this, true /*asyncHandler*/);
         mInputMethodSession = inputMethodSession;
     }
 
index 5275314..2d67875 100644 (file)
@@ -102,7 +102,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
     public IInputMethodWrapper(AbstractInputMethodService context,
             InputMethod inputMethod) {
         mTarget = new WeakReference<AbstractInputMethodService>(context);
-        mCaller = new HandlerCaller(context.getApplicationContext(), this);
+        mCaller = new HandlerCaller(context.getApplicationContext(), null,
+                this, true /*asyncHandler*/);
         mInputMethod = new WeakReference<InputMethod>(inputMethod);
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
     }
index 3a06dc0..37601fc 100644 (file)
@@ -74,7 +74,7 @@ public class EthernetDataTracker implements NetworkStateTracker {
         }
 
         public void interfaceLinkStateChanged(String iface, boolean up) {
-            if (mIface.equals(iface) && mLinkUp != up) {
+            if (mIface.equals(iface)) {
                 Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down"));
                 mLinkUp = up;
                 mTracker.mNetworkInfo.setIsAvailable(up);
index 336960e..e343e83 100644 (file)
@@ -939,8 +939,9 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
      * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
      *            listener should be called in the proper order (between other
      *            processing). May be null.
+     * @hide
      */
-    void performClick(PreferenceScreen preferenceScreen) {
+    public void performClick(PreferenceScreen preferenceScreen) {
         
         if (!isEnabled()) {
             return;
index 5ca7d79..17f88f1 100644 (file)
@@ -138,7 +138,10 @@ public class PreferenceManager {
     
     private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
     
-    PreferenceManager(Activity activity, int firstRequestCode) {
+    /**
+     * @hide
+     */
+    public PreferenceManager(Activity activity, int firstRequestCode) {
         mActivity = activity;
         mNextRequestCode = firstRequestCode;
         
index 46f2723..1a10644 100644 (file)
@@ -240,28 +240,8 @@ public class SearchManagerService extends ISearchManager.Stub {
     @Override
     public ComponentName getAssistIntent(int userHandle) {
         try {
-            if (userHandle != UserHandle.getCallingUserId()) {
-                // Requesting a different user, make sure that they have the permission
-                if (ActivityManager.checkComponentPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                        Binder.getCallingUid(), -1, true)
-                        == PackageManager.PERMISSION_GRANTED) {
-                    // Translate to the current user id, if caller wasn't aware
-                    if (userHandle == UserHandle.USER_CURRENT) {
-                        long identity = Binder.clearCallingIdentity();
-                        userHandle = ActivityManagerNative.getDefault().getCurrentUser().id;
-                        Binder.restoreCallingIdentity(identity);
-                    }
-                } else {
-                    String msg = "Permission Denial: "
-                            + "Request to getAssistIntent for " + userHandle
-                            + " but is calling from user " + UserHandle.getCallingUserId()
-                            + "; this requires "
-                            + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-                    Slog.w(TAG, msg);
-                    return null;
-                }
-            }
+            userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userHandle, true, false, "getAssistIntent", null);
             IPackageManager pm = AppGlobals.getPackageManager();
             Intent assistIntent = new Intent(Intent.ACTION_ASSIST);
             ResolveInfo info =
index 6d5705d..d1b23e4 100644 (file)
@@ -961,13 +961,7 @@ public abstract class WallpaperService extends Service {
         IWallpaperEngineWrapper(WallpaperService context,
                 IWallpaperConnection conn, IBinder windowToken,
                 int windowType, boolean isPreview, int reqWidth, int reqHeight) {
-            if (DEBUG && mCallbackLooper != null) {
-                mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
-            }
-            mCaller = new HandlerCaller(context,
-                    mCallbackLooper != null
-                            ? mCallbackLooper : context.getMainLooper(),
-                    this);
+            mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
             mConnection = conn;
             mWindowToken = windowToken;
             mWindowType = windowType;
index 5ef86b1..200b57b 100644 (file)
@@ -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 (executable)
index 0000000..eb9c47d
--- /dev/null
@@ -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 (executable)
index 0000000..4789682
--- /dev/null
@@ -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/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
new file mode 100644 (file)
index 0000000..b03e4c7
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * 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.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * This class creates DPAD events from touchpad events.
+ * 
+ * @see ViewRootImpl
+ */
+
+//TODO: Make this class an internal class of ViewRootImpl.java
+class SimulatedDpad {
+
+    private static final String TAG = "SimulatedDpad";
+
+    // 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;
+    // Where the cutoff is for determining an edge swipe
+    private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
+    private static final int MSG_FLICK = 313;
+    // TODO: Pass touch slop from the input device
+    private static final int TOUCH_SLOP = 30;
+    // 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;
+    // Did the swipe begin in a valid region
+    private boolean mEdgeSwipePossible;
+
+    private final Context mContext;
+
+    // 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;
+
+    // Information from the most recent event.
+    // Used to determine what device sent the event during a fling.
+    private int mLastSource;
+    private int mLastMetaState;
+    private int mLastDeviceId;
+
+    // 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 SimulatedDpad(Context context) {
+        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 = TOUCH_SLOP;
+        mTouchSlopSquared = mTouchSlop * mTouchSlop;
+
+        mContext = context;
+    }
+
+    private final Handler mHandler = new Handler(true /*async*/) {
+            @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_FLICK: {
+                    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, mLastMetaState,
+                            mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
+                    viewroot.enqueueInputEvent(new KeyEvent(time, time,
+                            KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
+                            mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
+
+                    // Increase the delay by the decay factor and resend
+                    final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
+                    if (delay <= mMaxRepeatDelay) {
+                        Message msgCopy = Message.obtain(msg);
+                        msgCopy.arg1 = delay;
+                        msgCopy.setAsynchronous(true);
+                        mHandler.sendMessageDelayed(msgCopy, delay);
+                    }
+                    break;
+                }
+            }
+        }
+    };
+
+    public void updateTouchPad(ViewRootImpl viewroot, MotionEvent event,
+            boolean synthesizeNewKeys) {
+        if (!synthesizeNewKeys) {
+            mHandler.removeMessages(MSG_FLICK);
+        }
+        InputDevice device = event.getDevice();
+        if (device == null) {
+            return;
+        }
+        // Store what time the touchpad event occurred
+        final long time = SystemClock.uptimeMillis();
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mLastTouchPadStartTimeMs = time;
+                mAlwaysInTapRegion = true;
+                mTouchpadEnterXPosition = event.getX();
+                mTouchpadEnterYPosition = event.getY();
+                mAccumulatedX = 0;
+                mAccumulatedY = 0;
+                mLastMoveX = 0;
+                mLastMoveY = 0;
+                if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
+                        * EDGE_SWIPE_THRESHOLD < event.getY()) {
+                    // Did the swipe begin in a valid region
+                    mEdgeSwipePossible = true;
+                }
+                // Clear any flings
+                if (synthesizeNewKeys) {
+                    mHandler.removeMessages(MSG_FLICK);
+                }
+                break;
+            case MotionEvent.ACTION_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;
+                }
+                // Checks if the swipe has crossed the midpoint
+                // and if our swipe gesture is complete
+                if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
+                        * .5) && mEdgeSwipePossible) {
+                    mEdgeSwipePossible = false;
+
+                    Intent intent =
+                            ((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
+                            .getAssistIntent(mContext, UserHandle.USER_CURRENT_OR_SELF);
+                    if (intent != null) {
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        try {
+                            mContext.startActivity(intent);
+                        } catch (ActivityNotFoundException e){
+                            Log.e(TAG, "Could not start search activity");
+                        }
+                    } else {
+                        Log.e(TAG, "Could not find a search activity");
+                    }
+                }
+                // 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;
+                        if (synthesizeNewKeys) {
+                            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_UP:
+                if (time - mLastTouchPadStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
+                    if (synthesizeNewKeys) {
+                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
+                                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+                                event.getMetaState(), event.getDeviceId(), 0,
+                                KeyEvent.FLAG_FALLBACK, event.getSource()));
+                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
+                                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+                                event.getMetaState(), event.getDeviceId(), 0,
+                                KeyEvent.FLAG_FALLBACK, event.getSource()));
+                    }
+                } 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) {
+                        mLastDeviceId = event.getDeviceId();
+                        mLastSource = event.getSource();
+                        mLastMetaState = event.getMetaState();
+
+                        if (synthesizeNewKeys) {
+                            Message message = Message.obtain(mHandler, MSG_FLICK,
+                                    mKeySendRateMs, mLastKeySent, viewroot);
+                            message.setAsynchronous(true);
+                            mHandler.sendMessageDelayed(message, mKeySendRateMs);
+                        }
+                    }
+                }
+                mEdgeSwipePossible = false;
+                break;
+        }
+
+        // Store touch event position and time
+        mLastTouchPadEventTimeMs = time;
+        mLastTouchpadXPosition = event.getX();
+        mLastTouchpadYPosition = event.getY();
+    }
+}
index b6016e9..58e6fbe 100644 (file)
@@ -144,6 +144,8 @@ public final class ViewRootImpl implements ViewParent,
     final TrackballAxis mTrackballAxisX = new TrackballAxis();
     final TrackballAxis mTrackballAxisY = new TrackballAxis();
 
+    final SimulatedDpad mSimulatedDpad;
+
     int mLastJoystickXDirection;
     int mLastJoystickYDirection;
     int mLastJoystickXKeyCode;
@@ -230,6 +232,13 @@ public final class ViewRootImpl implements ViewParent,
     int mLastSystemUiVisibility;
     int mClientWindowLayoutFlags;
 
+    /** @hide */
+    public static final int EVENT_NOT_HANDLED = 0;
+    /** @hide */
+    public static final int EVENT_HANDLED = 1;
+    /** @hide */
+    public static final int EVENT_IN_PROGRESS = 2;
+
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
@@ -383,6 +392,7 @@ public final class ViewRootImpl implements ViewParent,
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mAttachInfo.mScreenOn = powerManager.isScreenOn();
         loadSystemProperties();
+        mSimulatedDpad = new SimulatedDpad(context);
     }
 
     /**
@@ -3154,19 +3164,19 @@ public final class ViewRootImpl implements ViewParent,
         return false;
     }
 
-    private void deliverInputEvent(QueuedInputEvent q) {
+    private int deliverInputEvent(QueuedInputEvent q) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
         try {
             if (q.mEvent instanceof KeyEvent) {
-                deliverKeyEvent(q);
+                return deliverKeyEvent(q);
             } else {
                 final int source = q.mEvent.getSource();
                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    deliverPointerEvent(q);
+                    return deliverPointerEvent(q);
                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    deliverTrackballEvent(q);
+                    return deliverTrackballEvent(q);
                 } else {
-                    deliverGenericMotionEvent(q);
+                    return deliverGenericMotionEvent(q);
                 }
             }
         } finally {
@@ -3174,7 +3184,25 @@ public final class ViewRootImpl implements ViewParent,
         }
     }
 
-    private void deliverPointerEvent(QueuedInputEvent q) {
+    private int deliverInputEventPostIme(QueuedInputEvent q) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
+        try {
+            if (q.mEvent instanceof KeyEvent) {
+                return deliverKeyEventPostIme(q);
+            } else {
+                final int source = q.mEvent.getSource();
+                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    return deliverTrackballEventPostIme(q);
+                } else {
+                    return deliverGenericMotionEventPostIme(q);
+                }
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private int deliverPointerEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         final boolean isTouchEvent = event.isTouchEvent();
         if (mInputEventConsistencyVerifier != null) {
@@ -3187,8 +3215,7 @@ public final class ViewRootImpl implements ViewParent,
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Translate the pointer event for compatibility, if needed.
@@ -3221,16 +3248,10 @@ public final class ViewRootImpl implements ViewParent,
         if (MEASURE_LATENCY) {
             lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
         }
-        if (handled) {
-            finishInputEvent(q, true);
-            return;
-        }
-
-        // Pointer event was unhandled.
-        finishInputEvent(q, false);
+        return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
     }
 
-    private void deliverTrackballEvent(QueuedInputEvent q) {
+    private int deliverTrackballEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
@@ -3249,24 +3270,25 @@ public final class ViewRootImpl implements ViewParent,
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending trackball event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchTrackballEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverTrackballEventPostIme(q);
+        return deliverTrackballEventPostIme(q);
     }
 
-    private void deliverTrackballEventPostIme(QueuedInputEvent q) {
+    private int deliverTrackballEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the trackball event to the view.
@@ -3276,10 +3298,8 @@ public final class ViewRootImpl implements ViewParent,
             // event into a key event, touch mode will not exit, so we exit
             // touch mode here.
             ensureTouchMode(false);
-
-            finishInputEvent(q, true);
             mLastTrackballTime = Integer.MIN_VALUE;
-            return;
+            return EVENT_HANDLED;
         }
 
         // Translate the trackball event into DPAD keys and try to deliver those.
@@ -3387,15 +3407,14 @@ public final class ViewRootImpl implements ViewParent,
 
         // Unfortunately we can't tell whether the application consumed the keys, so
         // we always consider the trackball event handled.
-        finishInputEvent(q, true);
+        return EVENT_HANDLED;
     }
 
-    private void deliverGenericMotionEvent(QueuedInputEvent q) {
+    private int deliverGenericMotionEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         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);
@@ -3409,47 +3428,56 @@ public final class ViewRootImpl implements ViewParent,
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending generic motion event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverGenericMotionEventPostIme(q);
+        return deliverGenericMotionEventPostIme(q);
     }
 
-    private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {
+    private int 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) {
+              mSimulatedDpad.updateTouchPad(this, event, false);
             }
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the event to the view.
         if (mView.dispatchGenericMotionEvent(event)) {
             if (isJoystick) {
                 updateJoystickDirection(event, false);
+            } else if (isTouchPad) {
+              mSimulatedDpad.updateTouchPad(this, event, false);
             }
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         if (isJoystick) {
             // Translate the joystick event into DPAD keys and try to deliver
             // those.
             updateJoystickDirection(event, true);
-            finishInputEvent(q, true);
-        } else {
-            finishInputEvent(q, false);
+            return EVENT_HANDLED;
+        }
+        if (isTouchPad) {
+            mSimulatedDpad.updateTouchPad(this, event, true);
+            return EVENT_HANDLED;
         }
+        return EVENT_NOT_HANDLED;
     }
 
     private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
@@ -3593,7 +3621,7 @@ public final class ViewRootImpl implements ViewParent,
         return false;
     }
 
-    private void deliverKeyEvent(QueuedInputEvent q) {
+    private int deliverKeyEvent(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
@@ -3604,8 +3632,7 @@ public final class ViewRootImpl implements ViewParent,
 
             // Perform predispatching before the IME.
             if (mView.dispatchKeyEventPreIme(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
 
             // Dispatch to the IME before propagating down the view hierarchy.
@@ -3616,81 +3643,30 @@ public final class ViewRootImpl implements ViewParent,
                     final int seq = event.getSequenceNumber();
                     if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                             + seq + " event=" + event);
-                    imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
-                    return;
+                    int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
+                            mInputMethodCallback);
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverKeyEventPostIme(q);
+        return deliverKeyEventPostIme(q);
     }
 
-    void handleImeFinishedEvent(int seq, boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        if (q != null && q.mEvent.getSequenceNumber() == seq) {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + " event=" + q);
-            }
-            if (handled) {
-                finishInputEvent(q, true);
-            } else {
-                if (q.mEvent instanceof KeyEvent) {
-                    KeyEvent event = (KeyEvent)q.mEvent;
-                    if (event.getAction() != KeyEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    deliverKeyEventPostIme(q);
-                } else {
-                    MotionEvent event = (MotionEvent)q.mEvent;
-                    if (event.getAction() != MotionEvent.ACTION_CANCEL
-                            && event.getAction() != MotionEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    final int source = q.mEvent.getSource();
-                    if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                        deliverTrackballEventPostIme(q);
-                    } else {
-                        deliverGenericMotionEventPostIme(q);
-                    }
-                }
-            }
-        } else {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + ", event not found!");
-            }
-        }
-    }
-
-    private void deliverKeyEventPostIme(QueuedInputEvent q) {
+    private int deliverKeyEventPostIme(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
 
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // If the key's purpose is to exit touch mode then we consume it and consider it handled.
         if (checkForLeavingTouchModeAndConsume(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Make sure the fallback event policy sees all keys that will be delivered to the
@@ -3699,8 +3675,7 @@ public final class ViewRootImpl implements ViewParent,
 
         // Deliver the key to the view hierarchy.
         if (mView.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // If the Control modifier is held, try to interpret the key as a shortcut.
@@ -3709,15 +3684,13 @@ public final class ViewRootImpl implements ViewParent,
                 && event.getRepeatCount() == 0
                 && !KeyEvent.isModifierKey(event.getKeyCode())) {
             if (mView.dispatchKeyShortcutEvent(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
         }
 
         // Apply the fallback event policy.
         if (mFallbackEventHandler.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Handle automatic focus changes.
@@ -3770,22 +3743,20 @@ public final class ViewRootImpl implements ViewParent,
                         if (v.requestFocus(direction, mTempRect)) {
                             playSoundEffect(SoundEffectConstants
                                     .getContantForFocusDirection(direction));
-                            finishInputEvent(q, true);
-                            return;
+                            return EVENT_HANDLED;
                         }
                     }
 
                     // Give the focused view a last chance to handle the dpad key.
                     if (mView.dispatchUnhandledMove(focused, direction)) {
-                        finishInputEvent(q, true);
-                        return;
+                        return EVENT_HANDLED;
                     }
                 }
             }
         }
 
         // Key was unhandled.
-        finishInputEvent(q, false);
+        return EVENT_NOT_HANDLED;
     }
 
     /* drag/drop */
@@ -4289,7 +4260,11 @@ public final class ViewRootImpl implements ViewParent,
             mFirstPendingInputEvent = q.mNext;
             q.mNext = null;
             mCurrentInputEvent = q;
-            deliverInputEvent(q);
+
+            final int result = deliverInputEvent(q);
+            if (result != EVENT_IN_PROGRESS) {
+                finishCurrentInputEvent(result == EVENT_HANDLED);
+            }
         }
 
         // We are done processing all input events that we can process right now
@@ -4300,10 +4275,42 @@ public final class ViewRootImpl implements ViewParent,
         }
     }
 
-    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
-        if (q != mCurrentInputEvent) {
-            throw new IllegalStateException("finished input event out of order");
+    void handleImeFinishedEvent(int seq, boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        if (q != null && q.mEvent.getSequenceNumber() == seq) {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + " event=" + q);
+            }
+
+            if (!handled) {
+                // If the window doesn't currently have input focus, then drop
+                // this event.  This could be an event that came back from the
+                // IME dispatch but the window has lost focus in the meantime.
+                if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+                    Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                } else {
+                    final int result = deliverInputEventPostIme(q);
+                    if (result == EVENT_HANDLED) {
+                        handled = true;
+                    }
+                }
+            }
+            finishCurrentInputEvent(handled);
+
+            // Immediately start processing the next input event.
+            doProcessInputEvents();
+        } else {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + ", event not found!");
+            }
         }
+    }
+
+    private void finishCurrentInputEvent(boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        mCurrentInputEvent = null;
 
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -4312,10 +4319,18 @@ public final class ViewRootImpl implements ViewParent,
         }
 
         recycleQueuedInputEvent(q);
+    }
 
-        mCurrentInputEvent = null;
-        if (mFirstPendingInputEvent != null) {
-            scheduleProcessInputEvents();
+    private static boolean isTerminalInputEvent(InputEvent event) {
+        if (event instanceof KeyEvent) {
+            final KeyEvent keyEvent = (KeyEvent)event;
+            return keyEvent.getAction() == KeyEvent.ACTION_UP;
+        } else {
+            final MotionEvent motionEvent = (MotionEvent)event;
+            final int action = motionEvent.getAction();
+            return action == MotionEvent.ACTION_UP
+                    || action == MotionEvent.ACTION_CANCEL
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
         }
     }
 
index 4a3f846..d258f4d 100644 (file)
@@ -334,7 +334,7 @@ public final class InputMethodManager {
     
     class H extends Handler {
         H(Looper looper) {
-            super(looper);
+            super(looper, null, true);
         }
         
         @Override
@@ -1565,38 +1565,36 @@ public final class InputMethodManager {
     /**
      * @hide
      */
-    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
+    public int dispatchKeyEvent(Context context, int seq, KeyEvent key,
             FinishedEventCallback callback) {
-        boolean handled = false;
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
 
             if (mCurMethod != null) {
                 if (key.getAction() == KeyEvent.ACTION_DOWN
-                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
+                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM
+                        && key.getRepeatCount() == 0) {
                     showInputMethodPickerLocked();
-                    handled = true;
-                } else {
-                    try {
-                        if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
-                        final long startTime = SystemClock.uptimeMillis();
-                        enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                        mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
-                        return;
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
-                    }
+                    return ViewRootImpl.EVENT_HANDLED;
+                }
+                try {
+                    if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
+                    final long startTime = SystemClock.uptimeMillis();
+                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
+                    mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, handled);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     /**
      * @hide
      */
-    public void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
+    public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
             FinishedEventCallback callback) {
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
@@ -1607,20 +1605,19 @@ public final class InputMethodManager {
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
-                    return;
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, false);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     /**
      * @hide
      */
-    public void dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
+    public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
             FinishedEventCallback callback) {
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent");
@@ -1631,14 +1628,13 @@ public final class InputMethodManager {
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
-                    return;
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, false);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     void finishedEvent(int seq, boolean handled) {
index f1804f8..41ab5f2 100644 (file)
@@ -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;
+    }
 }
index 57e51c2..ec81214 100644 (file)
@@ -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);
+    }
 }
index 87396fb..26c801f 100644 (file)
@@ -551,7 +551,10 @@ public class ImageView extends View {
         applied to the drawable, be sure to call setImageMatrix().
     */
     public Matrix getImageMatrix() {
-        return mMatrix;
+        if (mDrawMatrix == null) {
+            return Matrix.IDENTITY_MATRIX;
+        }
+        return mDrawMatrix;
     }
 
     public void setImageMatrix(Matrix matrix) {
index ac21671..74ded18 100644 (file)
@@ -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);
     }
index 2737f94..a6486a8 100644 (file)
@@ -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 (executable)
index 0000000..2b91abf
--- /dev/null
@@ -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);
+}
index 84699dc..b442ff5 100644 (file)
@@ -24,38 +24,32 @@ import android.os.Message;
 public class HandlerCaller {
 
     public final Context mContext;
-    
+
     final Looper mMainLooper;
     final Handler mH;
 
     final Callback mCallback;
 
     class MyHandler extends Handler {
-        MyHandler(Looper looper) {
-            super(looper);
+        MyHandler(Looper looper, boolean async) {
+            super(looper, null, async);
         }
-        
+
         @Override
         public void handleMessage(Message msg) {
             mCallback.executeMessage(msg);
         }
     }
-    
+
     public interface Callback {
         public void executeMessage(Message msg);
     }
-    
-    public HandlerCaller(Context context, Callback callback) {
-        mContext = context;
-        mMainLooper = context.getMainLooper();
-        mH = new MyHandler(mMainLooper);
-        mCallback = callback;
-    }
 
-    public HandlerCaller(Context context, Looper looper, Callback callback) {
+    public HandlerCaller(Context context, Looper looper, Callback callback,
+            boolean asyncHandler) {
         mContext = context;
-        mMainLooper = looper;
-        mH = new MyHandler(mMainLooper);
+        mMainLooper = looper != null ? looper : context.getMainLooper();
+        mH = new MyHandler(mMainLooper, asyncHandler);
         mCallback = callback;
     }
 
index aa2c5f3..0a59ae7 100644 (file)
@@ -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,
-                    boolthrown)
+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;
 }
 
index 2a357af..bee9a8c 100644 (file)
         android:permissionGroup="android.permission-group.NETWORK"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows access to the loop radio (Android@Home mesh network) device.
+       @hide -->
+    <permission android:name="android.permission.LOOP_RADIO"
+       android:permissionGroup="android.permission-group.NETWORK"
+       android:protectionLevel="signature|system" />
+    
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
     <!-- ================================== -->
index 83ecdd9..cf04b5c 100644 (file)
         <group gid="net_bw_acct" />
     </permission>
 
+    <permission name="android.permission.LOOP_RADIO" >
+        <group gid="loop_radio" />
+    </permission>
+
     <!-- ================================================================== -->
     <!-- ================================================================== -->
     <!-- ================================================================== -->
index 94e0bd4..a296d34 100644 (file)
@@ -602,12 +602,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
         mHandler = new Handler(this);
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
-        mCaller = new HandlerCaller(context, new HandlerCaller.Callback() {
+        mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
             @Override
             public void executeMessage(Message msg) {
                 handleMessage(msg);
             }
-        });
+        }, true /*asyncHandler*/);
         mWindowManagerService = windowManager;
         mHardKeyboardListener = new HardKeyboardListener();
 
index 1d08d31..6e81e9d 100644 (file)
@@ -115,6 +115,7 @@ import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UpdateLock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.format.Time;
@@ -191,6 +192,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     static final boolean DEBUG_POWER = localLOGV || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean DEBUG_MU = localLOGV || false;
+    static final boolean DEBUG_IMMERSIVE = localLOGV || false;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -827,6 +829,12 @@ public final class ActivityManagerService extends ActivityManagerNative
     long mLastWriteTime = 0;
 
     /**
+     * Used to retain an update lock when the foreground activity is in
+     * immersive mode.
+     */
+    final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
+    /**
      * Set to true after the system has finished booting.
      */
     boolean mBooted = false;
@@ -895,6 +903,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     static final int REPORT_USER_SWITCH_MSG = 34;
     static final int CONTINUE_USER_SWITCH_MSG = 35;
     static final int USER_SWITCH_TIMEOUT_MSG = 36;
+    static final int IMMERSIVE_MODE_LOCK_MSG = 37;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1356,6 +1365,21 @@ public final class ActivityManagerService extends ActivityManagerNative
                 timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
                 break;
             }
+            case IMMERSIVE_MODE_LOCK_MSG: {
+                final boolean nextState = (msg.arg1 != 0);
+                if (mUpdateLock.isHeld() != nextState) {
+                    if (DEBUG_IMMERSIVE) {
+                        final ActivityRecord r = (ActivityRecord) msg.obj;
+                        Slog.d(TAG, "Applying new update lock state '" + nextState + "' for " + r);
+                    }
+                    if (nextState) {
+                        mUpdateLock.acquire();
+                    } else {
+                        mUpdateLock.release();
+                    }
+                }
+                break;
+            }
             }
         }
     };
@@ -1822,9 +1846,20 @@ public final class ActivityManagerService extends ActivityManagerNative
             if (r != null) {
                 mWindowManager.setFocusedApp(r.appToken, true);
             }
+            applyUpdateLockStateLocked(r);
         }
     }
 
+    final void applyUpdateLockStateLocked(ActivityRecord r) {
+        // Modifications to the UpdateLock state are done on our handler, outside
+        // the activity manager's locks.  The new state is determined based on the
+        // state *now* of the relevant activity record.  The object is passed to
+        // the handler solely for logging detail, not to be consulted/modified.
+        final boolean nextState = r != null && r.immersive;
+        mHandler.sendMessage(
+                mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
+    }
+
     private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) {
         // put it on the LRU to keep track of when it should be exited.
         int lrui = mLruProcesses.indexOf(app);
@@ -7421,11 +7456,19 @@ public final class ActivityManagerService extends ActivityManagerNative
 
     public void setImmersive(IBinder token, boolean immersive) {
         synchronized(this) {
-            ActivityRecord r = mMainStack.isInStackLocked(token);
+            final ActivityRecord r = mMainStack.isInStackLocked(token);
             if (r == null) {
                 throw new IllegalArgumentException();
             }
             r.immersive = immersive;
+
+            // update associated state if we're frontmost
+            if (r == mFocusedActivity) {
+                if (DEBUG_IMMERSIVE) {
+                    Slog.d(TAG, "Frontmost changed immersion: "+ r);
+                }
+                applyUpdateLockStateLocked(r);
+            }
         }
     }
 
diff --git a/services/java/com/android/server/updates/CertPinInstallReceiver.java b/services/java/com/android/server/updates/CertPinInstallReceiver.java
deleted file mode 100644 (file)
index c03fbc3..0000000
+++ /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 (file)
index b6742a1..0000000
+++ /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");
-    }
-
-}
index c3a0930..f7083a9 100644 (file)
@@ -1840,25 +1840,6 @@ int doCrunch(Bundle* bundle)
     return NO_ERROR;
 }
 
-/*
- * Do PNG Crunching on a single flag
- *  -i points to a single png file
- *  -o points to a single png output file
- */
-int doSingleCrunch(Bundle* bundle)
-{
-    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
-    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
-
-    String8 input(bundle->getSingleCrunchInputFile());
-    String8 output(bundle->getSingleCrunchOutputFile());
-    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
-        // we can't return the status_t as it gets truncate to the lower 8 bits.
-        return 42;
-    }
-    return NO_ERROR;
-}
-
 char CONSOLE_DATA[2925] = {
     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
     32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
index 77168f9..9c2e1b9 100644 (file)
@@ -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);
             }
index 039319d..4a4320c 100644 (file)
@@ -30,6 +30,7 @@ import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.net.IConnectivityManager;
 import android.net.ConnectivityManager;
@@ -68,6 +69,7 @@ import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -1949,6 +1951,26 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
                 break;
         }
 
+        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE) ==
+                Configuration.UI_MODE_TYPE_APPLIANCE) {
+            // For appliance devices, add a key listener which accepts.
+            dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
+
+                @Override
+                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+                    // TODO: make the actual key come from a config value.
+                    if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+                        sendMessage(PEER_CONNECTION_USER_ACCEPT);
+                        dialog.dismiss();
+                        return true;
+                    }
+                    return false;
+                }
+            });
+            // TODO: add timeout for this dialog.
+            // TODO: update UI in appliance mode to tell user what to do.
+        }
+
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
         dialog.show();
     }