Merge "Start to use importFile to file descriptor."
diff --git a/api/current.txt b/api/current.txt
index b034c86..d7fce79 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32483,7 +32483,7 @@
method public static boolean isDigitsOnly(java.lang.CharSequence);
method public static boolean isEmpty(java.lang.CharSequence);
method public static boolean isGraphic(java.lang.CharSequence);
- method public static boolean isGraphic(char);
+ method public static deprecated boolean isGraphic(char);
method public static java.lang.String join(java.lang.CharSequence, java.lang.Object[]);
method public static java.lang.String join(java.lang.CharSequence, java.lang.Iterable);
method public static int lastIndexOf(java.lang.CharSequence, char);
@@ -33984,6 +33984,7 @@
field public static final int DENSITY_280 = 280; // 0x118
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
+ field public static final int DENSITY_420 = 420; // 0x1a4
field public static final int DENSITY_560 = 560; // 0x230
field public static final int DENSITY_DEFAULT = 160; // 0xa0
field public static final int DENSITY_HIGH = 240; // 0xf0
diff --git a/api/system-current.txt b/api/system-current.txt
index c5a4af4..ece5327 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33484,6 +33484,7 @@
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+ field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -33500,11 +33501,15 @@
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
+ field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
+ field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+ field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -34810,7 +34815,7 @@
method public static boolean isDigitsOnly(java.lang.CharSequence);
method public static boolean isEmpty(java.lang.CharSequence);
method public static boolean isGraphic(java.lang.CharSequence);
- method public static boolean isGraphic(char);
+ method public static deprecated boolean isGraphic(char);
method public static java.lang.String join(java.lang.CharSequence, java.lang.Object[]);
method public static java.lang.String join(java.lang.CharSequence, java.lang.Iterable);
method public static int lastIndexOf(java.lang.CharSequence, char);
@@ -36311,6 +36316,7 @@
field public static final int DENSITY_280 = 280; // 0x118
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
+ field public static final int DENSITY_420 = 420; // 0x1a4
field public static final int DENSITY_560 = 560; // 0x230
field public static final int DENSITY_DEFAULT = 160; // 0xa0
field public static final int DENSITY_HIGH = 240; // 0xf0
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index ff7705b..5b88c8e 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -415,7 +415,8 @@
public boolean isRunning() {
int size = mNodes.size();
for (int i = 0; i < size; i++) {
- if (mNodes.get(i).mAnimation.isRunning()) {
+ Node node = mNodes.get(i);
+ if (node != mRootNode && node.mAnimation.isRunning()) {
return true;
}
}
@@ -512,7 +513,9 @@
int size = mNodes.size();
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
- node.mAnimation.setupStartValues();
+ if (node != mRootNode) {
+ node.mAnimation.setupStartValues();
+ }
}
}
@@ -521,7 +524,9 @@
int size = mNodes.size();
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
- node.mAnimation.setupEndValues();
+ if (node != mRootNode) {
+ node.mAnimation.setupEndValues();
+ }
}
}
@@ -536,7 +541,9 @@
int size = mNodes.size();
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
- node.mAnimation.pause();
+ if (node != mRootNode) {
+ node.mAnimation.pause();
+ }
}
}
}
@@ -553,7 +560,9 @@
int size = mNodes.size();
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
- node.mAnimation.resume();
+ if (node != mRootNode) {
+ node.mAnimation.resume();
+ }
}
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fe79629..a2f2e73 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4316,11 +4316,6 @@
configDiff = mConfiguration.updateFrom(config);
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
-
- final Theme systemTheme = getSystemContext().getTheme();
- if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
- systemTheme.rebase();
- }
}
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index abe12dc..7824072 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -1082,7 +1082,8 @@
* create and display the dialog.
*/
public AlertDialog create() {
- final AlertDialog dialog = new AlertDialog(P.mContext);
+ // Context has already been wrapped with the appropriate theme.
+ final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c505b0b..e3414d9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3204,8 +3204,13 @@
* Called by a profile or device owner to set the application restrictions for a given target
* application running in the profile.
*
- * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be
- * boolean, int, String, or String[].
+ * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
+ * <ul>
+ * <li>{@code boolean}
+ * <li>{@code int}
+ * <li>{@code String} or {@code String[]}
+ * <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
+ * </ul>
*
* <p>The application restrictions are only made visible to the target application and the
* profile or device owner.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bcce33b..9b860515e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5712,6 +5712,13 @@
public static final String ASSISTANT = "assistant";
/**
+ * Whether the camera launch gesture should be disabled.
+ *
+ * @hide
+ */
+ public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5768,6 +5775,7 @@
UI_NIGHT_MODE,
SLEEP_TIMEOUT,
DOUBLE_TAP_TO_WAKE,
+ CAMERA_GESTURE_DISABLED,
};
/**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index ae2c0c7..4d8a7d0 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1430,8 +1430,9 @@
*/
public static boolean isGraphic(CharSequence str) {
final int len = str.length();
- for (int i=0; i<len; i++) {
- int gc = Character.getType(str.charAt(i));
+ for (int cp, i=0; i<len; i+=Character.charCount(cp)) {
+ cp = Character.codePointAt(str, i);
+ int gc = Character.getType(cp);
if (gc != Character.CONTROL
&& gc != Character.FORMAT
&& gc != Character.SURROGATE
@@ -1447,7 +1448,12 @@
/**
* Returns whether this character is a printable character.
+ *
+ * This does not support non-BMP characters and should not be used.
+ *
+ * @deprecated Use {@link #isGraphic(CharSequence)} instead.
*/
+ @Deprecated
public static boolean isGraphic(char c) {
int gc = Character.getType(c);
return gc != Character.CONTROL
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index a36e66c..9a69600 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -90,6 +90,14 @@
public static final int DENSITY_400 = 400;
/**
+ * Intermediate density for screens that sit somewhere between
+ * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+ * This is not a density that applications should target, instead relying
+ * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+ */
+ public static final int DENSITY_420 = 420;
+
+ /**
* Standard quantized DPI for extra-extra-high-density screens.
*/
public static final int DENSITY_XXHIGH = 480;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1c8a79b..ee06806 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -26,7 +26,6 @@
import android.annotation.RequiresPermission;
import android.content.Context;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -377,8 +376,8 @@
*/
private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
- final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
- final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+ final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
+ final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
// -----------------------------------------------------------
@@ -2033,8 +2032,7 @@
*/
public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
synchronized (mH) {
- HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
- new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
+ HashMap<InputMethodInfo, List<InputMethodSubtype>> ret = new HashMap<>();
try {
// TODO: We should change the return type from List<Object> to List<Parcelable>
List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
@@ -2049,7 +2047,7 @@
Log.e(TAG, "IMI list already contains the same InputMethod.");
break;
}
- subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes = new ArrayList<>();
ret.put((InputMethodInfo)o, subtypes);
} else if (subtypes != null && o instanceof InputMethodSubtype) {
subtypes.add((InputMethodSubtype)o);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 742173b..d53efa0 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -428,7 +428,7 @@
}
public static ArrayList<InputMethodSubtype> getSubtypes(InputMethodInfo imi) {
- ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
final int subtypeCount = imi.getSubtypeCount();
for (int i = 0; i < subtypeCount; ++i) {
subtypes.add(imi.getSubtypeAt(i));
@@ -438,7 +438,7 @@
public static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
InputMethodInfo imi, String mode) {
- ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
final int subtypeCount = imi.getSubtypeCount();
for (int i = 0; i < subtypeCount; ++i) {
final InputMethodSubtype subtype = imi.getSubtypeAt(i);
@@ -496,10 +496,9 @@
Resources res, InputMethodInfo imi) {
final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
final String systemLocale = res.getConfiguration().locale.toString();
- if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>();
+ if (TextUtils.isEmpty(systemLocale)) return new ArrayList<>();
final String systemLanguage = res.getConfiguration().locale.getLanguage();
- final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
- new HashMap<String, InputMethodSubtype>();
+ final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
final int N = subtypes.size();
for (int i = 0; i < N; ++i) {
// scan overriding implicitly enabled subtypes.
@@ -512,7 +511,7 @@
}
}
if (applicableModeAndSubtypesMap.size() > 0) {
- return new ArrayList<InputMethodSubtype>(applicableModeAndSubtypesMap.values());
+ return new ArrayList<>(applicableModeAndSubtypesMap.values());
}
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
@@ -545,7 +544,7 @@
}
final InputMethodSubtype keyboardSubtype
= applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD);
- final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
+ final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<>(
applicableModeAndSubtypesMap.values());
if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
for (int i = 0; i < N; ++i) {
@@ -818,8 +817,7 @@
String enabledInputMethodsStr,
TextUtils.SimpleStringSplitter inputMethodSplitter,
TextUtils.SimpleStringSplitter subtypeSplitter) {
- ArrayList<Pair<String, ArrayList<String>>> imsList =
- new ArrayList<Pair<String, ArrayList<String>>>();
+ ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
if (TextUtils.isEmpty(enabledInputMethodsStr)) {
return imsList;
}
@@ -828,13 +826,13 @@
String nextImsStr = inputMethodSplitter.next();
subtypeSplitter.setString(nextImsStr);
if (subtypeSplitter.hasNext()) {
- ArrayList<String> subtypeHashes = new ArrayList<String>();
+ ArrayList<String> subtypeHashes = new ArrayList<>();
// The first element is ime id.
String imeId = subtypeSplitter.next();
while (subtypeSplitter.hasNext()) {
subtypeHashes.add(subtypeSplitter.next());
}
- imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+ imsList.add(new Pair<>(imeId, subtypeHashes));
}
}
return imsList;
@@ -880,12 +878,6 @@
getEnabledInputMethodsAndSubtypeListLocked());
}
- public List<Pair<InputMethodInfo, ArrayList<String>>>
- getEnabledInputMethodAndSubtypeHashCodeListLocked() {
- return createEnabledInputMethodAndSubtypeHashCodeListLocked(
- getEnabledInputMethodsAndSubtypeListLocked());
- }
-
public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
Context context, InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) {
List<InputMethodSubtype> enabledSubtypes =
@@ -901,8 +893,7 @@
InputMethodInfo imi) {
List<Pair<String, ArrayList<String>>> imsList =
getEnabledInputMethodsAndSubtypeListLocked();
- ArrayList<InputMethodSubtype> enabledSubtypes =
- new ArrayList<InputMethodSubtype>();
+ ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
if (imi != null) {
for (Pair<String, ArrayList<String>> imsPair : imsList) {
InputMethodInfo info = mMethodMap.get(imsPair.first);
@@ -991,7 +982,7 @@
private List<InputMethodInfo> createEnabledInputMethodListLocked(
List<Pair<String, ArrayList<String>>> imsList) {
- final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
+ final ArrayList<InputMethodInfo> res = new ArrayList<>();
for (Pair<String, ArrayList<String>> ims: imsList) {
InputMethodInfo info = mMethodMap.get(ims.first);
if (info != null) {
@@ -1001,20 +992,6 @@
return res;
}
- private List<Pair<InputMethodInfo, ArrayList<String>>>
- createEnabledInputMethodAndSubtypeHashCodeListLocked(
- List<Pair<String, ArrayList<String>>> imsList) {
- final ArrayList<Pair<InputMethodInfo, ArrayList<String>>> res
- = new ArrayList<Pair<InputMethodInfo, ArrayList<String>>>();
- for (Pair<String, ArrayList<String>> ims : imsList) {
- InputMethodInfo info = mMethodMap.get(ims.first);
- if (info != null) {
- res.add(new Pair<InputMethodInfo, ArrayList<String>>(info, ims.second));
- }
- }
- return res;
- }
-
private void putEnabledInputMethodsStr(String str) {
Settings.Secure.putStringForUser(
mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str, mCurrentUserId);
@@ -1118,7 +1095,7 @@
if (DEBUG) {
Slog.d(TAG, "Enabled subtype found in the history: " + subtypeHashCode);
}
- return new Pair<String, String>(imeInTheHistory, subtypeHashCode);
+ return new Pair<>(imeInTheHistory, subtypeHashCode);
}
}
}
@@ -1179,7 +1156,7 @@
}
private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
- ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>();
+ ArrayList<Pair<String, String>> imsList = new ArrayList<>();
final String subtypeHistoryStr = getSubtypeHistoryStr();
if (TextUtils.isEmpty(subtypeHistoryStr)) {
return imsList;
@@ -1196,7 +1173,7 @@
subtypeId = mSubtypeSplitter.next();
break;
}
- imsList.add(new Pair<String, String>(imeId, subtypeId));
+ imsList.add(new Pair<>(imeId, subtypeId));
}
}
return imsList;
@@ -1229,11 +1206,6 @@
subtypeId, mCurrentUserId);
}
- public String getDisabledSystemInputMethods() {
- return Settings.Secure.getStringForUser(
- mResolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, mCurrentUserId);
- }
-
public String getSelectedInputMethod() {
if (DEBUG) {
Slog.d(TAG, "getSelectedInputMethodStr: " + Settings.Secure.getStringForUser(
@@ -1294,7 +1266,7 @@
public HashMap<InputMethodInfo, List<InputMethodSubtype>>
getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) {
HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
- new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
+ new HashMap<>();
for (InputMethodInfo imi: getEnabledInputMethodListLocked()) {
enabledInputMethodAndSubtypes.put(
imi, getEnabledInputMethodSubtypeListLocked(context, imi, true));
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index eccf5fa..1068b51 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -16,8 +16,10 @@
package com.android.internal.policy;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID;
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.HOME_STACK_ID;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
@@ -4011,8 +4013,10 @@
boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION;
mWorkspaceId = getWorkspaceId();
- // Only a non floating application window can get a non client decor.
- if (!isFloating() && isApplication) {
+ // Only a non floating application window on one of the allowed worksapces can get a non
+ // client decor.
+ if (!isFloating() && isApplication && mWorkspaceId != HOME_STACK_ID &&
+ mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
TypedValue value = new TypedValue();
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index af0c9cd..c66fd15 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -3,6 +3,7 @@
import com.android.frameworks.coretests.R;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;
@@ -18,6 +19,32 @@
super(AnimatorSetActivity.class);
}
+ static class MyListener implements Animator.AnimatorListener {
+ boolean startIsCalled = false;
+ boolean endIsCalled = false;
+ boolean cancelIsCalled = false;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ startIsCalled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endIsCalled = true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancelIsCalled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ }
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -86,12 +113,7 @@
@SmallTest
public void testTotalDuration() {
- ArrayList<Animator> list = new ArrayList<>(5);
- list.add(a1);
- list.add(a2);
- list.add(a3);
- list.add(a4);
- list.add(a5);
+ ArrayList<Animator> list = getAnimatorList();
// Run animations sequentially and test the total duration against sum of durations.
AnimatorSet s1 = new AnimatorSet();
@@ -152,4 +174,328 @@
}
+ @SmallTest
+ public void testGetDuration() {
+ AnimatorSet s = new AnimatorSet();
+ assertTrue(s.getDuration() < 0);
+ s.play(a1).before(a2).before(a3).after(a4).after(a5);
+ assertTrue(s.getDuration() < 0);
+
+ long duration = 200;
+ s.setDuration(duration);
+ assertEquals(duration, s.getDuration());
+
+ }
+
+ @SmallTest
+ @UiThreadTest
+ public void testSetDuration() {
+ AnimatorSet s = getSequentialSet();
+ assertTrue(s.getDuration() < 0);
+
+ long duration = 300;
+ s.setDuration(duration);
+ assertEquals(duration, s.getDuration());
+
+ s.start();
+ assertEquals(duration, s.getDuration());
+ assertEquals(duration, a1.getDuration());
+ assertEquals(duration, a2.getDuration());
+ assertEquals(duration, a3.getDuration());
+ assertEquals(duration, a4.getDuration());
+ assertEquals(duration, a5.getDuration());
+ }
+
+ @SmallTest
+ public void testAddListener() throws InterruptedException {
+ // Verify that the listener is added to the list of listeners in the AnimatorSet
+ // and that newly added listener gets callback for lifecycle events of the animator
+ final AnimatorSet s = new AnimatorSet();
+ s.play(a1).before(a2).before(a3).after(a4).after(a5);
+ final MyListener listener = new MyListener();
+ if (s.getListeners() != null) {
+ assertFalse(s.getListeners().contains(listener));
+ }
+ s.addListener(listener);
+ assertTrue(s.getListeners().contains(listener));
+
+ assertFalse(listener.startIsCalled);
+ assertFalse(listener.endIsCalled);
+
+ try {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ assertTrue(listener.startIsCalled);
+ assertFalse(listener.endIsCalled);
+ }
+ });
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+
+ Thread.sleep(s.getTotalDuration() + 200);
+ assertTrue(listener.startIsCalled);
+ assertTrue(listener.endIsCalled);
+ }
+
+ @SmallTest
+ public void testRemoveListener() throws Throwable {
+ final AnimatorSet s = new AnimatorSet();
+ s.playTogether(a1, a2, a3, a4);
+ MyListener listener = new MyListener();
+ s.addListener(listener);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ }
+ });
+
+ Thread.sleep(s.getTotalDuration() + 100);
+ assertTrue(listener.startIsCalled);
+ assertTrue(listener.endIsCalled);
+
+ s.removeListener(listener);
+ if (s.getListeners() != null) {
+ assertFalse(s.getListeners().contains(listener));
+ }
+ listener.startIsCalled = false;
+ listener.endIsCalled = false;
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ }
+ });
+ Thread.sleep(s.getTotalDuration() + 100);
+ assertFalse(listener.startIsCalled);
+ assertFalse(listener.endIsCalled);
+ }
+
+ @SmallTest
+ public void testEnd() throws Throwable {
+ // End animator set
+ final AnimatorSet s = new AnimatorSet();
+ s.play(a1).before(a2).after(a3).with(a4);
+ final MyListener listener = new MyListener();
+ s.addListener(listener);
+ assertFalse(listener.endIsCalled);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ assertTrue(s.isStarted());
+ assertTrue(listener.startIsCalled);
+ assertFalse(listener.endIsCalled);
+ }
+ });
+
+ Thread.sleep(a2.getTotalDuration());
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.end();
+ assertTrue(listener.startIsCalled);
+ assertTrue(listener.endIsCalled);
+ assertFalse(s.isRunning());
+ assertFalse(s.isStarted());
+
+ assertFalse(a1.isStarted());
+ assertFalse(a2.isStarted());
+ assertFalse(a3.isStarted());
+ assertFalse(a4.isStarted());
+ }
+ });
+
+ }
+
+ @SmallTest
+ public void testStart() throws Throwable {
+ final AnimatorSet s = new AnimatorSet();
+ ArrayList<Animator> animators = getAnimatorList();
+
+ s.playSequentially(animators);
+ final MyListener l = new MyListener();
+ s.addListener(l);
+
+ ArrayList<MyListener> listeners = new ArrayList<>(animators.size());
+ for (int i = 0; i < animators.size(); i++) {
+ MyListener listener = new MyListener();
+ listeners.add(listener);
+ animators.get(i).addListener(listener);
+ }
+
+ // Check the state before calling start()
+ assertFalse(l.startIsCalled);
+ assertFalse(l.endIsCalled);
+ for (int i = 0; i < listeners.size(); i++) {
+ assertFalse(l.startIsCalled);
+ assertFalse(l.endIsCalled);
+ }
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ assertTrue(l.startIsCalled);
+ }
+ });
+
+ long timeout = s.getTotalDuration() * 2;
+ long wait = 0;
+
+ while (wait < timeout) {
+ if (l.endIsCalled) {
+ break;
+ }
+ Thread.sleep(200);
+ wait += 200;
+ }
+
+ // Now the set should finished
+ assertTrue(l.startIsCalled);
+ assertTrue(l.endIsCalled);
+ for (int i = 0; i < listeners.size(); i++) {
+ assertTrue(listeners.get(i).startIsCalled);
+ assertTrue(listeners.get(i).endIsCalled);
+ }
+ }
+
+ @SmallTest
+ public void testCancel() throws Throwable {
+ // Check whether cancel would trigger onAnimationCanceled and cancel all the unfinished
+ // animations
+ final AnimatorSet s = new AnimatorSet();
+ final ArrayList<Animator> animators = getAnimatorList();
+
+ s.playTogether(animators);
+ final MyListener l = new MyListener();
+ s.addListener(l);
+
+ final ArrayList<MyListener> listeners = new ArrayList<>(5);
+ for (int i = 0; i < animators.size(); i++) {
+ MyListener listener = new MyListener();
+ listeners.add(listener);
+ animators.get(i).addListener(listener);
+ }
+
+ // Check the state before calling start()
+ assertFalse(l.startIsCalled);
+ assertFalse(l.cancelIsCalled);
+ assertFalse(l.endIsCalled);
+ for (int i = 0; i < listeners.size(); i++) {
+ assertFalse(l.startIsCalled);
+ assertFalse(l.cancelIsCalled);
+ assertFalse(l.endIsCalled);
+ }
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ }
+ });
+
+ Thread.sleep(a1.getTotalDuration());
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(s.isStarted());
+ ArrayList<Integer> runningAnimIds = new ArrayList<Integer>();
+ for (int i = 0; i < animators.size(); i++) {
+ if (animators.get(i).isStarted()) {
+ runningAnimIds.add(i);
+ }
+ }
+ s.cancel();
+ assertTrue(l.startIsCalled);
+ assertTrue(l.cancelIsCalled);
+ assertTrue(l.endIsCalled);
+
+ for (int i = 0; i < listeners.size(); i++) {
+ assertTrue(listeners.get(i).startIsCalled);
+ if (runningAnimIds.contains(i)) {
+ assertTrue(listeners.get(i).cancelIsCalled);
+ }
+ assertTrue(listeners.get(i).endIsCalled);
+ }
+ }
+ });
+
+ }
+
+ @SmallTest
+ public void testIsRunning() throws Throwable {
+ final AnimatorSet s = new AnimatorSet();
+ final long startDelay = 500;
+ s.play(a1).before(a2).after(a3).with(a4);
+ s.play(a3).after(a5);
+ s.setStartDelay(startDelay);
+ MyListener listener = new MyListener();
+ s.addListener(listener);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ s.start();
+ }
+ });
+
+ while (!listener.endIsCalled) {
+ boolean passedStartDelay = a1.isStarted() || a2.isStarted() || a3.isStarted() ||
+ a4.isStarted() || a5.isStarted();
+ assertEquals(passedStartDelay, s.isRunning());
+ Thread.sleep(50);
+ }
+ assertFalse(s.isRunning());
+ }
+
+ @SmallTest
+ public void testPauseAndResume() throws Throwable {
+ final AnimatorSet set = getSequentialSet();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Calling pause before start should have no effect, per documentation
+ set.pause();
+ set.start();
+ assertFalse(set.isPaused());
+ }
+ });
+
+ while (!a2.isStarted()) {
+ Thread.sleep(50);
+ }
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(set.isPaused());
+ set.pause();
+ assertTrue(set.isPaused());
+ set.resume();
+ assertFalse(set.isPaused());
+ }
+ });
+ }
+
+ // Create an AnimatorSet with all the animators running sequentially
+ private AnimatorSet getSequentialSet() {
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(a1, a2, a3, a4, a5);
+ return set;
+ }
+
+ private ArrayList<Animator> getAnimatorList() {
+ ArrayList<Animator> list = new ArrayList<>();
+ list.add(a1);
+ list.add(a2);
+ list.add(a3);
+ list.add(a4);
+ list.add(a5);
+ return list;
+ }
+
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index bf069d3..32f6a89 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -786,12 +786,16 @@
mMaskColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_IN);
}
- // Draw the appropriate mask.
+ // Draw the appropriate mask anchored to (0,0).
+ final int left = bounds.left;
+ final int top = bounds.top;
+ mMaskCanvas.translate(-left, -top);
if (maskType == MASK_EXPLICIT) {
drawMask(mMaskCanvas);
} else if (maskType == MASK_CONTENT) {
drawContent(mMaskCanvas);
}
+ mMaskCanvas.translate(left, top);
}
private int getMaskType() {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 7605231..f42d750 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -38,27 +38,35 @@
/**
* {@link AlgorithmParameterSpec} for initializing a {@link KeyPairGenerator} or a
* {@link KeyGenerator} of the <a href="{@docRoot}training/articles/keystore.html">Android Keystore
- * system</a>. The spec determines whether user authentication is required for using the key, what
- * uses the key is authorized for (e.g., only for signing -- decryption not permitted), the key's
- * validity start and end dates.
+ * system</a>. The spec determines authorized uses of the key, such as whether user authentication
+ * is required for using the key, what operations are authorized (e.g., signing, but not
+ * decryption) and with what parameters (e.g., only with a particular padding scheme or digest), the
+ * key's validity start and end dates. Key use authorizations expressed in the spec apply only to
+ * secret keys and private keys -- public keys can be used for any supported operations.
*
* <p>To generate an asymmetric key pair or a symmetric key, create an instance of this class using
* the {@link Builder}, initialize a {@code KeyPairGenerator} or a {@code KeyGenerator} of the
* desired key type (e.g., {@code EC} or {@code AES} -- see
* {@link KeyProperties}.{@code KEY_ALGORITHM} constants) from the {@code AndroidKeyStore} provider
- * with the {@code KeyPairGeneratorSpec} instance, and then generate a key or key pair using
- * {@link KeyPairGenerator#generateKeyPair()}.
+ * with the {@code KeyGenParameterSpec} instance, and then generate a key or key pair using
+ * {@link KeyGenerator#generateKey()} or {@link KeyPairGenerator#generateKeyPair()}.
*
* <p>The generated key pair or key will be returned by the generator and also stored in the Android
- * Keystore system under the alias specified in this spec. To obtain the secret or private key from
- * the Android KeyStore use {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)}
+ * Keystore under the alias specified in this spec. To obtain the secret or private key from the
+ * Android Keystore use {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)}
* or {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}.
- * To obtain the public key from the Android Keystore system use
+ * To obtain the public key from the Android Keystore use
* {@link java.security.KeyStore#getCertificate(String)} and then
* {@link Certificate#getPublicKey()}.
*
+ * <p>To help obtain algorithm-specific public parameters of key pairs stored in the Android
+ * Keystore, generated private keys implement {@link java.security.interfaces.ECKey} or
+ * {@link java.security.interfaces.RSAKey} interfaces whereas public keys implement
+ * {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey}
+ * interfaces.
+ *
* <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in
- * the Android KeyStore. This is because the {@link java.security.KeyStore} abstraction does not
+ * the Android Keystore. This is because the {@link java.security.KeyStore} abstraction does not
* support storing key pairs without a certificate. The subject, serial number, and validity dates
* of the certificate can be customized in this spec. The self-signed certificate may be replaced at
* a later time by a certificate signed by a Certificate Authority (CA).
@@ -82,27 +90,33 @@
*
* <p>Instances of this class are immutable.
*
- * <p><h3>Example: Asymmetric key pair</h3>
- * The following example illustrates how to generate an EC key pair in the Android KeyStore system
- * under alias {@code key1} authorized to be used only for signing using SHA-256, SHA-384,
- * or SHA-512 digest and only if the user has been authenticated within the last five minutes.
+ * <p><h3>Example: NIST P-256 EC key pair for signing/verification using ECDSA</h3>
+ * This example illustrates how to generate a NIST P-256 (aka secp256r1 aka prime256v1) EC key pair
+ * in the Android KeyStore system under alias {@code key1} where the private key is authorized to be
+ * used only for signing using SHA-256, SHA-384, or SHA-512 digest and only if the user has been
+ * authenticated within the last five minutes. The use of public key is unrestricted, thus
+ * permitting signature verification using any padding schemes and digests, and without user
+ * authentication.
* <pre> {@code
* KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
- * KeyProperties.KEY_ALGORITHM_EC,
- * "AndroidKeyStore");
+ * KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
* keyPairGenerator.initialize(
* new KeyGenParameterSpec.Builder(
* "key1",
- * KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ * KeyProperties.PURPOSE_SIGN)
+ * .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
* .setDigests(KeyProperties.DIGEST_SHA256,
* KeyProperties.DIGEST_SHA384,
* KeyProperties.DIGEST_SHA512)
- * // Only permit this key to be used if the user authenticated
+ * // Only permit the private key to be used if the user authenticated
* // within the last five minutes.
* .setUserAuthenticationRequired(true)
* .setUserAuthenticationValidityDurationSeconds(5 * 60)
* .build());
* KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ * Signature signature = Signature.getInstance("SHA256withECDSA");
+ * signature.initSign(keyPair.getPrivate());
+ * ...
*
* // The key pair can also be obtained from the Android Keystore any time as follows:
* KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
@@ -111,14 +125,67 @@
* PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();
* }</pre>
*
- * <p><h3>Example: Symmetric key</h3>
+ * <p><h3>Example: RSA key pair for signing/verification using RSA-PSS</h3>
+ * This example illustrates how to generate an RSA key pair in the Android KeyStore system under
+ * alias {@code key1} authorized to be used only for signing using the RSA-PSS signature padding
+ * scheme with SHA-256 or SHA-512 digests. The use of public key is unrestricted, thus permitting
+ * signature verification using any padding schemes and digests.
+ * <pre> {@code
+ * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+ * KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
+ * keyPairGenerator.initialize(
+ * new KeyGenParameterSpec.Builder(
+ * "key1",
+ * KeyProperties.PURPOSE_SIGN)
+ * .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ * .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+ * .build());
+ * KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ * Signature signature = Signature.getInstance("SHA256withRSA/PSS");
+ * signature.initSign(keyPair.getPrivate());
+ * ...
+ *
+ * // The key pair can also be obtained from the Android Keystore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null);
+ * PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();
+ * }</pre>
+ *
+ * <p><h3>Example: RSA key pair for encryption/decryption using RSA OAEP</h3>
+ * This example illustrates how to generate an RSA key pair in the Android KeyStore system under
+ * alias {@code key1} where the private key is authorized to be used only for decryption using RSA
+ * OAEP encryption padding scheme with SHA-256 or SHA-512 digests. The use of public key is
+ * unrestricted, thus permitting encryption using any padding schemes and digests.
+ * <pre> {@code
+ * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+ * KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
+ * keyPairGenerator.initialize(
+ * new KeyGenParameterSpec.Builder(
+ * "key1",
+ * KeyProperties.PURPOSE_DECRYPT)
+ * .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
+ * .build());
+ * KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ * Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+ * cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
+ * ...
+ *
+ * // The key pair can also be obtained from the Android Keystore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null);
+ * PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();
+ * }</pre>
+ *
+ * <p><h3>Example: AES key for encryption/decryption in GCM mode</h3>
* The following example illustrates how to generate an AES key in the Android KeyStore system under
* alias {@code key2} authorized to be used only for encryption/decryption in GCM mode with no
* padding.
* <pre> {@code
* KeyGenerator keyGenerator = KeyGenerator.getInstance(
- * KeyProperties.KEY_ALGORITHM_AES,
- * "AndroidKeyStore");
+ * KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
* keyGenerator.initialize(
* new KeyGenParameterSpec.Builder("key2",
* KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
@@ -127,6 +194,29 @@
* .build());
* SecretKey key = keyGenerator.generateKey();
*
+ * Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ * cipher.init(Cipher.ENCRYPT_MODE, key);
+ * ...
+ *
+ * // The key can also be obtained from the Android Keystore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * key = (SecretKey) keyStore.getKey("key2", null);
+ * }</pre>
+ *
+ * <p><h3>Example: HMAC key for generating a MAC using SHA-256</h3>
+ * This example illustrates how to generate an HMAC key in the Android KeyStore system under alias
+ * {@code key2} authorized to be used only for generating an HMAC using SHA-256.
+ * <pre> {@code
+ * KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ * KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
+ * keyGenerator.initialize(
+ * new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_SIGN).build());
+ * SecretKey key = keyGenerator.generateKey();
+ * Mac mac = Mac.getInstance("HmacSHA256");
+ * mac.init(key);
+ * ...
+ *
* // The key can also be obtained from the Android Keystore any time as follows:
* KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
* keyStore.load(null);
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index b71dc82..c984439 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -33,28 +33,36 @@
/**
* Specification of how a key or key pair is secured when imported into the
- * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. This class
- * specifies parameters such as whether user authentication is required for using the key, what uses
- * the key is authorized for (e.g., only in {@code GCM} mode, or only for signing -- decryption not
- * permitted), the key's and validity start and end dates.
+ * <a href="{@docRoot}training/articles/keystore.html">Android Keystore system</a>. This class
+ * specifies authorized uses of the imported key, such as whether user authentication is required
+ * for using the key, what operations the key is authorized for (e.g., decryption, but not signing)
+ * and with what parameters (e.g., only with a particular padding scheme or digest), the key's and
+ * validity start and end dates. Key use authorizations expressed in this class apply only to secret
+ * keys and private keys -- public keys can be used for any supported operations.
*
- * <p>To import a key or key pair into the Android KeyStore, create an instance of this class using
+ * <p>To import a key or key pair into the Android Keystore, create an instance of this class using
* the {@link Builder} and pass the instance into {@link java.security.KeyStore#setEntry(String, java.security.KeyStore.Entry, ProtectionParameter) KeyStore.setEntry}
* with the key or key pair being imported.
*
- * <p>To obtain the secret/symmetric or private key from the Android KeyStore use
+ * <p>To obtain the secret/symmetric or private key from the Android Keystore use
* {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or
* {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}.
- * To obtain the public key from the Android KeyStore use
+ * To obtain the public key from the Android Keystore use
* {@link java.security.KeyStore#getCertificate(String)} and then
* {@link Certificate#getPublicKey()}.
*
- * <p>NOTE: The key material of keys stored in the Android KeyStore is not accessible.
+ * <p>To help obtain algorithm-specific public parameters of key pairs stored in the Android
+ * Keystore, its private keys implement {@link java.security.interfaces.ECKey} or
+ * {@link java.security.interfaces.RSAKey} interfaces whereas its public keys implement
+ * {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey}
+ * interfaces.
+ *
+ * <p>NOTE: The key material of keys stored in the Android Keystore is not accessible.
*
* <p>Instances of this class are immutable.
*
- * <p><h3>Example: Symmetric Key</h3>
- * The following example illustrates how to import an AES key into the Android KeyStore under alias
+ * <p><h3>Example: AES key for encryption/decryption in GCM mode</h3>
+ * This example illustrates how to import an AES key into the Android KeyStore under alias
* {@code key1} authorized to be used only for encryption/decryption in GCM mode with no padding.
* The key must export its key material via {@link Key#getEncoded()} in {@code RAW} format.
* <pre> {@code
@@ -71,15 +79,41 @@
* .build());
* // Key imported, obtain a reference to it.
* SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
- * // The original key can now be thrown away.
+ * // The original key can now be discarded.
+ *
+ * Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ * cipher.init(Cipher.ENCRYPT_MODE, keyStoreKey);
+ * ...
* }</pre>
*
- * <p><h3>Example: Asymmetric Key Pair</h3>
- * The following example illustrates how to import an EC key pair into the Android KeyStore under
- * alias {@code key2} authorized to be used only for signing with SHA-256 digest and only if
- * the user has been authenticated within the last ten minutes. Both the private and the public key
- * must export their key material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509}
- * format respectively.
+ * <p><h3>Example: HMAC key for generating MACs using SHA-512</h3>
+ * This example illustrates how to import an HMAC key into the Android KeyStore under alias
+ * {@code key1} authorized to be used only for generating MACs using SHA-512 digest. The key must
+ * export its key material via {@link Key#getEncoded()} in {@code RAW} format.
+ * <pre> {@code
+ * SecretKey key = ...; // HMAC key of algorithm "HmacSHA512".
+ *
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * keyStore.setEntry(
+ * "key1",
+ * new KeyStore.SecretKeyEntry(key),
+ * new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build());
+ * // Key imported, obtain a reference to it.
+ * SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
+ * // The original key can now be discarded.
+ *
+ * Mac mac = Mac.getInstance("HmacSHA512");
+ * mac.init(keyStoreKey);
+ * ...
+ * }</pre>
+ *
+ * <p><h3>Example: EC key pair for signing/verification using ECDSA</h3>
+ * This example illustrates how to import an EC key pair into the Android KeyStore under alias
+ * {@code key2} with the private key authorized to be used only for signing with SHA-256 or SHA-512
+ * digests. The use of public key is unrestricted, thus permitting signature verification using any
+ * digests. Both the private and the public key must export their key material via
+ * {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509} format respectively.
* <pre> {@code
* PrivateKey privateKey = ...; // EC private key
* Certificate[] certChain = ...; // Certificate chain with the first certificate
@@ -91,7 +125,39 @@
* "key2",
* new KeyStore.PrivateKeyEntry(privateKey, certChain),
* new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
+ * .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ * .build());
+ * // Key pair imported, obtain a reference to it.
+ * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null);
+ * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
+ * // The original private key can now be discarded.
+ *
+ * Signature signature = Signature.getInstance("SHA256withECDSA");
+ * signature.initSign(keyStorePrivateKey);
+ * ...
+ * }</pre>
+ *
+ * <p><h3>Example: RSA key pair for signing/verification using PKCS#1 padding</h3>
+ * This example illustrates how to import an RSA key pair into the Android KeyStore under alias
+ * {@code key2} with the private key authorized to be used only for signing using the PKCS#1
+ * signature padding scheme with SHA-256 digest and only if the user has been authenticated within
+ * the last ten minutes. The use of public key is unrestricted, thus permitting signature
+ * verification using any padding schemes and digests, and without user authentication. Both the
+ * private and the public key must export their key material via {@link Key#getEncoded()} in
+ * {@code PKCS#8} and {@code X.509} format respectively.
+ * <pre> {@code
+ * PrivateKey privateKey = ...; // RSA private key
+ * Certificate[] certChain = ...; // Certificate chain with the first certificate
+ * // containing the corresponding RSA public key.
+ *
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * keyStore.setEntry(
+ * "key2",
+ * new KeyStore.PrivateKeyEntry(privateKey, certChain),
+ * new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
* .setDigests(KeyProperties.DIGEST_SHA256)
+ * .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
* // Only permit this key to be used if the user
* // authenticated within the last ten minutes.
* .setUserAuthenticationRequired(true)
@@ -100,7 +166,40 @@
* // Key pair imported, obtain a reference to it.
* PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null);
* PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
- * // The original private key can now be thrown away.
+ * // The original private key can now be discarded.
+ *
+ * Signature signature = Signature.getInstance("SHA256withRSA");
+ * signature.initSign(keyStorePrivateKey);
+ * ...
+ * }</pre>
+ *
+ * <p><h3>Example: RSA key pair for encryption/decryption using PKCS#1 padding</h3>
+ * This example illustrates how to import an RSA key pair into the Android KeyStore under alias
+ * {@code key2} with the private key authorized to be used only for decryption using the PKCS#1
+ * encryption padding scheme. The use of public key is unrestricted, thus permitting encryption
+ * using any padding schemes and digests. Both the private and the public key must export their key
+ * material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509} format respectively.
+ * <pre> {@code
+ * PrivateKey privateKey = ...; // RSA private key
+ * Certificate[] certChain = ...; // Certificate chain with the first certificate
+ * // containing the corresponding RSA public key.
+ *
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * keyStore.setEntry(
+ * "key2",
+ * new KeyStore.PrivateKeyEntry(privateKey, certChain),
+ * new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
+ * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
+ * .build());
+ * // Key pair imported, obtain a reference to it.
+ * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null);
+ * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
+ * // The original private key can now be discarded.
+ *
+ * Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ * cipher.init(Cipher.DECRYPT_MODE, keyStorePrivateKey);
+ * ...
* }</pre>
*/
public final class KeyProtection implements ProtectionParameter {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 8f792de..dfc31ab 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -234,6 +234,12 @@
}
}
+ @Override
+ public void showMessage(String message, int color) {
+ mSecurityMessageDisplay.setNextMessageColor(color);
+ mSecurityMessageDisplay.setMessage(message, true /* important */);
+ }
+
protected abstract int getPromtReasonStringRes(int reason);
// Cause a VIRTUAL_KEY vibration
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index ff4e815..32892cf 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -170,6 +170,10 @@
mSecurityContainer.showPromptReason(reason);
}
+ public void showMessage(String message, int color) {
+ mSecurityContainer.showMessage(message, color);
+ }
+
/**
* Dismisses the keyguard by going to the next screen or making it gone.
*
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 2951af9..c8adf64 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -39,15 +39,18 @@
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
+ private static final int DEFAULT_COLOR = -1;
private static final int SECURITY_MESSAGE_DURATION = 5000;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mHandler;
+ private final int mDefaultColor;
// Timeout before we reset the message to show charging/owner info
long mTimeout = SECURITY_MESSAGE_DURATION;
CharSequence mMessage;
+ private int mNextMessageColor = DEFAULT_COLOR;
private final Runnable mClearMessageRunnable = new Runnable() {
@Override
@@ -78,10 +81,16 @@
mUpdateMonitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
+ mDefaultColor = getCurrentTextColor();
update();
}
@Override
+ public void setNextMessageColor(int color) {
+ mNextMessageColor = color;
+ }
+
+ @Override
public void setMessage(CharSequence msg, boolean important) {
if (!TextUtils.isEmpty(msg) && important) {
securityMessageChanged(msg);
@@ -151,6 +160,12 @@
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
setText(status);
+ int color = mDefaultColor;
+ if (mNextMessageColor != DEFAULT_COLOR) {
+ color = mNextMessageColor;
+ mNextMessageColor = DEFAULT_COLOR;
+ }
+ setTextColor(color);
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 4bd1a2e..a96c79f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,10 +15,6 @@
*/
package com.android.keyguard;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.os.AsyncTask;
@@ -28,7 +24,6 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
@@ -342,6 +337,12 @@
}
@Override
+ public void showMessage(String message, int color) {
+ mSecurityMessageDisplay.setNextMessageColor(color);
+ mSecurityMessageDisplay.setMessage(message, true /* important */);
+ }
+
+ @Override
public void startAppearAnimation() {
enableClipping(false);
setAlpha(1f);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 85da298..8fc3cde 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -518,6 +518,13 @@
}
}
+
+ public void showMessage(String message, int color) {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
+ }
+ }
+
@Override
public void showUsabilityHint() {
mSecurityViewFlipper.showUsabilityHint();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index 5658a74..7e82c63 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -77,6 +77,14 @@
void showPromptReason(int reason);
/**
+ * Show a message on the security view with a specified color
+ *
+ * @param message the message to show
+ * @param color the color to use
+ */
+ void showMessage(String message, int color);
+
+ /**
* Instruct the view to show usability hints, if any.
*
*/
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index a0ff21b..6012c45 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -139,6 +139,14 @@
}
@Override
+ public void showMessage(String message, int color) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showMessage(message, color);
+ }
+ }
+
+ @Override
public void showUsabilityHint() {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 55d85206..0ee68fd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -118,7 +118,13 @@
* Mode in which we wake up the device, but play the normal dismiss animation. Active when we
* acquire a fingerprint pulsing in doze mode.
* */
- private static final int FP_WAKE_WAKE_TO_BOUNCER = 2;
+ private static final int FP_WAKE_TO_BOUNCER = 2;
+
+ /**
+ * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
+ * fingerprint.
+ * */
+ private static final int FP_ONLY_WAKE = 3;
// Callback messages
private static final int MSG_TIME_UPDATE = 301;
@@ -401,14 +407,14 @@
mWakeLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
mWakeLock.acquire();
- mFpWakeMode = FP_WAKE_DIRECT_UNLOCK;
+ mFpWakeMode = mKeyguardIsVisible ? FP_WAKE_DIRECT_UNLOCK : FP_ONLY_WAKE;
if (DEBUG_FP_WAKELOCK) {
Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
}
mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
FINGERPRINT_WAKELOCK_TIMEOUT_MS);
} else if (!mDeviceInteractive) {
- mFpWakeMode = FP_WAKE_WAKE_TO_BOUNCER;
+ mFpWakeMode = FP_WAKE_TO_BOUNCER;
} else {
mFpWakeMode = FP_WAKE_NONE;
}
@@ -436,7 +442,8 @@
}
private void handleFingerprintAuthenticated() {
- if (mFpWakeMode == FP_WAKE_WAKE_TO_BOUNCER || mFpWakeMode == FP_WAKE_DIRECT_UNLOCK) {
+ if (mFpWakeMode == FP_WAKE_TO_BOUNCER || mFpWakeMode == FP_WAKE_DIRECT_UNLOCK
+ || mFpWakeMode == FP_ONLY_WAKE) {
if (DEBUG_FP_WAKELOCK) {
Log.i(TAG, "fp wakelock: Authenticated, waking up...");
}
@@ -942,9 +949,7 @@
}
private boolean shouldListenForFingerprint() {
- return mKeyguardIsVisible && !mSwitchingUser &&
- mTrustManager.hasUserAuthenticatedSinceBoot(
- ActivityManager.getCurrentUser());
+ return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser;
}
private void startListeningForFingerprint() {
diff --git a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
index b38cfd5..ddb1f6e 100644
--- a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -17,11 +17,14 @@
package com.android.keyguard;
public interface SecurityMessageDisplay {
- public void setMessage(CharSequence msg, boolean important);
- public void setMessage(int resId, boolean important);
+ void setNextMessageColor(int color);
- public void setMessage(int resId, boolean important, Object... formatArgs);
+ void setMessage(CharSequence msg, boolean important);
- public void setTimeout(int timeout_ms);
+ void setMessage(int resId, boolean important);
+
+ void setMessage(int resId, boolean important, Object... formatArgs);
+
+ void setTimeout(int timeout_ms);
}
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png
new file mode 100644
index 0000000..165ef4f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
index d6e2065..f95f09f 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
index 6be4161..860a906 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
index b031273..bab268e 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png
new file mode 100644
index 0000000..0feb405
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
index 12ceb90..cabab0d 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
index dc8809e..16e1bf5 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
index 5178ac5..40375de 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png
new file mode 100644
index 0000000..b7b8f98
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
index 98be526..69b7449 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
index eed3f54..57d243c 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
index 22ae09d..e53eaff 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png
new file mode 100644
index 0000000..695e7a4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
index c819545..88294c0 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
index 6075caf..09d684a 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
index bccda1b..e31ea32 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png
new file mode 100644
index 0000000..24f12d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png
new file mode 100644
index 0000000..51482f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png
new file mode 100644
index 0000000..46c7b18
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png
new file mode 100644
index 0000000..396ad7d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 42c4e12..a6cea62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -42,6 +42,7 @@
import android.database.ContentObserver;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -1816,10 +1817,16 @@
sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ final Icon smallIcon = n.getSmallIcon();
+ if (smallIcon == null) {
+ handleNotificationError(sbn,
+ "No small icon in notification from " + sbn.getPackageName());
+ return null;
+ }
final StatusBarIcon ic = new StatusBarIcon(
sbn.getUser(),
sbn.getPackageName(),
- n.getSmallIcon(),
+ smallIcon,
n.iconLevel,
n.number,
n.tickerText);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 46b00c7..ac4dee2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,18 +16,13 @@
package com.android.systemui.statusbar;
-import com.android.internal.app.IBatteryStats;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Handler;
@@ -40,8 +35,16 @@
import android.util.Log;
import android.view.View;
+import com.android.internal.app.IBatteryStats;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.statusbar.phone.LockIcon;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
/**
- * Controls the little text indicator on the keyguard.
+ * Controls the indications and error messages shown on the Keyguard
*/
public class KeyguardIndicationController {
@@ -49,6 +52,8 @@
private static final boolean DEBUG_CHARGING_CURRENT = false;
private static final int MSG_HIDE_TRANSIENT = 1;
+ private static final int MSG_CLEAR_FP_MSG = 2;
+ private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
private final Context mContext;
private final KeyguardIndicationTextView mTextView;
@@ -56,6 +61,8 @@
private final int mSlowThreshold;
private final int mFastThreshold;
+ private final LockIcon mLockIcon;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private String mRestingIndication;
private String mTransientIndication;
@@ -66,10 +73,13 @@
private boolean mPowerCharged;
private int mChargingSpeed;
private int mChargingCurrent;
+ private String mMessageToShowOnScreenOn;
- public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView) {
+ public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
+ LockIcon lockIcon) {
mContext = context;
mTextView = textView;
+ mLockIcon = lockIcon;
Resources res = context.getResources();
mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
@@ -216,6 +226,64 @@
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
updateIndication();
}
+
+ @Override
+ public void onFingerprintHelp(int msgId, String helpString) {
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+ return;
+ }
+ int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
+ } else if (updateMonitor.isDeviceInteractive()) {
+ mLockIcon.setTransientFpError(true);
+ showTransientIndication(helpString, errorColor);
+ mHandler.removeMessages(MSG_CLEAR_FP_MSG);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
+ TRANSIENT_FP_ERROR_TIMEOUT);
+ }
+ }
+
+ @Override
+ public void onFingerprintError(int msgId, String errString) {
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ if (!updateMonitor.isUnlockingWithFingerprintAllowed()
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+ return;
+ }
+ int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
+ } else if (updateMonitor.isDeviceInteractive()) {
+ showTransientIndication(errString, errorColor);
+ // We want to keep this message around in case the screen was off
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(5000);
+ } else {
+ mMessageToShowOnScreenOn = errString;
+ }
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ if (mMessageToShowOnScreenOn != null) {
+ int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
+ null);
+ showTransientIndication(mMessageToShowOnScreenOn, errorColor);
+ // We want to keep this message around in case the screen was off
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(5000);
+ mMessageToShowOnScreenOn = null;
+ }
+ }
+
+ @Override
+ public void onFingerprintRunningStateChanged(boolean running) {
+ if (running) {
+ mMessageToShowOnScreenOn = null;
+ }
+ }
};
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -233,7 +301,15 @@
if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
mTransientIndication = null;
updateIndication();
+ } else if (msg.what == MSG_CLEAR_FP_MSG) {
+ mLockIcon.setTransientFpError(false);
+ hideTransientIndication();
}
}
};
+
+ public void setStatusBarKeyguardViewManager(
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 7b67c6c..4878cd92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -29,7 +29,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.hardware.fingerprint.FingerprintManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
@@ -86,7 +85,6 @@
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
- private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
private KeyguardAffordanceView mCameraImageView;
private KeyguardAffordanceView mLeftAffordanceView;
@@ -528,7 +526,7 @@
return mCameraPreview;
}
- public KeyguardAffordanceView getLockIcon() {
+ public LockIcon getLockIcon() {
return mLockIcon;
}
@@ -613,21 +611,6 @@
}
};
- private final Runnable mTransientFpErrorClearRunnable = new Runnable() {
- @Override
- public void run() {
- mLockIcon.setTransientFpError(false);
- mIndicationController.hideTransientIndication();
- }
- };
-
- private final Runnable mHideTransientIndicationRunnable = new Runnable() {
- @Override
- public void run() {
- mIndicationController.hideTransientIndication();
- }
- };
-
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -661,38 +644,9 @@
}
@Override
- public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
- }
-
- @Override
public void onFingerprintRunningStateChanged(boolean running) {
mLockIcon.update();
}
-
- @Override
- public void onFingerprintHelp(int msgId, String helpString) {
- if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) {
- return;
- }
- mLockIcon.setTransientFpError(true);
- mIndicationController.showTransientIndication(helpString,
- getResources().getColor(R.color.system_warning_color, null));
- removeCallbacks(mTransientFpErrorClearRunnable);
- postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT);
- }
-
- @Override
- public void onFingerprintError(int msgId, String errString) {
- if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()
- || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
- return;
- }
- // TODO: Go to bouncer if this is "too many attempts" (lockout) error.
- mIndicationController.showTransientIndication(errString,
- getResources().getColor(R.color.system_warning_color, null));
- removeCallbacks(mHideTransientIndicationRunnable);
- postDelayed(mHideTransientIndicationRunnable, 5000);
- }
};
public void setKeyguardIndicationController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index e9b2c61..37f563e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
-import android.view.Choreographer;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -103,6 +102,10 @@
mKeyguardView.showPromptReason(reason);
}
+ public void showMessage(String message, int color) {
+ mKeyguardView.showMessage(message, color);
+ }
+
private void cancelShowRunnable() {
DejankUtils.removeCallbacks(mShowRunnable);
mShowingSoon = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 367ff1a..1649acb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -262,7 +262,7 @@
mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
- mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
+ mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime_land);
mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 400f167..453b268 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -785,7 +785,8 @@
mKeyguardBottomArea.setAssistManager(mAssistManager);
mKeyguardIndicationController = new KeyguardIndicationController(mContext,
(KeyguardIndicationTextView) mStatusBarWindow.findViewById(
- R.id.keyguard_indication_text));
+ R.id.keyguard_indication_text),
+ mKeyguardBottomArea.getLockIcon());
mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
// set the inital view visibility
@@ -1031,6 +1032,8 @@
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
mStatusBarWindow, mStatusBarWindowManager, mScrimController);
+ mKeyguardIndicationController.setStatusBarKeyguardViewManager(
+ mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 44aa780..1bdcf03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -491,4 +491,8 @@
mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
}
+
+ public void showBouncerMessage(String message, int color) {
+ mBouncer.showMessage(message, color);
+ }
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 30680ed..fa87270 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2905,9 +2905,9 @@
}
// Now that we've told the host, push out an update.
sendUpdateIntentLocked(provider, appWidgetIds);
- providersUpdated = true;
}
}
+ providersUpdated = true;
}
}
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index e17ff5c..1f3d61c 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -16,21 +16,26 @@
package com.android.server;
+import android.app.ActivityManager;
import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.Handler;
import android.os.PowerManager;
-import android.os.Vibrator;
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
+import android.os.Vibrator;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Slog;
@@ -56,6 +61,8 @@
/** The wake lock held when a gesture is detected. */
private WakeLock mWakeLock;
+ private boolean mRegistered;
+ private int mUserId;
public GestureLauncherService(Context context) {
super(context);
@@ -81,9 +88,35 @@
mWakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
"GestureLauncherService");
- if (isCameraLaunchEnabled(resources)) {
- registerCameraLaunchGesture(resources);
- }
+ updateCameraRegistered();
+
+ mUserId = ActivityManager.getCurrentUser();
+ mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
+ registerContentObserver();
+ }
+ }
+
+ private void registerContentObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
+ }
+
+ private void updateCameraRegistered() {
+ Resources resources = mContext.getResources();
+ if (isCameraLaunchSettingEnabled(mContext, mUserId)) {
+ registerCameraLaunchGesture(resources);
+ } else {
+ unregisterCameraLaunchGesture();
+ }
+ }
+
+ private void unregisterCameraLaunchGesture() {
+ if (mRegistered) {
+ mRegistered = false;
+ SensorManager sensorManager = (SensorManager) mContext.getSystemService(
+ Context.SENSOR_SERVICE);
+ sensorManager.unregisterListener(mGestureListener);
}
}
@@ -91,12 +124,15 @@
* Registers for the camera launch gesture.
*/
private void registerCameraLaunchGesture(Resources resources) {
+ if (mRegistered) {
+ return;
+ }
SensorManager sensorManager = (SensorManager) mContext.getSystemService(
Context.SENSOR_SERVICE);
int cameraLaunchGestureId = resources.getInteger(
com.android.internal.R.integer.config_cameraLaunchGestureSensorType);
if (cameraLaunchGestureId != -1) {
- boolean registered = false;
+ mRegistered = false;
String sensorName = resources.getString(
com.android.internal.R.string.config_cameraLaunchGestureSensorStringType);
mCameraLaunchSensor = sensorManager.getDefaultSensor(
@@ -108,7 +144,7 @@
// makes the code more robust.
if (mCameraLaunchSensor != null) {
if (sensorName.equals(mCameraLaunchSensor.getStringType())) {
- registered = sensorManager.registerListener(mGestureListener,
+ mRegistered = sensorManager.registerListener(mGestureListener,
mCameraLaunchSensor, 0);
} else {
String message = String.format("Wrong configuration. Sensor type and sensor "
@@ -117,12 +153,18 @@
throw new RuntimeException(message);
}
}
- if (DBG) Slog.d(TAG, "Camera launch sensor registered: " + registered);
+ if (DBG) Slog.d(TAG, "Camera launch sensor registered: " + mRegistered);
} else {
if (DBG) Slog.d(TAG, "Camera launch sensor is not specified.");
}
}
+ public static boolean isCameraLaunchSettingEnabled(Context context, int userId) {
+ return isCameraLaunchEnabled(context.getResources())
+ && (Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
+ }
+
/**
* Whether to enable the camera launch gesture.
*/
@@ -142,6 +184,26 @@
return isCameraLaunchEnabled(resources);
}
+ private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+ registerContentObserver();
+ updateCameraRegistered();
+ }
+ }
+ };
+
+ private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
+ if (userId == mUserId) {
+ updateCameraRegistered();
+ }
+ }
+ };
+
private final class GestureEventListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent event) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4e11070..f9f617e 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -32,7 +32,6 @@
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.wm.WindowManagerService;
@@ -134,7 +133,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -197,10 +195,10 @@
// All known input methods. mMethodMap also serves as the global
// lock for this class.
- final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
- final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>();
+ final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
+ final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
- new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
+ new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
private final InputMethodSubtypeSwitchingController mSwitchingController;
// Used to bring IME service up to visible adjustment while it is being shown.
@@ -277,8 +275,7 @@
}
}
- final HashMap<IBinder, ClientState> mClients
- = new HashMap<IBinder, ClientState>();
+ final HashMap<IBinder, ClientState> mClients = new HashMap<>();
/**
* Set once the system is ready to run third party code.
@@ -329,8 +326,7 @@
// This list contains the pairs of InputMethodInfo and InputMethodSubtype.
private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
- mShortcutInputMethodsAndSubtypes =
- new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>();
+ mShortcutInputMethodsAndSubtypes = new HashMap<>();
// Was the keyguard locked when this client became current?
private boolean mCurClientInKeyguard;
@@ -540,7 +536,7 @@
final String imeId = entry.getKey();
ArraySet<String> prevSubtypes = prevMap.get(imeId);
if (prevSubtypes == null) {
- prevSubtypes = new ArraySet<String>(2);
+ prevSubtypes = new ArraySet<>(2);
prevMap.put(imeId, prevSubtypes);
}
prevSubtypes.addAll(entry.getValue());
@@ -559,16 +555,15 @@
static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) {
// we want to use the canonical InputMethodSettings implementation,
// so we convert data structures first.
- List<Pair<String, ArrayList<String>>> imeMap =
- new ArrayList<Pair<String, ArrayList<String>>>(4);
+ List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
final String imeName = entry.getKey();
final ArraySet<String> subtypeSet = entry.getValue();
- final ArrayList<String> subtypes = new ArrayList<String>(2);
+ final ArrayList<String> subtypes = new ArrayList<>(2);
if (subtypeSet != null) {
subtypes.addAll(subtypeSet);
}
- imeMap.add(new Pair<String, ArrayList<String>>(imeName, subtypes));
+ imeMap.add(new Pair<>(imeName, subtypes));
}
return InputMethodSettings.buildInputMethodsSettingString(imeMap);
}
@@ -576,8 +571,7 @@
// TODO: Move this method to InputMethodUtils with adding unit tests.
static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString(
final String inputMethodsAndSubtypesString) {
- final ArrayMap<String, ArraySet<String>> imeMap =
- new ArrayMap<String, ArraySet<String>>();
+ final ArrayMap<String, ArraySet<String>> imeMap = new ArrayMap<>();
if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
return imeMap;
}
@@ -591,7 +585,7 @@
typeSplitter,
subtypeSplitter);
for (Pair<String, ArrayList<String>> ime : allImeSettings) {
- ArraySet<String> subtypes = new ArraySet<String>();
+ ArraySet<String> subtypes = new ArraySet<>();
if (ime.second != null) {
subtypes.addAll(ime.second);
}
@@ -1166,7 +1160,7 @@
return Collections.emptyList();
}
synchronized (mMethodMap) {
- return new ArrayList<InputMethodInfo>(mMethodList);
+ return new ArrayList<>(mMethodList);
}
}
@@ -1190,7 +1184,7 @@
boolean allowsImplicitlySelectedSubtypes) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUser()) {
- return Collections.<InputMethodSubtype>emptyList();
+ return Collections.emptyList();
}
synchronized (mMethodMap) {
final InputMethodInfo imi;
@@ -1200,7 +1194,7 @@
imi = mMethodMap.get(imiId);
}
if (imi == null) {
- return Collections.<InputMethodSubtype>emptyList();
+ return Collections.emptyList();
}
return mSettings.getEnabledInputMethodSubtypeListLocked(
mContext, imi, allowsImplicitlySelectedSubtypes);
@@ -2898,8 +2892,6 @@
// Use for queryIntentServicesAsUser
final PackageManager pm = mContext.getPackageManager();
- String disabledSysImes = mSettings.getDisabledSystemInputMethods();
- if (disabledSysImes == null) disabledSysImes = "";
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
@@ -2931,10 +2923,7 @@
if (DEBUG) {
Slog.d(TAG, "Found an input method " + p);
}
-
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "Unable to load input method " + compName, e);
- } catch (IOException e) {
+ } catch (XmlPullParserException | IOException e) {
Slog.w(TAG, "Unable to load input method " + compName, e);
}
}
@@ -3408,8 +3397,7 @@
}
}
if (mostApplicableIMI != null) {
- return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
- mostApplicableSubtype);
+ return new Pair<> (mostApplicableIMI, mostApplicableSubtype);
} else {
return null;
}
@@ -3468,23 +3456,12 @@
return mCurrentSubtype;
}
- private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi,
- InputMethodSubtype subtype) {
- if (mShortcutInputMethodsAndSubtypes.containsKey(imi)) {
- mShortcutInputMethodsAndSubtypes.get(imi).add(subtype);
- } else {
- ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
- subtypes.add(subtype);
- mShortcutInputMethodsAndSubtypes.put(imi, subtypes);
- }
- }
-
// TODO: We should change the return type from List to List<Parcelable>
@SuppressWarnings("rawtypes")
@Override
public List getShortcutInputMethodsAndSubtypes() {
synchronized (mMethodMap) {
- ArrayList<Object> ret = new ArrayList<Object>();
+ ArrayList<Object> ret = new ArrayList<>();
if (mShortcutInputMethodsAndSubtypes.size() == 0) {
// If there are no selected shortcut subtypes, the framework will try to find
// the most applicable subtype from all subtypes whose mode is
@@ -3545,7 +3522,7 @@
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
- new HashMap<String, List<InputMethodSubtype>>();
+ new HashMap<>();
public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
if (methodMap == null) {
throw new NullPointerException("methodMap is null");
@@ -3581,7 +3558,7 @@
public void addInputMethodSubtypes(
InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
synchronized (mMethodMap) {
- final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
final int N = additionalSubtypes.length;
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = additionalSubtypes[i];
@@ -3655,9 +3632,7 @@
HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
if (allSubtypes == null || subtypesFile == null) return;
allSubtypes.clear();
- FileInputStream fis = null;
- try {
- fis = subtypesFile.openRead();
+ try (final FileInputStream fis = subtypesFile.openRead()) {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, StandardCharsets.UTF_8.name());
int type = parser.getEventType();
@@ -3682,7 +3657,7 @@
Slog.w(TAG, "Invalid imi id found in subtypes.xml");
continue;
}
- tempSubtypesArray = new ArrayList<InputMethodSubtype>();
+ tempSubtypesArray = new ArrayList<>();
allSubtypes.put(currentImiId, tempSubtypesArray);
} else if (NODE_SUBTYPE.equals(nodeName)) {
if (TextUtils.isEmpty(currentImiId) || tempSubtypesArray == null) {
@@ -3712,23 +3687,9 @@
tempSubtypesArray.add(subtype);
}
}
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "Error reading subtypes: " + e);
+ } catch (XmlPullParserException | IOException | NumberFormatException e) {
+ Slog.w(TAG, "Error reading subtypes", e);
return;
- } catch (java.io.IOException e) {
- Slog.w(TAG, "Error reading subtypes: " + e);
- return;
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Error reading subtypes: " + e);
- return;
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (java.io.IOException e1) {
- Slog.w(TAG, "Failed to close.");
- }
- }
}
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index aab6374..8b0e6f2 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -283,7 +283,22 @@
// Don't delete accounts when updating a authenticator's
// package.
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- purgeOldGrantsAll();
+ /* Purging data requires file io, don't block the main thread. This is probably
+ * less than ideal because we are introducing a race condition where old grants
+ * could be exercised until they are purged. But that race condition existed
+ * anyway with the broadcast receiver.
+ *
+ * Ideally, we would completely clear the cache, purge data from the database,
+ * and then rebuild the cache. All under the cache lock. But that change is too
+ * large at this point.
+ */
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ purgeOldGrantsAll();
+ }
+ };
+ new Thread(r).start();
}
}
}, intentFilter);
@@ -329,52 +344,6 @@
return mUserManager;
}
- /* Caller should lock mUsers */
- private UserAccounts initUserLocked(int userId) {
- UserAccounts accounts = mUsers.get(userId);
- if (accounts == null) {
- accounts = new UserAccounts(mContext, userId);
- initializeDebugDbSizeAndCompileSqlStatementForLogging(
- accounts.openHelper.getWritableDatabase(), accounts);
- mUsers.append(userId, accounts);
- purgeOldGrants(accounts);
- validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
- }
- return accounts;
- }
-
- private void purgeOldGrantsAll() {
- synchronized (mUsers) {
- for (int i = 0; i < mUsers.size(); i++) {
- purgeOldGrants(mUsers.valueAt(i));
- }
- }
- }
-
- private void purgeOldGrants(UserAccounts accounts) {
- synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
- final Cursor cursor = db.query(TABLE_GRANTS,
- new String[]{GRANTS_GRANTEE_UID},
- null, null, GRANTS_GRANTEE_UID, null, null);
- try {
- while (cursor.moveToNext()) {
- final int uid = cursor.getInt(0);
- final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
- if (packageExists) {
- continue;
- }
- Log.d(TAG, "deleting grants for UID " + uid
- + " because its package is no longer installed");
- db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
- new String[]{Integer.toString(uid)});
- }
- } finally {
- cursor.close();
- }
- }
- }
-
/**
* Validate internal set of accounts against installed authenticators for
* given user. Clears cached authenticators before validating.
@@ -469,13 +438,49 @@
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
if (accounts == null) {
- accounts = initUserLocked(userId);
+ accounts = new UserAccounts(mContext, userId);
+ initializeDebugDbSizeAndCompileSqlStatementForLogging(
+ accounts.openHelper.getWritableDatabase(), accounts);
mUsers.append(userId, accounts);
+ purgeOldGrants(accounts);
+ validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
}
return accounts;
}
}
+ private void purgeOldGrantsAll() {
+ synchronized (mUsers) {
+ for (int i = 0; i < mUsers.size(); i++) {
+ purgeOldGrants(mUsers.valueAt(i));
+ }
+ }
+ }
+
+ private void purgeOldGrants(UserAccounts accounts) {
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final Cursor cursor = db.query(TABLE_GRANTS,
+ new String[]{GRANTS_GRANTEE_UID},
+ null, null, GRANTS_GRANTEE_UID, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ final int uid = cursor.getInt(0);
+ final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
+ if (packageExists) {
+ continue;
+ }
+ Log.d(TAG, "deleting grants for UID " + uid
+ + " because its package is no longer installed");
+ db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
+ new String[]{Integer.toString(uid)});
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
private void onUserRemoved(Intent intent) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userId < 1) return;
@@ -2510,8 +2515,9 @@
}
long identityToken = clearCallingIdentity();
try {
+ UserAccounts accounts = getUserAccounts(userId);
return getAccountsInternal(
- userId,
+ accounts,
callingUid,
null, // packageName
visibleAccountTypes);
@@ -2609,8 +2615,9 @@
long identityToken = clearCallingIdentity();
try {
+ UserAccounts accounts = getUserAccounts(userId);
return getAccountsInternal(
- userId,
+ accounts,
callingUid,
callingPackage,
visibleAccountTypes);
@@ -2620,13 +2627,11 @@
}
private Account[] getAccountsInternal(
- int userId,
+ UserAccounts userAccounts,
int callingUid,
String callingPackage,
List<String> visibleAccountTypes) {
- UserAccounts accounts = getUserAccounts(userId);
- synchronized (accounts.cacheLock) {
- UserAccounts userAccounts = getUserAccounts(userId);
+ synchronized (userAccounts.cacheLock) {
ArrayList<Account> visibleAccounts = new ArrayList<>();
for (String visibleType : visibleAccountTypes) {
Account[] accountsForType = getAccountsFromCacheLocked(
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b997926..0c6c067 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3800,7 +3800,12 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
boolean prevIsHome = false;
- if (tr.isOverHomeStack()) {
+
+ // If true, we should resume the home activity next if the task we are moving to the
+ // back is over the home stack. We force to false if the task we are moving to back
+ // is the home task and we don't want it resumed after moving to the back.
+ final boolean canGoHome = !tr.isHomeTask() && tr.isOverHomeStack();
+ if (canGoHome) {
final TaskRecord nextTask = getNextTask(tr);
if (nextTask != null) {
nextTask.setTaskToReturnTo(tr.getTaskToReturnTo());
@@ -3834,8 +3839,7 @@
}
final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
- if (prevIsHome || task == tr && tr.isOverHomeStack()
- || numTasks <= 1 && isOnHomeDisplay()) {
+ if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) {
if (!mService.mBooting && !mService.mBooted) {
// Not ready yet!
return false;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index c705fbf..2c9d82b 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -454,6 +454,18 @@
"Must have " + permission + " permission.");
}
+ int getEffectiveUserId(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um != null) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ userId = um.getCredentialOwnerProfile(userId);
+ Binder.restoreCallingIdentity(callingIdentity);
+ } else {
+ Slog.e(TAG, "Unable to acquire UserManager");
+ }
+ return userId;
+ }
+
boolean isCurrentUserOrProfile(int userId) {
UserManager um = UserManager.get(mContext);
@@ -686,11 +698,15 @@
}
final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default fingerprints for the user.
+ final int effectiveGroupId = getEffectiveUserId(groupId);
+
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted);
+ startEnrollment(token, cryptoClone, effectiveGroupId, receiver, flags, restricted);
}
});
}
@@ -724,11 +740,16 @@
Slog.w(TAG, "Calling not granted permission to use fingerprint");
return;
}
+
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default fingerprints for the user.
+ final int effectiveGroupId = getEffectiveUserId(groupId);
+
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
- startAuthentication(token, opId, groupId, receiver, flags, restricted);
+ startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted);
}
});
}
@@ -751,10 +772,14 @@
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
final boolean restricted = isRestricted();
+
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default fingerprints for the user.
+ final int effectiveGroupId = getEffectiveUserId(groupId);
mHandler.post(new Runnable() {
@Override
public void run() {
- startRemove(token, fingerId, groupId, receiver, restricted);
+ startRemove(token, fingerId, effectiveGroupId, receiver, restricted);
}
});
@@ -771,10 +796,15 @@
@Override // Binder call
public void rename(final int fingerId, final int groupId, final String name) {
checkPermission(MANAGE_FINGERPRINT);
+
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default fingerprints for the user.
+ final int effectiveGroupId = getEffectiveUserId(groupId);
mHandler.post(new Runnable() {
@Override
public void run() {
- mFingerprintUtils.renameFingerprintForUser(mContext, fingerId, groupId, name);
+ mFingerprintUtils.renameFingerprintForUser(mContext, fingerId,
+ effectiveGroupId, name);
}
});
}
@@ -784,15 +814,19 @@
if (!canUseFingerprint(opPackageName)) {
return Collections.emptyList();
}
- return FingerprintService.this.getEnrolledFingerprints(userId);
+ int effectiveUserId = getEffectiveUserId(userId);
+
+ return FingerprintService.this.getEnrolledFingerprints(effectiveUserId);
}
@Override // Binder call
- public boolean hasEnrolledFingerprints(int groupId, String opPackageName) {
+ public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
if (!canUseFingerprint(opPackageName)) {
return false;
}
- return FingerprintService.this.hasEnrolledFingerprints(groupId);
+
+ int effectiveUserId = getEffectiveUserId(userId);
+ return FingerprintService.this.hasEnrolledFingerprints(effectiveUserId);
}
@Override // Binder call
@@ -829,8 +863,7 @@
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon != null) {
try {
- // TODO: if this is a managed profile, use the profile parent's directory for
- // storage.
+ userId = getEffectiveUserId(userId);
final File systemDir = Environment.getUserSystemDirectory(userId);
final File fpDir = new File(systemDir, FP_DATA_DIR);
if (!fpDir.exists()) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fcf743e..2b26b42 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -208,7 +208,9 @@
}
void moveStack(TaskStack stack, boolean toTop) {
- mStacks.remove(stack);
+ if (!mStacks.remove(stack)) {
+ Slog.wtf(TAG, "moving stack that was not added: " + stack, new Throwable());
+ }
mStacks.add(toTop ? mStacks.size() : 0, stack);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 90d2593..1c65c27 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -318,8 +318,6 @@
}
close();
-
- mDisplayContent = null;
}
void resetAnimationBackgroundAnimator() {
@@ -358,6 +356,7 @@
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
mTasks.get(taskNdx).close();
}
+ mDisplayContent = null;
}
public void dump(String prefix, PrintWriter pw) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6e6c71f..eb64a9d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1022,12 +1022,6 @@
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
- // The system context's theme may be configuration-dependent.
- final Theme systemTheme = context.getTheme();
- if (systemTheme.getChangingConfigurations() != 0) {
- systemTheme.rebase();
- }
-
try {
// TODO: use boot phase
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 2d40291..9ee9cf4 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -603,7 +603,10 @@
@Override
public void exit() {
cancelOneshotTimeout();
- mReceiveThread.halt(); // Also closes sockets.
+ if (mReceiveThread != null) {
+ mReceiveThread.halt(); // Also closes sockets.
+ mReceiveThread = null;
+ }
clearDhcpState();
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 303a492..80515cf 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -220,6 +220,8 @@
private int mCdmaEriIconIndex;
private int mCdmaEriIconMode;
+ private boolean mIsDataRoamingFromRegistration;
+
/**
* get String description of roaming type
* @hide
@@ -297,6 +299,7 @@
mCdmaEriIconIndex = s.mCdmaEriIconIndex;
mCdmaEriIconMode = s.mCdmaEriIconMode;
mIsEmergencyOnly = s.mIsEmergencyOnly;
+ mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
}
/**
@@ -324,6 +327,7 @@
mCdmaEriIconIndex = in.readInt();
mCdmaEriIconMode = in.readInt();
mIsEmergencyOnly = in.readInt() != 0;
+ mIsDataRoamingFromRegistration = in.readInt() != 0;
}
public void writeToParcel(Parcel out, int flags) {
@@ -348,6 +352,7 @@
out.writeInt(mCdmaEriIconIndex);
out.writeInt(mCdmaEriIconMode);
out.writeInt(mIsEmergencyOnly ? 1 : 0);
+ out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
}
public int describeContents() {
@@ -439,6 +444,26 @@
}
/**
+ * Set whether data network registration state is roaming
+ *
+ * This should only be set to the roaming value received
+ * once the data registration phase has completed.
+ * @hide
+ */
+ public void setDataRoamingFromRegistration(boolean dataRoaming) {
+ mIsDataRoamingFromRegistration = dataRoaming;
+ }
+
+ /**
+ * Get whether data network registration state is roaming
+ * @return true if registration indicates roaming, false otherwise
+ * @hide
+ */
+ public boolean getDataRoamingFromRegistration() {
+ return mIsDataRoamingFromRegistration;
+ }
+
+ /**
* Get current data network roaming type
* @return roaming type
* @hide
@@ -599,7 +624,8 @@
+ ((null == mDataOperatorNumeric) ? 0 : mDataOperatorNumeric.hashCode())
+ mCdmaRoamingIndicator
+ mCdmaDefaultRoamingIndicator
- + (mIsEmergencyOnly ? 1 : 0));
+ + (mIsEmergencyOnly ? 1 : 0)
+ + (mIsDataRoamingFromRegistration ? 1 : 0));
}
@Override
@@ -635,7 +661,8 @@
&& equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
&& equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
s.mCdmaDefaultRoamingIndicator)
- && mIsEmergencyOnly == s.mIsEmergencyOnly);
+ && mIsEmergencyOnly == s.mIsEmergencyOnly
+ && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration);
}
/**
@@ -736,7 +763,8 @@
+ " " + mSystemId
+ " RoamInd=" + mCdmaRoamingIndicator
+ " DefRoamInd=" + mCdmaDefaultRoamingIndicator
- + " EmergOnly=" + mIsEmergencyOnly);
+ + " EmergOnly=" + mIsEmergencyOnly
+ + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration);
}
private void setNullState(int state) {
@@ -762,6 +790,7 @@
mCdmaEriIconIndex = -1;
mCdmaEriIconMode = -1;
mIsEmergencyOnly = false;
+ mIsDataRoamingFromRegistration = false;
}
public void setStateOutOfService() {
@@ -934,6 +963,7 @@
mCdmaRoamingIndicator = m.getInt("cdmaRoamingIndicator");
mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator");
mIsEmergencyOnly = m.getBoolean("emergencyOnly");
+ mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
}
/**
@@ -962,6 +992,7 @@
m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator);
m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator);
m.putBoolean("emergencyOnly", Boolean.valueOf(mIsEmergencyOnly));
+ m.putBoolean("isDataRoamingFromRegistration", Boolean.valueOf(mIsDataRoamingFromRegistration));
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 88612e9..e104b38 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -602,6 +602,46 @@
public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
/**
+ * Broadcast intent action for letting custom component know to show voicemail notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION =
+ "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+
+ /**
+ * The number of voice messages associated with the notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NOTIFICATION_COUNT =
+ "android.telephony.extra.NOTIFICATION_COUNT";
+
+ /**
+ * The voicemail number.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_VOICEMAIL_NUMBER =
+ "android.telephony.extra.VOICEMAIL_NUMBER";
+
+ /**
+ * The intent to call voicemail.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_CALL_VOICEMAIL_INTENT =
+ "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+
+ /**
+ * The intent to launch voicemail settings.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT =
+ "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+
+ /**
* Response codes for sim activation. Activation completed successfully.
* @hide
*/
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 47258b6..baf2e2e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -97,13 +97,13 @@
* @return the delegate or null if not found.
*/
@Nullable
- public T getDelegate(long native_object) {
+ public synchronized T getDelegate(long native_object) {
if (native_object > 0) {
- T delegate = mDelegates.get(native_object);
+ T delegate = mDelegates.get(native_object);
if (Debug.DEBUG) {
if (delegate == null) {
- System.out.println("Unknown " + mClass.getSimpleName() + " with int " +
+ System.err.println("Unknown " + mClass.getSimpleName() + " with int " +
native_object);
}
}
@@ -119,14 +119,18 @@
* @param newDelegate the delegate to add
* @return a unique native int to identify the delegate
*/
- public long addNewDelegate(T newDelegate) {
+ public synchronized long addNewDelegate(T newDelegate) {
long native_object = ++mDelegateCounter;
+
mDelegates.put(native_object, newDelegate);
assert !mJavaReferences.contains(newDelegate);
mJavaReferences.add(newDelegate);
if (Debug.DEBUG) {
- System.out.println("New " + mClass.getSimpleName() + " with int " + native_object);
+ System.out.println(
+ "New " + mClass.getSimpleName() + " " +
+ "with int " +
+ native_object);
}
return native_object;
@@ -136,7 +140,7 @@
* Removes the main reference on the given delegate.
* @param native_object the native integer representing the delegate.
*/
- public void removeJavaReferenceFor(long native_object) {
+ public synchronized void removeJavaReferenceFor(long native_object) {
T delegate = getDelegate(native_object);
if (Debug.DEBUG) {