Merge "add javadoc for SurfaceView {unL|l}ockCanvas{AndPost}()" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 5a33193..6719066 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2306,28 +2306,23 @@
package android.animation {
- public abstract interface Animatable {
- method public abstract long getDuration();
- method public abstract android.animation.TimeInterpolator getInterpolator();
- method public abstract long getStartDelay();
- method public abstract android.animation.Animatable setDuration(long);
- method public abstract void setInterpolator(android.animation.TimeInterpolator);
- method public abstract void setStartDelay(long);
- }
-
- public abstract class Animator implements android.animation.Animatable java.lang.Cloneable {
+ public abstract class Animator implements java.lang.Cloneable {
ctor public Animator();
method public void addListener(android.animation.Animator.AnimatorListener);
method public void cancel();
method public android.animation.Animator clone();
method public void end();
+ method public abstract long getDuration();
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
+ method public abstract long getStartDelay();
method public abstract boolean isRunning();
method public boolean isStarted();
method public void removeAllListeners();
method public void removeListener(android.animation.Animator.AnimatorListener);
method public abstract android.animation.Animator setDuration(long);
+ method public abstract void setInterpolator(android.animation.TimeInterpolator);
+ method public abstract void setStartDelay(long);
method public void setTarget(java.lang.Object);
method public void setupEndValues();
method public void setupStartValues();
@@ -5618,7 +5613,6 @@
method public abstract java.lang.String[] fileList();
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
- method public java.util.List<android.content.RestrictionEntry> getApplicationRestrictions();
method public abstract android.content.res.AssetManager getAssets();
method public abstract java.io.File getCacheDir();
method public abstract java.lang.ClassLoader getClassLoader();
@@ -6205,8 +6199,9 @@
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING";
- field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+ field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent";
+ field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -17556,7 +17551,7 @@
}
public class UserManager {
- method public static synchronized android.os.UserManager get(android.content.Context);
+ method public android.os.Bundle getApplicationRestrictions(java.lang.String);
method public long getSerialNumberForUser(android.os.UserHandle);
method public int getUserCount();
method public android.os.UserHandle getUserForSerialNumber(long);
@@ -30049,7 +30044,6 @@
method public void setRelativeScrollPosition(int, int);
method public deprecated void setRemoteAdapter(int, int, android.content.Intent);
method public void setRemoteAdapter(int, android.content.Intent);
- method public void setRemoteAdapter(int, java.util.ArrayList<android.widget.RemoteViews>, int);
method public void setScrollPosition(int, int);
method public void setShort(int, java.lang.String, short);
method public void setString(int, java.lang.String, java.lang.String);
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
deleted file mode 100644
index f9ccb4d..0000000
--- a/core/java/android/animation/Animatable.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 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.animation;
-
-/**
- * This interface is implemented by animation-related classes that expose
- * the ability to set and get duration, startDelay, and interpolators.
- */
-public interface Animatable {
-
- /**
- * The amount of time, in milliseconds, to delay processing the animation
- * after the animation is started. The {@link #setDuration(long)} of the
- * animation will not begin to elapse until after the startDelay has elapsed.
- *
- * @return the number of milliseconds to delay running the animation
- */
- long getStartDelay();
-
- /**
- * The amount of time, in milliseconds, to delay processing the animation
- * after the animation is started. The {@link #setDuration(long)} of the
- * animation will not begin to elapse until after the startDelay has elapsed.
-
- * @param startDelay The amount of the delay, in milliseconds
- */
- void setStartDelay(long startDelay);
-
- /**
- * Sets the length of the animation.
- *
- * @param duration The length of the animation, in milliseconds.
- */
- Animatable setDuration(long duration);
-
- /**
- * Gets the duration of the animation.
- *
- * @return The length of the animation, in milliseconds.
- */
- long getDuration();
-
- /**
- * The time interpolator used in calculating the elapsed fraction of the
- * animation. The interpolator determines whether the animation runs with
- * linear or non-linear motion, such as acceleration and deceleration.
- *
- * @param value the interpolator to be used by this animation
- */
- void setInterpolator(TimeInterpolator value);
-
- /**
- * Returns the timing interpolator that this animation uses.
- *
- * @return The timing interpolator for this animation.
- */
- public TimeInterpolator getInterpolator();
-}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index da97d72..39eb8d6 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -22,21 +22,13 @@
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
-public abstract class Animator implements Cloneable, Animatable {
+public abstract class Animator implements Cloneable {
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatorListener> mListeners = null;
- @Override
- public abstract Animator setDuration(long duration);
-
- @Override
- public TimeInterpolator getInterpolator() {
- return null;
- }
-
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
@@ -77,6 +69,55 @@
}
/**
+ * The amount of time, in milliseconds, to delay processing the animation
+ * after {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ public abstract long getStartDelay();
+
+ /**
+ * The amount of time, in milliseconds, to delay processing the animation
+ * after {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ public abstract void setStartDelay(long startDelay);
+
+ /**
+ * Sets the duration of the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ */
+ public abstract Animator setDuration(long duration);
+
+ /**
+ * Gets the duration of the animation.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ public abstract long getDuration();
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of the
+ * animation. The interpolator determines whether the animation runs with
+ * linear or non-linear motion, such as acceleration and deceleration. The
+ * default value is {@link android.view.animation.AccelerateDecelerateInterpolator}.
+ *
+ * @param value the interpolator to be used by this animation
+ */
+ public abstract void setInterpolator(TimeInterpolator value);
+
+ /**
+ * Returns the timing interpolator that this animation uses.
+ *
+ * @return The timing interpolator for this animation.
+ */
+ public TimeInterpolator getInterpolator() {
+ return null;
+ }
+
+ /**
* Returns whether this Animator is currently running (having been started and gone past any
* initial startDelay period and not yet ended).
*
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 7b07438..0d7f0a7 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -134,11 +134,6 @@
}
}
- public List<RestrictionEntry> getApplicationRestrictions() {
- return ((UserManager) getSystemService(USER_SERVICE))
- .getApplicationRestrictions(getPackageName(), android.os.Process.myUserHandle());
- }
-
public void registerComponentCallbacks(ComponentCallbacks callback) {
synchronized (mComponentCallbacks) {
mComponentCallbacks.add(callback);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 03e241a..5bd28b9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -293,15 +293,6 @@
public abstract Context getApplicationContext();
/**
- * Returns the list of restrictions for the application, or null if there are no
- * restrictions.
- * @return
- */
- public List<RestrictionEntry> getApplicationRestrictions() {
- return getApplicationContext().getApplicationRestrictions();
- }
-
- /**
* Add a new {@link ComponentCallbacks} to the base application of the
* Context, which will be called at the same times as the ComponentCallbacks
* methods of activities and other components are called. Note that you
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 97ad7dd..1ab1eb8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2417,11 +2417,16 @@
/**
* Broadcast to a specific application to query any supported restrictions to impose
- * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS},
+ * on restricted users. The broadcast intent contains an extra
+ * {@link #EXTRA_RESTRICTIONS_BUNDLE} with the currently persisted
+ * restrictions as a Bundle of key/value pairs. The value types can be Boolean, String or
+ * String[] depending on the restriction type.<p/>
+ * The response should contain an extra {@link #EXTRA_RESTRICTIONS_LIST},
* which is of type <code>ArrayList<RestrictionEntry></code>. It can also
* contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>.
* The activity specified by that intent will be launched for a result which must contain
- * the extra {@link #EXTRA_RESTRICTIONS}. The returned restrictions will be persisted.
+ * the extra {@link #EXTRA_RESTRICTIONS_LIST}. The keys and values of the returned restrictions
+ * will be persisted.
* @see RestrictionEntry
*/
public static final String ACTION_GET_RESTRICTION_ENTRIES =
@@ -3160,7 +3165,8 @@
"android.intent.extra.ALLOW_MULTIPLE";
/**
- * The userHandle carried with broadcast intents related to addition, removal and switching of users
+ * The userHandle carried with broadcast intents related to addition, removal and switching of
+ * users
* - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
* @hide
*/
@@ -3169,9 +3175,18 @@
/**
* Extra used in the response from a BroadcastReceiver that handles
- * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+ * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is
+ * <code>ArrayList<RestrictionEntry></code>.
*/
- public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+ public static final String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
+
+ /**
+ * Extra sent in the intent to the BroadcastReceiver that handles
+ * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is a Bundle containing
+ * the restrictions as key/value pairs.
+ */
+ public static final String EXTRA_RESTRICTIONS_BUNDLE =
+ "android.intent.extra.restrictions_bundle";
/**
* Extra used in the response from a BroadcastReceiver that handles
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 2e8092a..a11358a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -42,7 +42,8 @@
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
void setUserRestrictions(in Bundle restrictions, int userHandle);
- void setApplicationRestrictions(in String packageName, in List<RestrictionEntry> entries,
+ void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
- List<RestrictionEntry> getApplicationRestrictions(in String packageName, int userHandle);
+ Bundle getApplicationRestrictions(in String packageName);
+ Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index e580e2b..df065e9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -142,6 +142,7 @@
private static UserManager sInstance = null;
+ /** @hide */
public synchronized static UserManager get(Context context) {
if (sInstance == null) {
sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -578,13 +579,29 @@
return -1;
}
+ /**
+ * Returns a Bundle containing any saved application restrictions for this user, for the
+ * given package name. Only an application with this package name can call this method.
+ * @param packageName the package name of the calling application
+ * @return a Bundle with the restrictions as key/value pairs, or null if there are no
+ * saved restrictions. The values can be of type Boolean, String or String[], depending
+ * on the restriction type, as defined by the application.
+ */
+ public Bundle getApplicationRestrictions(String packageName) {
+ try {
+ return mService.getApplicationRestrictions(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get application restrictions for package " + packageName);
+ }
+ return null;
+ }
/**
* @hide
*/
- public List<RestrictionEntry> getApplicationRestrictions(String packageName, UserHandle user) {
+ public Bundle getApplicationRestrictions(String packageName, UserHandle user) {
try {
- return mService.getApplicationRestrictions(packageName, user.getIdentifier());
+ return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier());
} catch (RemoteException re) {
Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
}
@@ -594,10 +611,10 @@
/**
* @hide
*/
- public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ public void setApplicationRestrictions(String packageName, Bundle restrictions,
UserHandle user) {
try {
- mService.setApplicationRestrictions(packageName, entries, user.getIdentifier());
+ mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier());
} catch (RemoteException re) {
Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 88ee414..3df4e99 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3260,6 +3260,7 @@
/**
* This preference contains the string that shows for owner info on LockScreen.
* @hide
+ * @deprecated
*/
public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
@@ -3287,6 +3288,7 @@
/**
* This preference enables showing the owner info on LockScreen.
* @hide
+ * @deprecated
*/
public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
"lock_screen_owner_info_enabled";
@@ -4102,9 +4104,7 @@
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- UI_NIGHT_MODE,
- LOCK_SCREEN_OWNER_INFO,
- LOCK_SCREEN_OWNER_INFO_ENABLED
+ UI_NIGHT_MODE
};
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4fb2431..be26d20 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13380,18 +13380,32 @@
/**
* Sets a rectangular area on this view to which the view will be clipped
- * it is drawn. Setting the value to null will remove the clip bounds
+ * when it is drawn. Setting the value to null will remove the clip bounds
* and the view will draw normally, using its full bounds.
*
* @param clipBounds The rectangular area, in the local coordinates of
* this view, to which future drawing operations will be clipped.
*/
public void setClipBounds(Rect clipBounds) {
- mClipBounds = clipBounds;
if (clipBounds != null) {
- invalidate(clipBounds);
+ if (clipBounds.equals(mClipBounds)) {
+ return;
+ }
+ if (mClipBounds == null) {
+ invalidate();
+ mClipBounds = new Rect(clipBounds);
+ } else {
+ invalidate(Math.min(mClipBounds.left, clipBounds.left),
+ Math.min(mClipBounds.top, clipBounds.top),
+ Math.max(mClipBounds.right, clipBounds.right),
+ Math.max(mClipBounds.bottom, clipBounds.bottom));
+ mClipBounds.set(clipBounds);
+ }
} else {
- invalidate();
+ if (mClipBounds != null) {
+ invalidate();
+ mClipBounds = null;
+ }
}
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 98df064..528eadd 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -16,7 +16,6 @@
package android.view;
-import android.animation.Animatable;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.TimeInterpolator;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 83e2e79..07e66b7 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2108,6 +2108,8 @@
* RemoteViews. This count cannot change during the life-cycle of a given widget, so this
* parameter should account for the maximum possible number of types that may appear in the
* See {@link Adapter#getViewTypeCount()}.
+ *
+ * @hide
*/
public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 555c7c2..d3ead26 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -140,6 +140,10 @@
public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
+ private static final String LOCK_SCREEN_OWNER_INFO = Settings.Secure.LOCK_SCREEN_OWNER_INFO;
+ private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
+ Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
+
private final Context mContext;
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
@@ -526,6 +530,22 @@
}
}
+ public void setOwnerInfo(String info, int userId) {
+ setString(LOCK_SCREEN_OWNER_INFO, info, userId);
+ }
+
+ public void setOwnerInfoEnabled(boolean enabled) {
+ setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled);
+ }
+
+ public String getOwnerInfo(int userId) {
+ return getString(LOCK_SCREEN_OWNER_INFO);
+ }
+
+ public boolean isOwnerInfoEnabled() {
+ return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false);
+ }
+
/**
* Compute the password quality from the given password string.
*/
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 1ace23e..17f205d 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -339,23 +339,11 @@
}
TextLayoutShaper::TextLayoutShaper() {
- init();
-
mBuffer = hb_buffer_create();
}
-void TextLayoutShaper::init() {
- mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
-}
-
-void TextLayoutShaper::unrefTypefaces() {
- SkSafeUnref(mDefaultTypeface);
-}
-
TextLayoutShaper::~TextLayoutShaper() {
hb_buffer_destroy(mBuffer);
-
- unrefTypefaces();
}
void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
@@ -839,23 +827,27 @@
}
if (baseGlyphCount != 0) {
+ SkTypeface::Style style = SkTypeface::kNormal;
+ if (typeface != NULL) {
+ style = typeface->style();
+ }
typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
if (!typeface) {
baseGlyphCount = 0;
- typeface = mDefaultTypeface;
- SkSafeRef(typeface);
+ typeface = SkFontHost::CreateTypeface(NULL, NULL, style);
#if DEBUG_GLYPHS
ALOGD("Using Default Typeface");
#endif
}
} else {
if (!typeface) {
- typeface = mDefaultTypeface;
+ typeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
#if DEBUG_GLYPHS
- ALOGD("Using Default Typeface");
+ ALOGD("Using Default Typeface (normal style)");
#endif
+ } else {
+ SkSafeRef(typeface);
}
- SkSafeRef(typeface);
}
mShapingPaint.setTypeface(typeface);
@@ -899,8 +891,6 @@
hb_face_destroy(mCachedHBFaces.valueAt(i));
}
mCachedHBFaces.clear();
- unrefTypefaces();
- init();
}
TextLayoutEngine::TextLayoutEngine() {
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index f9b9900..5414a11 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -197,18 +197,10 @@
SkPaint mShapingPaint;
/**
- * Skia default typeface to be returned if we cannot resolve script
- */
- SkTypeface* mDefaultTypeface;
-
- /**
* Cache of Harfbuzz faces
*/
KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces;
- void init();
- void unrefTypefaces();
-
SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
hb_script_t script);
@@ -228,7 +220,6 @@
hb_face_t* referenceCachedHBFace(SkTypeface* typeface);
bool isComplexScript(hb_script_t script);
-
}; // TextLayoutShaper
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6b4fe79..514178d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -918,7 +918,8 @@
<permission android:name="android.permission.RECORD_AUDIO"
android:permissionGroup="android.permission-group.MICROPHONE"
android:protectionLevel="dangerous"
- android:label="@string/permlab_recordAudio" />
+ android:label="@string/permlab_recordAudio"
+ android:description="@string/permdesc_recordAudio" />
<!-- =========================================== -->
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 4a5e82e..61a0134 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -999,7 +999,7 @@
if (mEventHandler != null) {
// signal new client
mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
- mEventHandler.dispatchMessage(
+ mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN,
/*arg1*/ generationId, /*arg2, ignored*/ 0));
// send the information
@@ -1007,12 +1007,12 @@
mEventHandler.removeMessages(MSG_REQUEST_METADATA);
mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
- mEventHandler.dispatchMessage(
+ mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE));
- mEventHandler.dispatchMessage(
+ mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL));
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
}
}
@@ -1020,7 +1020,7 @@
// only post messages, we can't block here
if (mEventHandler != null) {
mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN);
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/));
}
}
@@ -1028,7 +1028,7 @@
public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
// only post messages, we can't block here
if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_PLUG_DISPLAY, w, h, rcd));
}
}
@@ -1036,7 +1036,7 @@
public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
// only post messages, we can't block here
if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_UNPLUG_DISPLAY, rcd));
}
}
@@ -1044,7 +1044,7 @@
public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) {
// only post messages, we can't block here
if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd));
}
}
@@ -1053,7 +1053,7 @@
// only post messages, we can't block here
if (mEventHandler != null) {
mEventHandler.removeMessages(MSG_SEEK_TO);
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */,
new Long(timeMs)));
}
@@ -1145,6 +1145,7 @@
break;
case MSG_SEEK_TO:
onSeekTo(msg.arg1, ((Long)msg.obj).longValue());
+ break;
default:
Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
index 77359ff..9b58803 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
@@ -23,12 +23,16 @@
import android.content.Context;
import android.os.BatteryManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Slog;
import android.view.View;
import android.widget.TextView;
@@ -37,6 +41,8 @@
import java.lang.ref.WeakReference;
import com.android.internal.R;
+import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.LockPatternUtils;
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
@@ -57,6 +63,8 @@
static final int SECURITY_MESSAGE_DURATION = 5000;
protected static final int FADE_DURATION = 750;
+ private static final String TAG = "KeyguardMessageArea";
+
// are we showing battery information?
boolean mShowingBatteryInfo = false;
@@ -82,6 +90,9 @@
CharSequence mMessage;
boolean mShowingMessage;
+ private CharSequence mSeparator;
+ private LockPatternUtils mLockPatternUtils;
+
Runnable mClearMessageRunnable = new Runnable() {
@Override
public void run() {
@@ -156,8 +167,6 @@
}
};
- private CharSequence mSeparator;
-
public KeyguardMessageArea(Context context) {
this(context, null);
}
@@ -165,6 +174,8 @@
public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(context);
+
// This is required to ensure marquee works
setSelected(true);
@@ -228,11 +239,12 @@
String getOwnerInfo() {
ContentResolver res = getContext().getContentResolver();
- final boolean ownerInfoEnabled = Settings.Secure.getIntForUser(res,
- Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
- return ownerInfoEnabled && !mShowingMessage ?
- Settings.Secure.getStringForUser(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO,
- UserHandle.USER_CURRENT) : null;
+ String info = null;
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
+ if (ownerInfoEnabled && !mShowingMessage) {
+ info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
+ }
+ return info;
}
private CharSequence getChargeInfo(MutableInt icon) {
diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java
index e20a21f..41cc4d7 100644
--- a/services/java/com/android/server/LockSettingsService.java
+++ b/services/java/com/android/server/LockSettingsService.java
@@ -19,6 +19,11 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+
+import static android.content.Context.USER_SERVICE;
+import static android.Manifest.permission.READ_PROFILE;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -27,8 +32,10 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Slog;
@@ -40,6 +47,7 @@
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
+import java.util.List;
/**
* Keeps the lock pattern/password data and related settings for each user.
@@ -79,23 +87,52 @@
private void migrateOldData() {
try {
- if (getString("migrated", null, 0) != null) {
- // Already migrated
- return;
+ // These Settings moved before multi-user was enabled, so we only have to do it for the
+ // root user.
+ if (getString("migrated", null, 0) == null) {
+ final ContentResolver cr = mContext.getContentResolver();
+ for (String validSetting : VALID_SETTINGS) {
+ String value = Settings.Secure.getString(cr, validSetting);
+ if (value != null) {
+ setString(validSetting, value, 0);
+ }
+ }
+ // No need to move the password / pattern files. They're already in the right place.
+ setString("migrated", "true", 0);
+ Slog.i(TAG, "Migrated lock settings to new location");
}
- final ContentResolver cr = mContext.getContentResolver();
- for (String validSetting : VALID_SETTINGS) {
- String value = Settings.Secure.getString(cr, validSetting);
- if (value != null) {
- setString(validSetting, value, 0);
+ // These Settings changed after multi-user was enabled, hence need to be moved per user.
+ if (getString("migrated_user_specific", null, 0) == null) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final ContentResolver cr = mContext.getContentResolver();
+ List<UserInfo> users = um.getUsers();
+ for (int user = 0; user < users.size(); user++) {
+ int userId = users.get(user).getUserHandle().getIdentifier();
+ for (String perUserSetting : MIGRATE_SETTINGS_PER_USER) {
+ // Handle Strings
+ String value = Settings.Secure.getStringForUser(cr, perUserSetting, userId);
+ if (value != null) {
+ setString(perUserSetting, value, userId);
+ Settings.Secure.putStringForUser(cr, perUserSetting, "", userId);
+ continue;
+ }
+
+ // Handle integers
+ try {
+ int ivalue = Settings.Secure.getIntForUser(cr, perUserSetting, userId);
+ setLong(perUserSetting, ivalue, userId);
+ Settings.Secure.putIntForUser(cr, perUserSetting, 0, userId);
+ } catch (SettingNotFoundException e) {
+ }
+ }
}
+ // No need to move the password / pattern files. They're already in the right place.
+ setString("migrated_user_specific", "true", 0);
+ Slog.i(TAG, "Migrated per-user lock settings to new location");
}
- // No need to move the password / pattern files. They're already in the right place.
- setString("migrated", "true", 0);
- Slog.i(TAG, "Migrated lock settings to new location");
} catch (RemoteException re) {
- Slog.e(TAG, "Unable to migrate old data");
+ Slog.e(TAG, "Unable to migrate old data", re);
}
}
@@ -115,12 +152,16 @@
}
}
- private static final void checkReadPermission(int userId) {
+ private final void checkReadPermission(String requestedKey, int userId) {
final int callingUid = Binder.getCallingUid();
- if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID
- && UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("uid=" + callingUid
- + " not authorized to read settings of user " + userId);
+ for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) {
+ String key = READ_PROFILE_PROTECTED_SETTINGS[i];
+ if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("uid=" + callingUid
+ + " needs permission " + READ_PROFILE + " to read "
+ + requestedKey + " for user " + userId);
+ }
}
}
@@ -147,7 +188,7 @@
@Override
public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
- //checkReadPermission(userId);
+ checkReadPermission(key, userId);
String value = readFromDb(key, null, userId);
return TextUtils.isEmpty(value) ?
@@ -156,7 +197,7 @@
@Override
public long getLong(String key, long defaultValue, int userId) throws RemoteException {
- //checkReadPermission(userId);
+ checkReadPermission(key, userId);
String value = readFromDb(key, null, userId);
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
@@ -164,7 +205,7 @@
@Override
public String getString(String key, String defaultValue, int userId) throws RemoteException {
- //checkReadPermission(userId);
+ checkReadPermission(key, userId);
return readFromDb(key, defaultValue, userId);
}
@@ -404,5 +445,13 @@
Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
Secure.LOCK_PATTERN_VISIBLE,
Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
- };
+ };
+
+ private static final String[] MIGRATE_SETTINGS_PER_USER = new String[] {
+ Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+ Secure.LOCK_SCREEN_OWNER_INFO
+ };
+
+ // These are protected with a read permission
+ private static final String[] READ_PROFILE_PROTECTED_SETTINGS = MIGRATE_SETTINGS_PER_USER;
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index fa18e76..3bebf91 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -1676,8 +1676,12 @@
.getSystemService(Context.AUDIO_SERVICE);
// sound
+
+ // should we use the default notification sound? (indicated either by DEFAULT_SOUND
+ // or because notification.sound is pointing at Settings.System.NOTIFICATION_SOUND)
final boolean useDefaultSound =
- (notification.defaults & Notification.DEFAULT_SOUND) != 0;
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0
+ || Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound);
Uri soundUri = null;
boolean hasValidSound = false;
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
index 0688c50..de439d7 100644
--- a/services/java/com/android/server/am/NativeCrashListener.java
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -43,13 +43,14 @@
class NativeCrashListener extends Thread {
static final String TAG = "NativeCrashListener";
static final boolean DEBUG = false;
+ static final boolean MORE_DEBUG = DEBUG && false;
// Must match the path defined in debuggerd.c.
static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
// Use a short timeout on socket operations and abandon the connection
// on hard errors
- static final long SOCKET_TIMEOUT_MILLIS = 1000; // 1 second
+ static final long SOCKET_TIMEOUT_MILLIS = 2000; // 2 seconds
final ActivityManagerService mAm;
@@ -124,9 +125,9 @@
InetSocketAddress peer = new InetSocketAddress();
FileDescriptor peerFd = null;
try {
- if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
+ if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
peerFd = Libcore.os.accept(serverFd, peer);
- if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
+ if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
if (peerFd != null) {
// Only the superuser is allowed to talk to us over this socket
StructUcred credentials =
@@ -145,7 +146,12 @@
if (peerFd != null) {
try {
Libcore.os.write(peerFd, ackSignal, 0, 1);
- } catch (Exception e) { /* we don't care about failures here */ }
+ } catch (Exception e) {
+ /* we don't care about failures here */
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Exception writing ack: " + e.getMessage());
+ }
+ }
}
}
}
@@ -183,7 +189,7 @@
// Read the crash report from the debuggerd connection
void consumeNativeCrashData(FileDescriptor fd) {
- if (DEBUG) Slog.i(TAG, "debuggerd connected");
+ if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
final byte[] buf = new byte[4096];
final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
@@ -218,7 +224,7 @@
// get some data
bytes = Libcore.os.read(fd, buf, 0, buf.length);
if (bytes > 0) {
- if (DEBUG) {
+ if (MORE_DEBUG) {
String s = new String(buf, 0, bytes, "UTF-8");
Slog.v(TAG, "READ=" + bytes + "> " + s);
}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index df90a56..11c6dab 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -88,8 +88,13 @@
private static final String TAG_ENTRY = "entry";
private static final String TAG_VALUE = "value";
private static final String ATTR_KEY = "key";
+ private static final String ATTR_VALUE_TYPE = "type";
private static final String ATTR_MULTIPLE = "m";
+ private static final String ATTR_TYPE_STRING_ARRAY = "sa";
+ private static final String ATTR_TYPE_STRING = "s";
+ private static final String ATTR_TYPE_BOOLEAN = "b";
+
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private static final String USER_PHOTO_FILENAME = "photo.png";
@@ -965,7 +970,12 @@
}
@Override
- public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
+ public Bundle getApplicationRestrictions(String packageName) {
+ return getApplicationRestrictionsForUser(packageName, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
checkManageUsersPermission("Only system can get restrictions for other users/apps");
@@ -977,7 +987,7 @@
}
@Override
- public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ public void setApplicationRestrictions(String packageName, Bundle restrictions,
int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
@@ -985,7 +995,7 @@
}
synchronized (mPackagesLock) {
// Write the restrictions to XML
- writeApplicationRestrictionsLocked(packageName, entries, userId);
+ writeApplicationRestrictionsLocked(packageName, restrictions, userId);
}
}
@@ -1001,9 +1011,9 @@
}
}
- private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
+ private Bundle readApplicationRestrictionsLocked(String packageName,
int userId) {
- final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
+ final Bundle restrictions = new Bundle();
final ArrayList<String> values = new ArrayList<String>();
FileInputStream fis = null;
@@ -1023,12 +1033,13 @@
if (type != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read restrictions file "
+ restrictionsFile.getBaseFile());
- return entries;
+ return restrictions;
}
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
String key = parser.getAttributeValue(null, ATTR_KEY);
+ String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
if (multiple != null) {
int count = Integer.parseInt(multiple);
@@ -1041,14 +1052,13 @@
}
String [] valueStrings = new String[values.size()];
values.toArray(valueStrings);
- Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
- RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
- entries.add(entry);
+ restrictions.putStringArray(key, valueStrings);
+ } else if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+ restrictions.putBoolean(key, Boolean.parseBoolean(
+ parser.nextText().trim()));
} else {
String value = parser.nextText().trim();
- Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
- RestrictionEntry entry = new RestrictionEntry(key, value);
- entries.add(entry);
+ restrictions.putString(key, value);
}
}
}
@@ -1063,11 +1073,11 @@
}
}
}
- return entries;
+ return restrictions;
}
private void writeApplicationRestrictionsLocked(String packageName,
- List<RestrictionEntry> entries, int userId) {
+ Bundle restrictions, int userId) {
FileOutputStream fos = null;
AtomicFile restrictionsFile = new AtomicFile(
new File(Environment.getUserSystemDirectory(userId),
@@ -1084,18 +1094,24 @@
serializer.startTag(null, TAG_RESTRICTIONS);
- for (RestrictionEntry entry : entries) {
+ for (String key : restrictions.keySet()) {
+ Object value = restrictions.get(key);
serializer.startTag(null, TAG_ENTRY);
- serializer.attribute(null, ATTR_KEY, entry.getKey());
- if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) {
- String value = entry.getSelectedString();
- serializer.text(value != null ? value : "");
+ serializer.attribute(null, ATTR_KEY, key);
+
+ if (value instanceof Boolean) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
+ serializer.text(value.toString());
+ } else if (value == null || value instanceof String) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
+ serializer.text(value != null ? (String) value : "");
} else {
- String[] values = entry.getAllSelectedStrings();
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
+ String[] values = (String[]) value;
serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
- for (String value : values) {
+ for (String choice : values) {
serializer.startTag(null, TAG_VALUE);
- serializer.text(value != null ? value : "");
+ serializer.text(choice != null ? choice : "");
serializer.endTag(null, TAG_VALUE);
}
}
diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml
index 1cb8c37..b55e290 100644
--- a/tests/CanvasCompare/AndroidManifest.xml
+++ b/tests/CanvasCompare/AndroidManifest.xml
@@ -15,8 +15,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.hwuicompare" >
- <!-- for perfhud -->
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:label="@string/app_name"
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
index e0ff1dc..1ed4723 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
@@ -16,11 +16,20 @@
package com.android.test.hwuicompare;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.TreeSet;
-import com.android.test.hwuicompare.R;
+import org.json.JSONException;
+import org.json.JSONObject;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Trace;
import android.util.Log;
import android.widget.ImageView;
@@ -31,20 +40,31 @@
private static final float ERROR_DISPLAY_THRESHOLD = 0.01f;
protected static final boolean DRAW_BITMAPS = false;
+ /**
+ * Threshold of error change required to consider a test regressed/improved
+ */
+ private static final float ERROR_CHANGE_THRESHOLD = 0.001f;
+
+ private static final float[] ERROR_CUTOFFS = {
+ 0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f
+ };
+
+ private final float[] mErrorRates = new float[ERROR_CUTOFFS.length];
+ private float mTotalTests = 0;
+ private float mTotalError = 0;
+ private int mTestsRegressed = 0;
+ private int mTestsImproved = 0;
+
private ImageView mSoftwareImageView = null;
private ImageView mHardwareImageView = null;
- private static final float[] ERROR_CUTOFFS = {0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f};
- private float[] mErrorRates = new float[ERROR_CUTOFFS.length];
- private float mTotalTests = 0;
- private float mTotalError = 0;
- public abstract static class TestCallback {
+ public abstract static class FinalCallback {
abstract void report(String name, float value);
- void complete() {}
+ void complete() {};
}
- private ArrayList<TestCallback> mTestCallbacks = new ArrayList<TestCallback>();
+ private final ArrayList<FinalCallback> mFinalCallbacks = new ArrayList<FinalCallback>();
Runnable mRunnable = new Runnable() {
@Override
@@ -64,32 +84,11 @@
float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- if (error > ERROR_DISPLAY_THRESHOLD) {
- String modname = "";
- for (String s : DisplayModifier.getLastAppliedModifications()) {
- modname = modname.concat(s + ".");
- }
- Log.d(LOG_TAG, String.format("error for %s was %2.9f", modname, error));
- }
- for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
- if (error <= ERROR_CUTOFFS[i]) break;
- mErrorRates[i]++;
- }
- mTotalError += error;
- mTotalTests++;
+ final String[] modifierNames = DisplayModifier.getLastAppliedModifications();
+ handleError(modifierNames, error);
if (DisplayModifier.step()) {
- for (TestCallback c : mTestCallbacks) {
- c.report("averageError", (mTotalError / mTotalTests));
- for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
- c.report(String.format("error over %1.3f", ERROR_CUTOFFS[i]),
- mErrorRates[i]/mTotalTests);
- }
- c.complete();
- }
-
- Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
- finish();
+ finishTest();
} else {
mHardwareView.invalidate();
if (DRAW_BITMAPS) {
@@ -116,11 +115,186 @@
mHardwareImageView = (ImageView) findViewById(R.id.hardware_image_view);
onCreateCommon(mRunnable);
- mTestCallbacks.add(new TestCallback() {
+ beginTest();
+ }
+
+ private static class TestResult {
+ TestResult(String label, float error) {
+ mLabel = label;
+ mTotalError = error;
+ mCount = 1;
+ }
+ public void addInto(float error) {
+ mTotalError += error;
+ mCount++;
+ }
+ public float getAverage() {
+ return mTotalError / mCount;
+ }
+ final String mLabel;
+ float mTotalError;
+ int mCount;
+ }
+
+ JSONObject mOutputJson = null;
+ JSONObject mInputJson = null;
+ final HashMap<String, TestResult> mModifierResults = new HashMap<String, TestResult>();
+ final HashMap<String, TestResult> mIndividualResults = new HashMap<String, TestResult>();
+ final HashMap<String, TestResult> mModifierDiffResults = new HashMap<String, TestResult>();
+ final HashMap<String, TestResult> mIndividualDiffResults = new HashMap<String, TestResult>();
+ private void beginTest() {
+ mFinalCallbacks.add(new FinalCallback() {
+ @Override
void report(String name, float value) {
Log.d(LOG_TAG, name + " " + value);
};
});
+
+ File inputFile = new File(Environment.getExternalStorageDirectory(),
+ "CanvasCompareInput.json");
+ if (inputFile.exists() && inputFile.canRead() && inputFile.length() > 0) {
+ try {
+ FileInputStream inputStream = new FileInputStream(inputFile);
+ Log.d(LOG_TAG, "Parsing input file...");
+ StringBuffer content = new StringBuffer((int)inputFile.length());
+ byte[] buffer = new byte[1024];
+ while (inputStream.read(buffer) != -1) {
+ content.append(new String(buffer));
+ }
+ mInputJson = new JSONObject(content.toString());
+ inputStream.close();
+ Log.d(LOG_TAG, "Parsed input file with " + mInputJson.length() + " entries");
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "error parsing input json", e);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "error reading input json from sd", e);
+ }
+ }
+
+ mOutputJson = new JSONObject();
+ }
+
+ private static void logTestResultHash(String label, HashMap<String, TestResult> map) {
+ Log.d(LOG_TAG, "---------------");
+ Log.d(LOG_TAG, label + ":");
+ Log.d(LOG_TAG, "---------------");
+ TreeSet<TestResult> set = new TreeSet<TestResult>(new Comparator<TestResult>() {
+ @Override
+ public int compare(TestResult lhs, TestResult rhs) {
+ if (lhs == rhs) return 0; // don't need to worry about complex equality
+
+ int cmp = Float.compare(lhs.getAverage(), rhs.getAverage());
+ if (cmp != 0) {
+ return cmp;
+ }
+ return lhs.mLabel.compareTo(rhs.mLabel);
+ }
+ });
+
+ for (TestResult t : map.values()) {
+ set.add(t);
+ }
+
+ for (TestResult t : set.descendingSet()) {
+ if (Math.abs(t.getAverage()) > ERROR_DISPLAY_THRESHOLD) {
+ Log.d(LOG_TAG, String.format("%2.4f : %s", t.getAverage(), t.mLabel));
+ }
+ }
+ Log.d(LOG_TAG, "");
+ }
+
+ private void finishTest() {
+ for (FinalCallback c : mFinalCallbacks) {
+ c.report("averageError", (mTotalError / mTotalTests));
+ for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
+ c.report(String.format("tests with error over %1.3f", ERROR_CUTOFFS[i]),
+ mErrorRates[i]);
+ }
+ if (mInputJson != null) {
+ c.report("tests regressed", mTestsRegressed);
+ c.report("tests improved", mTestsImproved);
+ }
+ c.complete();
+ }
+
+ try {
+ if (mOutputJson != null) {
+ String outputString = mOutputJson.toString(4);
+ File outputFile = new File(Environment.getExternalStorageDirectory(),
+ "CanvasCompareOutput.json");
+ FileOutputStream outputStream = new FileOutputStream(outputFile);
+ outputStream.write(outputString.getBytes());
+ outputStream.close();
+ Log.d(LOG_TAG, "Saved output file with " + mOutputJson.length() + " entries");
+ }
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "error during JSON stringify", e);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "error storing JSON output on sd", e);
+ }
+
+ logTestResultHash("Modifier change vs previous", mModifierDiffResults);
+ logTestResultHash("Invidual test change vs previous", mIndividualDiffResults);
+ logTestResultHash("Modifier average test results", mModifierResults);
+ logTestResultHash("Individual test results", mIndividualResults);
+
+ Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ /**
+ * Inserts the error value into all TestResult objects, associated with each of its modifiers
+ */
+ private static void addForAllModifiers(String fullName, float error, String[] modifierNames,
+ HashMap<String, TestResult> modifierResults) {
+ for (String modifierName : modifierNames) {
+ TestResult r = modifierResults.get(fullName);
+ if (r == null) {
+ modifierResults.put(modifierName, new TestResult(modifierName, error));
+ } else {
+ r.addInto(error);
+ }
+ }
+ }
+
+ private void handleError(final String[] modifierNames, final float error) {
+ String fullName = "";
+ for (String s : modifierNames) {
+ fullName = fullName.concat("." + s);
+ }
+ fullName = fullName.substring(1);
+
+ float deltaError = 0;
+ if (mInputJson != null) {
+ try {
+ deltaError = error - (float)mInputJson.getDouble(fullName);
+ } catch (JSONException e) {
+ Log.w(LOG_TAG, "Warning: unable to read from input json", e);
+ }
+ if (deltaError > ERROR_CHANGE_THRESHOLD) mTestsRegressed++;
+ if (deltaError < -ERROR_CHANGE_THRESHOLD) mTestsImproved++;
+ mIndividualDiffResults.put(fullName, new TestResult(fullName, deltaError));
+ addForAllModifiers(fullName, deltaError, modifierNames, mModifierDiffResults);
+ }
+
+ mIndividualResults.put(fullName, new TestResult(fullName, error));
+ addForAllModifiers(fullName, error, modifierNames, mModifierResults);
+
+ try {
+ if (mOutputJson != null) {
+ mOutputJson.put(fullName, error);
+ }
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "exception during JSON recording", e);
+ mOutputJson = null;
+ }
+
+ for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
+ if (error <= ERROR_CUTOFFS[i]) break;
+ mErrorRates[i]++;
+ }
+ mTotalError += error;
+ mTotalTests++;
}
@Override
@@ -130,7 +304,7 @@
}
// FOR TESTING
- public void setCallback(TestCallback c) {
- mTestCallbacks.add(c);
+ public void setFinalCallback(FinalCallback c) {
+ mFinalCallbacks.add(c);
}
}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
index 6ea8237..1ff153c 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
@@ -1,6 +1,6 @@
package com.android.test.hwuicompare;
-import com.android.test.hwuicompare.AutomaticActivity.TestCallback;
+import com.android.test.hwuicompare.AutomaticActivity.FinalCallback;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
@@ -18,7 +18,7 @@
super.setUp();
mBundle = new Bundle();
mActivity = getActivity();
- mActivity.setCallback(new TestCallback() {
+ mActivity.setFinalCallback(new FinalCallback() {
@Override
void report(String key, float value) {