summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java6
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java4
-rw-r--r--core/java/android/content/Intent.java3
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java2
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java21
-rw-r--r--core/jni/android/graphics/Canvas.cpp8
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp25
-rw-r--r--graphics/java/android/graphics/Canvas.java7
-rw-r--r--media/java/android/media/MediaRouter.java193
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java8
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java15
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java34
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java29
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java5
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java15
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java42
-rw-r--r--services/java/com/android/server/BluetoothManagerService.java12
-rw-r--r--services/java/com/android/server/NotificationManagerService.java8
-rw-r--r--services/java/com/android/server/accounts/AccountManagerService.java5
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java2
22 files changed, 346 insertions, 107 deletions
diff --git a/api/current.txt b/api/current.txt
index c1caaf2f157c..1fb398e0e4be 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12202,6 +12202,7 @@ package android.media {
public class MediaRouter {
method public void addCallback(int, android.media.MediaRouter.Callback);
+ method public void addCallback(int, android.media.MediaRouter.Callback, int);
method public void addUserRoute(android.media.MediaRouter.UserRouteInfo);
method public void clearUserRoutes();
method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
@@ -12216,6 +12217,8 @@ package android.media {
method public void removeCallback(android.media.MediaRouter.Callback);
method public void removeUserRoute(android.media.MediaRouter.UserRouteInfo);
method public void selectRoute(int, android.media.MediaRouter.RouteInfo);
+ field public static final int CALLBACK_FLAG_ACTIVE_SCAN = 1; // 0x1
+ field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
field public static final int ROUTE_TYPE_LIVE_AUDIO = 1; // 0x1
field public static final int ROUTE_TYPE_LIVE_VIDEO = 2; // 0x2
field public static final int ROUTE_TYPE_USER = 8388608; // 0x800000
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index cfbfb48d4a1f..7ec73ef74135 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1472,9 +1472,13 @@ public final class BluetoothAdapter {
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+ if (iGatt == null) {
+ // BLE is not supported
+ return false;
+ }
+
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
-
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
if (wrapper.scanStarted()) {
mLeScanClients.put(callback, wrapper);
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 3c1ec90f19c3..79a5ffea46ab 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1187,6 +1187,10 @@ public final class BluetoothDevice implements Parcelable {
IBluetoothManager managerService = adapter.getBluetoothManager();
try {
IBluetoothGatt iGatt = managerService.getBluetoothGatt();
+ if (iGatt == null) {
+ // BLE is not supported
+ return null;
+ }
BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this);
gatt.connect(autoConnect, callback);
return gatt;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bfc7bf5dff85..65f904ff4f3d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2567,8 +2567,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent to the system when a user's information changes. Carries an extra
* {@link #EXTRA_USER_HANDLE} to indicate which user's information changed.
- * This is only sent to registered receivers, not manifest receivers. It is sent to the user
- * whose information has changed.
+ * This is only sent to registered receivers, not manifest receivers. It is sent to all users.
* @hide
*/
public static final String ACTION_USER_INFO_CHANGED =
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 528eadd812fe..71a85bc7a2f5 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -838,7 +838,7 @@ public class ViewPropertyAnimator {
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
mView.removeCallbacks(mAnimationStarter);
- mView.post(mAnimationStarter);
+ mView.postOnAnimation(mAnimationStarter);
}
/**
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
index 2bc80ff717a3..cf797bbf9860 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -70,7 +70,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
};
MediaRouter mRouter;
- DisplayManager mDisplayService;
private int mRouteTypes;
private LayoutInflater mInflater;
@@ -98,7 +97,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mDisplayService = (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE);
+ mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_ACTIVE_SCAN);
}
@Override
@@ -121,15 +120,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
public void setRouteTypes(int types) {
mRouteTypes = types;
- if ((mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0 && mDisplayService == null) {
- final Context activity = getActivity();
- if (activity != null) {
- mDisplayService = (DisplayManager) activity.getSystemService(
- Context.DISPLAY_SERVICE);
- }
- } else {
- mDisplayService = null;
- }
}
void updateVolume() {
@@ -192,7 +182,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
list.setOnItemClickListener(mAdapter);
mListView = list;
- mRouter.addCallback(mRouteTypes, mCallback);
mAdapter.scrollToSelectedItem();
@@ -204,14 +193,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
return new RouteChooserDialog(getActivity(), getTheme());
}
- @Override
- public void onResume() {
- super.onResume();
- if (mDisplayService != null) {
- mDisplayService.scanWifiDisplays();
- }
- }
-
private static class ViewHolder {
public TextView text1;
public TextView text2;
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 11c70530e96a..eb97a9cd599b 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -964,16 +964,10 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
jobject bounds) {
SkRect r;
SkIRect ir;
- bool result = canvas->getClipBounds(&r);
+ bool result = canvas->getClipBounds(&r);
if (!result) {
r.setEmpty();
- } else {
- // ensure the clip is not larger than the canvas
- SkRect canvasRect;
- SkISize deviceSize = canvas->getDeviceSize();
- canvasRect.iset(0, 0, deviceSize.fWidth, deviceSize.fHeight);
- r.intersect(canvasRect);
}
r.round(&ir);
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index bcc1573f5034..b2cf9c1674f1 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -823,27 +823,26 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
}
+ SkTypeface* scriptTypeface = NULL;
if (baseGlyphCount != 0) {
- SkTypeface::Style style = SkTypeface::kNormal;
- if (typeface != NULL) {
- style = typeface->style();
- }
- typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
- if (!typeface) {
- baseGlyphCount = 0;
- typeface = SkFontHost::CreateTypeface(NULL, NULL, style);
+ scriptTypeface = typefaceForScript(paint, typeface,
+ hb_buffer_get_script(mBuffer));
#if DEBUG_GLYPHS
- ALOGD("Using Default Typeface");
+ ALOGD("Using Default Typeface for script %c%c%c%c",
+ HB_UNTAG(hb_buffer_get_script(mBuffer)));
#endif
- }
+ }
+ if (scriptTypeface) {
+ typeface = scriptTypeface;
} else {
- if (!typeface) {
+ baseGlyphCount = 0;
+ if (typeface) {
+ SkSafeRef(typeface);
+ } else {
typeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
#if DEBUG_GLYPHS
ALOGD("Using Default Typeface (normal style)");
#endif
- } else {
- SkSafeRef(typeface);
}
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 79bf54b387c0..c851844bc2d3 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -781,7 +781,10 @@ public class Canvas {
}
/**
- * Retrieve the clip bounds, returning true if they are non-empty.
+ * Return the bounds of the current clip (in local coordinates) in the
+ * bounds parameter, and return true if it is non-empty. This can be useful
+ * in a way similar to quickReject, in that it tells you that drawing
+ * outside of these bounds will be clipped out.
*
* @param bounds Return the clip bounds here. If it is null, ignore it but
* still return true if the current clip is non-empty.
@@ -792,7 +795,7 @@ public class Canvas {
}
/**
- * Retrieve the clip bounds.
+ * Retrieve the bounds of the current clip (in local coordinates).
*
* @return the clip bounds, or [0, 0, 0, 0] if the clip is empty.
*/
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 61c55a58e5ef..9168d4633b2f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -53,6 +53,9 @@ public class MediaRouter {
private static final String TAG = "MediaRouter";
static class Static implements DisplayManager.DisplayListener {
+ // Time between wifi display scans when actively scanning in milliseconds.
+ private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
+
final Resources mResources;
final IAudioService mAudioService;
final DisplayManager mDisplayService;
@@ -73,8 +76,10 @@ public class MediaRouter {
RouteInfo mSelectedRoute;
WifiDisplayStatus mLastKnownWifiDisplayStatus;
+ boolean mActivelyScanningWifiDisplays;
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
+ @Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
mHandler.post(new Runnable() {
@Override public void run() {
@@ -84,6 +89,16 @@ public class MediaRouter {
}
};
+ final Runnable mScanWifiDisplays = new Runnable() {
+ @Override
+ public void run() {
+ if (mActivelyScanningWifiDisplays) {
+ mDisplayService.scanWifiDisplays();
+ mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
+ }
+ }
+ };
+
Static(Context appContext) {
mResources = Resources.getSystem();
mHandler = new Handler(appContext.getMainLooper());
@@ -195,6 +210,32 @@ public class MediaRouter {
}
}
+ void updateActiveScan() {
+ if (hasActiveScanCallbackOfType(ROUTE_TYPE_LIVE_VIDEO)) {
+ if (!mActivelyScanningWifiDisplays) {
+ mActivelyScanningWifiDisplays = true;
+ mHandler.post(mScanWifiDisplays);
+ }
+ } else {
+ if (mActivelyScanningWifiDisplays) {
+ mActivelyScanningWifiDisplays = false;
+ mHandler.removeCallbacks(mScanWifiDisplays);
+ }
+ }
+ }
+
+ private boolean hasActiveScanCallbackOfType(int type) {
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ CallbackInfo cbi = mCallbacks.get(i);
+ if ((cbi.flags & CALLBACK_FLAG_ACTIVE_SCAN) != 0
+ && (cbi.type & type) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void onDisplayAdded(int displayId) {
updatePresentationDisplays(displayId);
@@ -270,6 +311,33 @@ public class MediaRouter {
*/
public static final int ROUTE_TYPE_USER = 0x00800000;
+ /**
+ * Flag for {@link #addCallback}: Actively scan for routes while this callback
+ * is registered.
+ * <p>
+ * When this flag is specified, the media router will actively scan for new
+ * routes. Certain routes, such as wifi display routes, may not be discoverable
+ * except when actively scanning. This flag is typically used when the route picker
+ * dialog has been opened by the user to ensure that the route information is
+ * up to date.
+ * </p><p>
+ * Active scanning may consume a significant amount of power and may have intrusive
+ * effects on wireless connectivity. Therefore it is important that active scanning
+ * only be requested when it is actually needed to satisfy a user request to
+ * discover and select a new route.
+ * </p>
+ */
+ public static final int CALLBACK_FLAG_ACTIVE_SCAN = 1 << 0;
+
+ /**
+ * Flag for {@link #addCallback}: Do not filter route events.
+ * <p>
+ * When this flag is specified, the callback will be invoked for event that affect any
+ * route event if they do not match the callback's associated media route selector.
+ * </p>
+ */
+ public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
+
// Maps application contexts
static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
@@ -343,20 +411,48 @@ public class MediaRouter {
* Add a callback to listen to events about specific kinds of media routes.
* If the specified callback is already registered, its registration will be updated for any
* additional route types specified.
+ * <p>
+ * This is a convenience method that has the same effect as calling
+ * {@link #addCallback(int, Callback, int)} without flags.
+ * </p>
*
* @param types Types of routes this callback is interested in
* @param cb Callback to add
*/
public void addCallback(int types, Callback cb) {
- final int count = sStatic.mCallbacks.size();
- for (int i = 0; i < count; i++) {
- final CallbackInfo info = sStatic.mCallbacks.get(i);
- if (info.cb == cb) {
- info.type |= types;
- return;
- }
+ addCallback(types, cb, 0);
+ }
+
+ /**
+ * Add a callback to listen to events about specific kinds of media routes.
+ * If the specified callback is already registered, its registration will be updated for any
+ * additional route types specified.
+ * <p>
+ * By default, the callback will only be invoked for events that affect routes
+ * that match the specified selector. The filtering may be disabled by specifying
+ * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
+ * </p>
+ *
+ * @param types Types of routes this callback is interested in
+ * @param cb Callback to add
+ * @param flags Flags to control the behavior of the callback.
+ * May be zero or a combination of {@link #CALLBACK_FLAG_ACTIVE_SCAN} and
+ * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
+ */
+ public void addCallback(int types, Callback cb, int flags) {
+ CallbackInfo info;
+ int index = findCallbackInfo(cb);
+ if (index >= 0) {
+ info = sStatic.mCallbacks.get(index);
+ info.type |= types;
+ info.flags |= flags;
+ } else {
+ info = new CallbackInfo(cb, types, flags, this);
+ sStatic.mCallbacks.add(info);
+ }
+ if ((info.flags & CALLBACK_FLAG_ACTIVE_SCAN) != 0) {
+ sStatic.updateActiveScan();
}
- sStatic.mCallbacks.add(new CallbackInfo(cb, types, this));
}
/**
@@ -365,14 +461,26 @@ public class MediaRouter {
* @param cb Callback to remove
*/
public void removeCallback(Callback cb) {
+ int index = findCallbackInfo(cb);
+ if (index >= 0) {
+ CallbackInfo info = sStatic.mCallbacks.remove(index);
+ if ((info.flags & CALLBACK_FLAG_ACTIVE_SCAN) != 0) {
+ sStatic.updateActiveScan();
+ }
+ } else {
+ Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
+ }
+ }
+
+ private int findCallbackInfo(Callback cb) {
final int count = sStatic.mCallbacks.size();
for (int i = 0; i < count; i++) {
- if (sStatic.mCallbacks.get(i).cb == cb) {
- sStatic.mCallbacks.remove(i);
- return;
+ final CallbackInfo info = sStatic.mCallbacks.get(i);
+ if (info.cb == cb) {
+ return i;
}
}
- Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
+ return -1;
}
/**
@@ -431,12 +539,10 @@ public class MediaRouter {
}
if (oldRoute != null) {
- // TODO filter types properly
dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
}
sStatic.mSelectedRoute = route;
if (route != null) {
- // TODO filter types properly
dispatchRouteSelected(types & route.getSupportedTypes(), route);
}
}
@@ -671,7 +777,7 @@ public class MediaRouter {
static void dispatchRouteSelected(int type, RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & type) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRouteSelected(cbi.router, type, info);
}
}
@@ -679,7 +785,7 @@ public class MediaRouter {
static void dispatchRouteUnselected(int type, RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & type) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRouteUnselected(cbi.router, type, info);
}
}
@@ -687,7 +793,7 @@ public class MediaRouter {
static void dispatchRouteChanged(RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & info.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRouteChanged(cbi.router, info);
}
}
@@ -695,7 +801,7 @@ public class MediaRouter {
static void dispatchRouteAdded(RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & info.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRouteAdded(cbi.router, info);
}
}
@@ -703,7 +809,7 @@ public class MediaRouter {
static void dispatchRouteRemoved(RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & info.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRouteRemoved(cbi.router, info);
}
}
@@ -711,7 +817,7 @@ public class MediaRouter {
static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & group.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(group)) {
cbi.cb.onRouteGrouped(cbi.router, info, group, index);
}
}
@@ -719,7 +825,7 @@ public class MediaRouter {
static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & group.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(group)) {
cbi.cb.onRouteUngrouped(cbi.router, info, group);
}
}
@@ -727,7 +833,7 @@ public class MediaRouter {
static void dispatchRouteVolumeChanged(RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & info.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRouteVolumeChanged(cbi.router, info);
}
}
@@ -735,7 +841,7 @@ public class MediaRouter {
static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
for (CallbackInfo cbi : sStatic.mCallbacks) {
- if ((cbi.type & info.mSupportedTypes) != 0) {
+ if (cbi.filterRouteEvent(info)) {
cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
}
}
@@ -783,25 +889,21 @@ public class MediaRouter {
for (int i = 0; i < newDisplays.length; i++) {
final WifiDisplay d = newDisplays[i];
- final WifiDisplay oldRemembered = findMatchingDisplay(d, oldDisplays);
- if (oldRemembered == null) {
- addRouteStatic(makeWifiDisplayRoute(d,
- findMatchingDisplay(d, availableDisplays) != null));
+ final boolean available = findMatchingDisplay(d, availableDisplays) != null;
+ RouteInfo route = findWifiDisplayRoute(d);
+ if (route == null) {
+ route = makeWifiDisplayRoute(d, available);
+ addRouteStatic(route);
wantScan = true;
} else {
- final boolean available = findMatchingDisplay(d, availableDisplays) != null;
- final RouteInfo route = findWifiDisplayRoute(d);
updateWifiDisplayRoute(route, d, available, newStatus);
}
if (d.equals(activeDisplay)) {
- final RouteInfo activeRoute = findWifiDisplayRoute(d);
- if (activeRoute != null) {
- selectRouteStatic(activeRoute.getSupportedTypes(), activeRoute);
+ selectRouteStatic(route.getSupportedTypes(), route);
- // Don't scan if we're already connected to a wifi display,
- // the scanning process can cause a hiccup with some configurations.
- blockScan = true;
- }
+ // Don't scan if we're already connected to a wifi display,
+ // the scanning process can cause a hiccup with some configurations.
+ blockScan = true;
}
}
for (int i = 0; i < oldDisplays.length; i++) {
@@ -1896,24 +1998,33 @@ public class MediaRouter {
static class CallbackInfo {
public int type;
+ public int flags;
public final Callback cb;
public final MediaRouter router;
- public CallbackInfo(Callback cb, int type, MediaRouter router) {
+ public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
this.cb = cb;
this.type = type;
+ this.flags = flags;
this.router = router;
}
+
+ public boolean filterRouteEvent(RouteInfo route) {
+ return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
+ || (type & route.mSupportedTypes) != 0;
+ }
}
/**
* Interface for receiving events about media routing changes.
* All methods of this interface will be called from the application's main thread.
+ * <p>
+ * A Callback will only receive events relevant to routes that the callback
+ * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
+ * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
+ * </p>
*
- * <p>A Callback will only receive events relevant to routes that the callback
- * was registered for.</p>
- *
- * @see MediaRouter#addCallback(int, Callback)
+ * @see MediaRouter#addCallback(int, Callback, int)
* @see MediaRouter#removeCallback(Callback)
*/
public static abstract class Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index d826282288a5..df1eb67e1b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -819,8 +819,10 @@ class QuickSettings {
if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
try {
- final int userId = ActivityManagerNative.getDefault().getCurrentUser().id;
- if (getSendingUserId() == userId) {
+ final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+ final int changedUser =
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+ if (changedUser == currentUser) {
reloadUserInfo();
}
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index f526f0c6f988..5620e1b35ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -24,6 +24,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewRootImpl;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextSwitcher;
@@ -63,6 +64,13 @@ public class StatusBarWindowView extends FrameLayout
mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight);
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollView(mScrollView);
+
+ // We really need to be able to animate while window animations are going on
+ // so that activities may be started asynchronously from panel animations
+ final ViewRootImpl root = getViewRootImpl();
+ if (root != null) {
+ root.setDrawDuringWindowsAnimating(true);
+ }
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
index 79b66f4818bb..fe32099965e9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
@@ -100,6 +100,11 @@ class KeyguardCircleFramedDrawable extends Drawable {
mFramePath = new Path();
}
+ public void reset() {
+ mScale = 1f;
+ mPressed = false;
+ }
+
@Override
public void draw(Canvas canvas) {
// clear background
@@ -157,4 +162,14 @@ class KeyguardCircleFramedDrawable extends Drawable {
@Override
public void setColorFilter(ColorFilter cf) {
}
+
+ public boolean verifyParams(float iconSize, int frameColor, float stroke,
+ int frameShadowColor, float shadowRadius, int highlightColor) {
+ return mSize == iconSize
+ && mFrameColor == frameColor
+ && mStrokeWidth == stroke
+ && mFrameShadowColor == frameShadowColor
+ && mShadowRadius == shadowRadius
+ && mHighlightColor == highlightColor;
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 9d1f0419910d..387e0cea64bb 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -124,20 +124,32 @@ class KeyguardMultiUserAvatar extends FrameLayout {
mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
mUserName = (TextView) findViewById(R.id.keyguard_user_name);
- Bitmap icon = null;
- try {
- icon = BitmapFactory.decodeFile(rewriteIconPath(user.iconPath));
- } catch (Exception e) {
- if (DEBUG) Log.d(TAG, "failed to open profile icon " + user.iconPath, e);
- }
+ mFramed = (KeyguardCircleFramedDrawable)
+ KeyguardViewMediator.getAvatarCache().get(user.id);
+
+ // If we can't find it or the params don't match, create the drawable again
+ if (mFramed == null
+ || !mFramed.verifyParams(mIconSize, mFrameColor, mStroke, mFrameShadowColor,
+ mShadowRadius, mHighlightColor)) {
+ Bitmap icon = null;
+ try {
+ icon = BitmapFactory.decodeFile(rewriteIconPath(user.iconPath));
+ } catch (Exception e) {
+ if (DEBUG) Log.d(TAG, "failed to open profile icon " + user.iconPath, e);
+ }
- if (icon == null) {
- icon = BitmapFactory.decodeResource(mContext.getResources(),
- com.android.internal.R.drawable.ic_contact_picture);
+ if (icon == null) {
+ icon = BitmapFactory.decodeResource(mContext.getResources(),
+ com.android.internal.R.drawable.ic_contact_picture);
+ }
+
+ mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
+ mFrameShadowColor, mShadowRadius, mHighlightColor);
+ KeyguardViewMediator.getAvatarCache().put(user.id, mFramed);
}
- mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
- mFrameShadowColor, mShadowRadius, mHighlightColor);
+ mFramed.reset();
+
mUserImage.setImageDrawable(mFramed);
mUserName.setText(mUserInfo.name);
setOnClickListener(mUserSelector);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 986dc49b29a9..5a6458605e4e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -91,6 +91,7 @@ public class KeyguardUpdateMonitor {
private static final int MSG_USER_SWITCH_COMPLETE = 314;
private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
protected static final int MSG_SET_PLAYBACK_STATE = 316;
+ protected static final int MSG_USER_INFO_CHANGED = 317;
private static KeyguardUpdateMonitor sInstance;
@@ -178,6 +179,9 @@ public class KeyguardUpdateMonitor {
case MSG_SET_PLAYBACK_STATE:
handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
break;
+ case MSG_USER_INFO_CHANGED:
+ handleUserInfoChanged(msg.arg1);
+ break;
}
}
};
@@ -280,6 +284,17 @@ public class KeyguardUpdateMonitor {
}
};
+ private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
+
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED,
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0));
+ }
+ }
+ };
+
/**
* When we receive a
* {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast,
@@ -389,7 +404,6 @@ public class KeyguardUpdateMonitor {
return sInstance;
}
-
protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
mDisplayClientState.clientGeneration = clientGeneration;
mDisplayClientState.clearing = clearing;
@@ -422,6 +436,15 @@ public class KeyguardUpdateMonitor {
}
}
+ private void handleUserInfoChanged(int userId) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserInfoChanged(userId);
+ }
+ }
+ }
+
private KeyguardUpdateMonitor(Context context) {
mContext = context;
@@ -456,6 +479,10 @@ public class KeyguardUpdateMonitor {
bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
context.registerReceiver(mBroadcastReceiver, bootCompleteFilter);
+ final IntentFilter userInfoFilter = new IntentFilter(Intent.ACTION_USER_INFO_CHANGED);
+ context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, userInfoFilter,
+ null, null);
+
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
new IUserSwitchObserver.Stub() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
index 368ccb396e5a..41816db0edc8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
@@ -107,6 +107,11 @@ class KeyguardUpdateMonitorCallback {
void onUserRemoved(int userId) { }
/**
+ * Called when the user's info changed.
+ */
+ void onUserInfoChanged(int userId) { }
+
+ /**
* Called when boot completed.
*
* Note, this callback will only be received if boot complete occurs after registering with
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 08a95a6dd27e..885cb450224a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -254,6 +254,11 @@ public class KeyguardViewMediator {
private final float mLockSoundVolume;
/**
+ * Cache of avatar drawables, for use by KeyguardMultiUserAvatar.
+ */
+ private static MultiUserAvatarCache sMultiUserAvatarCache = new MultiUserAvatarCache();
+
+ /**
* The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
* various things.
*/
@@ -333,6 +338,12 @@ public class KeyguardViewMediator {
@Override
public void onUserRemoved(int userId) {
mLockPatternUtils.removeUser(userId);
+ sMultiUserAvatarCache.clear(userId);
+ }
+
+ @Override
+ public void onUserInfoChanged(int userId) {
+ sMultiUserAvatarCache.clear(userId);
}
@Override
@@ -1431,4 +1442,8 @@ public class KeyguardViewMediator {
return mSearchManager != null
&& mSearchManager.getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
}
+
+ public static MultiUserAvatarCache getAvatarCache() {
+ return sMultiUserAvatarCache;
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java
new file mode 100644
index 000000000000..7969c7d5eec6
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiUserAvatarCache.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.graphics.drawable.Drawable;
+
+import java.util.HashMap;
+
+public class MultiUserAvatarCache {
+
+ private final HashMap<Integer, Drawable> mCache;
+
+ public MultiUserAvatarCache() {
+ mCache = new HashMap<Integer, Drawable>();
+ }
+
+ public void clear(int userId) {
+ mCache.remove(userId);
+ }
+
+ public Drawable get(int userId) {
+ return mCache.get(userId);
+ }
+
+ public void put(int userId, Drawable image) {
+ mCache.put(userId, image);
+ }
+}
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index ea7b696f68b5..f7a7fdf5a41a 100644
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -1092,10 +1093,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (isUp) {
// connect to GattService
- Intent i = new Intent(IBluetoothGatt.class.getName());
- if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE,
- UserHandle.CURRENT)) {
- Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName());
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Intent i = new Intent(IBluetoothGatt.class.getName());
+ if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.CURRENT)) {
+ Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName());
+ }
}
} else {
//If Bluetooth is off, send service down event to proxy objects, and unbind
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index cc74b9202014..6b38f89f3879 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -727,7 +727,13 @@ public class NotificationManagerService extends INotificationManager.Stub
&& info.userid == userid) {
mListeners.remove(i);
if (info.connection != null) {
- mContext.unbindService(info.connection);
+ try {
+ mContext.unbindService(info.connection);
+ } catch (IllegalArgumentException ex) {
+ // something happened to the service: we think we have a connection
+ // but it's bogus.
+ Slog.e(TAG, "Listener " + name + " could not be unbound: " + ex);
+ }
}
}
}
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 241b224807fb..3b6393722e44 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -1265,6 +1265,11 @@ public class AccountManagerService
final boolean customTokens =
authenticatorInfo != null && authenticatorInfo.type.customTokens;
+ // Check to see that the app is authorized to access the account, in case it's a
+ // restricted account.
+ if (!ArrayUtils.contains(getAccounts((String) null), account)) {
+ throw new IllegalArgumentException("no such account");
+ }
// skip the check if customTokens
final int callerUid = Binder.getCallingUid();
final boolean permissionGranted = customTokens ||
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 11c6dabf8ade..1323c9326bf8 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -296,7 +296,7 @@ public class UserManagerService extends IUserManager.Stub {
Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId));
+ mContext.sendBroadcastAsUser(changedIntent, UserHandle.ALL);
}
@Override