diff options
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 |