Merge "Fix missing observer reply callbacks"
diff --git a/api/current.txt b/api/current.txt
index 24cddd6..1231ff3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4867,6 +4867,7 @@
field public static final int PRIORITY_MAX = 2; // 0x2
field public static final int PRIORITY_MIN = -2; // 0xfffffffe
field public static final deprecated int STREAM_DEFAULT = -1; // 0xffffffff
+ field public static final java.lang.String TOPIC_DEFAULT = "system_default_topic";
field public static final int VISIBILITY_PRIVATE = 0; // 0x0
field public static final int VISIBILITY_PUBLIC = 1; // 0x1
field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
@@ -36763,7 +36764,8 @@
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
- method public final boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final deprecated boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
diff --git a/api/system-current.txt b/api/system-current.txt
index 00d5745..ef897b4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4985,6 +4985,7 @@
field public static final int PRIORITY_MAX = 2; // 0x2
field public static final int PRIORITY_MIN = -2; // 0xfffffffe
field public static final deprecated int STREAM_DEFAULT = -1; // 0xffffffff
+ field public static final java.lang.String TOPIC_DEFAULT = "system_default_topic";
field public static final int VISIBILITY_PRIVATE = 0; // 0x0
field public static final int VISIBILITY_PUBLIC = 1; // 0x1
field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
@@ -39086,7 +39087,8 @@
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
- method public final boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final deprecated boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 84ddd9f..c1d5b19 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -47,11 +47,11 @@
void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
boolean areNotificationsEnabledForPackage(String pkg, int uid);
- void setPackagePriority(String pkg, int uid, int priority);
- int getPackagePriority(String pkg, int uid);
-
- void setPackageVisibilityOverride(String pkg, int uid, int visibility);
- int getPackageVisibilityOverride(String pkg, int uid);
+ ParceledListSlice getTopics(String pkg, int uid);
+ void setTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility);
+ int getTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic);
+ void setTopicPriority(String pkg, int uid, in Notification.Topic topic, int priority);
+ int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
index 9d8129c..3f1d113 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,3 +17,4 @@
package android.app;
parcelable Notification;
+parcelable Notification.Topic;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4e6548b..848b33f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -1433,6 +1434,9 @@
};
}
+ @SystemApi
+ public static final String TOPIC_DEFAULT = "system_default_topic";
+
private Topic topic;
public Topic getTopic() {
@@ -3419,6 +3423,10 @@
mN.extras.putStringArray(EXTRA_PEOPLE,
mPersonList.toArray(new String[mPersonList.size()]));
}
+ if (mN.topic == null) {
+ mN.topic = new Topic(TOPIC_DEFAULT, mContext.getString(
+ R.string.default_notification_topic_label));
+ }
return mN;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9034cc9..f560f8a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7758,6 +7758,7 @@
AUTO_TIME_ZONE,
POWER_SOUNDS_ENABLED,
DOCK_SOUNDS_ENABLED,
+ CHARGING_SOUNDS_ENABLED,
USB_MASS_STORAGE_ENABLED,
ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 227d8f2..7cd4f0d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3696,7 +3696,7 @@
/**
* Flag indicating that a drag can cross window boundaries. When
- * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
+ * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, all visible applications will be able to participate
* in the drag operation and receive the dragged content.
*
@@ -3741,7 +3741,7 @@
/**
* Flag indicating that the drag shadow will be opaque. When
- * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
+ * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, the drag shadow will be opaque, otherwise, it will be semitransparent.
*/
public static final int DRAG_FLAG_OPAQUE = 1 << 9;
@@ -19910,6 +19910,15 @@
}
/**
+ * @deprecated Use {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)
+ * startDragAndDrop()} for newer platform versions.
+ */
+ public final boolean startDrag(ClipData data, DragShadowBuilder shadowBuilder,
+ Object myLocalState, int flags) {
+ return startDragAndDrop(data, shadowBuilder, myLocalState, flags);
+ }
+
+ /**
* Starts a drag and drop operation. When your application calls this method, it passes a
* {@link android.view.View.DragShadowBuilder} object to the system. The
* system calls this object's {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)}
@@ -19926,9 +19935,10 @@
* {@link android.view.DragEvent#ACTION_DRAG_STARTED}.
* </p>
* <p>
- * Your application can invoke startDrag() on any attached View object. The View object does not
- * need to be the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to
- * be related to the View the user selected for dragging.
+ * Your application can invoke {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object,
+ * int) startDragAndDrop()} on any attached View object. The View object does not need to be
+ * the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to be related
+ * to the View the user selected for dragging.
* </p>
* @param data A {@link android.content.ClipData} object pointing to the data to be
* transferred by the drag and drop operation.
@@ -19948,10 +19958,10 @@
* {@code false} if it fails anywhere. Returning {@code false} means the system was unable to
* do a drag, and so no drag operation is in progress.
*/
- public final boolean startDrag(ClipData data, DragShadowBuilder shadowBuilder,
+ public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,
Object myLocalState, int flags) {
if (ViewDebug.DEBUG_DRAG) {
- Log.d(VIEW_LOG_TAG, "startDrag: data=" + data + " flags=" + flags);
+ Log.d(VIEW_LOG_TAG, "startDragAndDrop: data=" + data + " flags=" + flags);
}
boolean okay = false;
@@ -20030,7 +20040,8 @@
/**
* Handles drag events sent by the system following a call to
- * {@link android.view.View#startDrag(ClipData,DragShadowBuilder,Object,int) startDrag()}.
+ * {@link android.view.View#startDragAndDrop(ClipData,DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
*<p>
* When the system calls this method, it passes a
* {@link android.view.DragEvent} object. A call to
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 47df4e8..41f1ce7 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -937,10 +937,11 @@
}
@Override
- public void onDismiss() {
- super.onDismiss();
+ protected void onDismiss() {
mMenu.close();
mOverflowPopup = null;
+
+ super.onDismiss();
}
}
@@ -959,10 +960,11 @@
}
@Override
- public void onDismiss() {
- super.onDismiss();
+ protected void onDismiss() {
mActionButtonPopup = null;
mOpenSubMenuId = 0;
+
+ super.onDismiss();
}
}
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index a53df88..027f874 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -19,7 +19,6 @@
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
-import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.ShowableListMenu;
import android.annotation.MenuRes;
@@ -45,7 +44,7 @@
private final MenuPopupHelper mPopup;
private OnMenuItemClickListener mMenuItemClickListener;
- private OnDismissListener mDismissListener;
+ private OnDismissListener mOnDismissListener;
private OnTouchListener mDragListener;
/**
@@ -114,20 +113,13 @@
mPopup = new MenuPopupHelper(context, mMenu, anchor, false, popupStyleAttr, popupStyleRes);
mPopup.setGravity(gravity);
- mPopup.setCallback(new MenuPresenter.Callback() {
+ mPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(PopupMenu.this);
+ public void onDismiss() {
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss(PopupMenu.this);
}
}
-
- @Override
- public boolean onOpenSubMenu(MenuBuilder subMenu) {
- // The menu presenter will handle opening the submenu itself.
- // Nothing to do here.
- return false;
- }
});
}
@@ -259,7 +251,7 @@
* @param listener the listener to notify
*/
public void setOnDismissListener(OnDismissListener listener) {
- mDismissListener = listener;
+ mOnDismissListener = listener;
}
/**
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 08c7935..c992c70 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -33,6 +33,7 @@
public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 260;
public static final int ACTION_ZEN_ALLOW_PEEK = 261;
public static final int ACTION_ZEN_ALLOW_LIGHTS = 262;
+ public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f73df00..9391c60 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 135 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 136 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1975,8 +1975,14 @@
private int buildBatteryLevelInt(HistoryItem h) {
return ((((int)h.batteryLevel)<<25)&0xfe000000)
- | ((((int)h.batteryTemperature)<<14)&0x01ff8000)
- | ((((int)h.batteryVoltage)<<1)&0x00007fff);
+ | ((((int)h.batteryTemperature)<<15)&0x01ff8000)
+ | ((((int)h.batteryVoltage)<<1)&0x00007ffe);
+ }
+
+ private void readBatteryLevelInt(int batteryLevelInt, HistoryItem out) {
+ out.batteryLevel = (byte)((batteryLevelInt & 0xfe000000) >>> 25);
+ out.batteryTemperature = (short)((batteryLevelInt & 0x01ff8000) >>> 15);
+ out.batteryVoltage = (char)((batteryLevelInt & 0x00007ffe) >>> 1);
}
private int buildStateInt(HistoryItem h) {
@@ -2117,9 +2123,7 @@
final int batteryLevelInt;
if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
batteryLevelInt = src.readInt();
- cur.batteryLevel = (byte)((batteryLevelInt>>25)&0x7f);
- cur.batteryTemperature = (short)((batteryLevelInt<<7)>>21);
- cur.batteryVoltage = (char)(batteryLevelInt&0x3fff);
+ readBatteryLevelInt(batteryLevelInt, cur);
cur.numReadInts += 1;
if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x"
+ Integer.toHexString(batteryLevelInt)
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index e674ecc..59d5f94 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -18,82 +18,104 @@
import com.android.internal.view.menu.MenuPresenter.Callback;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
-import android.widget.PopupWindow;
+import android.widget.PopupWindow.OnDismissListener;
/**
* Presents a menu as a small, simple popup anchored to another view.
- *
- * @hide
*/
-public class MenuPopupHelper implements PopupWindow.OnDismissListener {
+public class MenuPopupHelper {
private final Context mContext;
+
+ // Immutable cached popup menu properties.
private final MenuBuilder mMenu;
private final boolean mOverflowOnly;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
+ // Mutable cached popup menu properties.
private View mAnchorView;
- private MenuPopup mPopup;
-
- private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private int mDropDownGravity = Gravity.START;
private boolean mForceShowIcon;
- private boolean mShowTitle;
private Callback mPresenterCallback;
- private int mInitXOffset;
- private int mInitYOffset;
- public MenuPopupHelper(Context context, MenuBuilder menu) {
+ private MenuPopup mPopup;
+ private OnDismissListener mOnDismissListener;
+
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) {
this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
}
- public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
+ @NonNull View anchorView) {
this(context, menu, anchorView, false, com.android.internal.R.attr.popupMenuStyle, 0);
}
- public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
- boolean overflowOnly, int popupStyleAttr) {
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
+ @NonNull View anchorView,
+ boolean overflowOnly, @AttrRes int popupStyleAttr) {
this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0);
}
- public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
- boolean overflowOnly, int popupStyleAttr, int popupStyleRes) {
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
+ @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr,
+ @StyleRes int popupStyleRes) {
mContext = context;
mMenu = menu;
+ mAnchorView = anchorView;
mOverflowOnly = overflowOnly;
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
- mAnchorView = anchorView;
- mPopup = createMenuPopup();
}
- private MenuPopup createMenuPopup() {
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus)) {
- return new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr, mPopupStyleRes,
- mOverflowOnly);
- }
- return new StandardMenuPopup(
- mContext, mMenu, mAnchorView, mPopupStyleAttr, mPopupStyleRes, mOverflowOnly);
+ public void setOnDismissListener(@Nullable OnDismissListener listener) {
+ mOnDismissListener = listener;
}
- public void setAnchorView(View anchor) {
+ /**
+ * Sets the view to which the popup window is anchored.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param anchor the view to which the popup window should be anchored
+ */
+ public void setAnchorView(@NonNull View anchor) {
mAnchorView = anchor;
- mPopup.setAnchorView(anchor);
}
- public void setForceShowIcon(boolean forceShow) {
- mForceShowIcon = forceShow;
- mPopup.setForceShowIcon(forceShow);
+ /**
+ * Sets whether the popup menu's adapter is forced to show icons in the
+ * menu item views.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param forceShowIcon {@code true} to force icons to be shown, or
+ * {@code false} for icons to be optionally shown
+ */
+ public void setForceShowIcon(boolean forceShowIcon) {
+ mForceShowIcon = forceShowIcon;
}
+ /**
+ * Sets the alignment of the popup window relative to the anchor view.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param gravity alignment of the popup relative to the anchor
+ */
public void setGravity(int gravity) {
mDropDownGravity = gravity;
- mPopup.setGravity(gravity);
}
+ /**
+ * @return alignment of the popup relative to the anchor
+ */
public int getGravity() {
return mDropDownGravity;
}
@@ -110,7 +132,11 @@
}
}
- public ShowableListMenu getPopup() {
+ @NonNull
+ public MenuPopup getPopup() {
+ if (mPopup == null) {
+ mPopup = createPopup();
+ }
return mPopup;
}
@@ -129,14 +155,28 @@
return false;
}
- mInitXOffset = 0;
- mInitYOffset = 0;
- mShowTitle = false;
-
- showPopup();
+ showPopup(0, 0, false, false);
return true;
}
+ /**
+ * Shows the popup menu and makes a best-effort to anchor it to the
+ * specified (x,y) coordinate relative to the anchor view.
+ * <p>
+ * If the popup's resolved gravity is {@link Gravity#LEFT}, this will
+ * display the popup with its top-left corner at (x,y) relative to the
+ * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the
+ * popup's top-right corner will be at (x,y).
+ * <p>
+ * If the popup cannot be displayed fully on-screen, this method will
+ * attempt to scroll the anchor view's ancestors and/or offset the popup
+ * such that it may be displayed fully on-screen.
+ *
+ * @param x x coordinate relative to the anchor view
+ * @param y y coordinate relative to the anchor view
+ * @return {@code true} if the popup was shown or was already showing prior
+ * to calling this method, {@code false} otherwise
+ */
public boolean tryShow(int x, int y) {
if (isShowing()) {
return true;
@@ -146,51 +186,104 @@
return false;
}
- mInitXOffset = x;
- mInitYOffset = y;
- mShowTitle = true;
-
- showPopup();
+ showPopup(x, y, true, true);
return true;
}
- private void showPopup() {
- mPopup = createMenuPopup();
- mPopup.setAnchorView(mAnchorView);
- mPopup.setCallback(mPresenterCallback);
- mPopup.setForceShowIcon(mForceShowIcon);
- mPopup.setGravity(mDropDownGravity);
- mPopup.setHorizontalOffset(mInitXOffset);
- mPopup.setShowTitle(mShowTitle);
- mPopup.setVerticalOffset(mInitYOffset);
+ /**
+ * Creates the popup and assigns cached properties.
+ *
+ * @return an initialized popup
+ */
+ @NonNull
+ private MenuPopup createPopup() {
+ final boolean enableCascadingSubmenus = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCascadingSubmenus);
- // In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
- // we must set the listener to this outer Helper rather than to the inner MenuPopup.
- // Not to worry -- the inner MenuPopup will call our own #onDismiss method after it's done
- // its own handling.
- mPopup.setOnDismissListener(this);
+ final MenuPopup popup;
+ if (enableCascadingSubmenus) {
+ popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr,
+ mPopupStyleRes, mOverflowOnly);
+ } else {
+ popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr,
+ mPopupStyleRes, mOverflowOnly);
+ }
- mPopup.addMenu(mMenu);
- mPopup.show();
+ // Assign immutable properties.
+ popup.addMenu(mMenu);
+ popup.setOnDismissListener(mInternalOnDismissListener);
+
+ // Assign mutable properties. These may be reassigned later.
+ popup.setAnchorView(mAnchorView);
+ popup.setCallback(mPresenterCallback);
+ popup.setForceShowIcon(mForceShowIcon);
+ popup.setGravity(mDropDownGravity);
+
+ return popup;
}
+ private void showPopup(int xOffset, int yOffset, boolean resolveOffsets, boolean showTitle) {
+ if (resolveOffsets) {
+ // If the resolved drop-down gravity is RIGHT, the popup's right
+ // edge will be aligned with the anchor view. Adjust by the anchor
+ // width such that the top-right corner is at the X offset.
+ final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
+ mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
+ if (hgrav == Gravity.RIGHT) {
+ xOffset -= mAnchorView.getWidth();
+ }
+ }
+
+ final MenuPopup popup = getPopup();
+ popup.setHorizontalOffset(xOffset);
+ popup.setVerticalOffset(yOffset);
+ popup.setShowTitle(showTitle);
+ popup.show();
+ }
+
+ /**
+ * Dismisses the popup, if showing.
+ */
public void dismiss() {
if (isShowing()) {
mPopup.dismiss();
}
}
- @Override
- public void onDismiss() {
+ /**
+ * Called after the popup has been dismissed.
+ * <p>
+ * <strong>Note:</strong> Subclasses should call the super implementation
+ * last to ensure that any necessary tear down has occurred before the
+ * listener specified by {@link #setOnDismissListener(OnDismissListener)}
+ * is called.
+ */
+ protected void onDismiss() {
mPopup = null;
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
public boolean isShowing() {
return mPopup != null && mPopup.isShowing();
}
- public void setCallback(MenuPresenter.Callback cb) {
+ public void setCallback(@Nullable MenuPresenter.Callback cb) {
mPresenterCallback = cb;
- mPopup.setCallback(cb);
+ if (mPopup != null) {
+ mPopup.setCallback(cb);
+ }
}
+
+ /**
+ * Listener used to proxy dismiss callbacks to the helper's owner.
+ */
+ private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() {
+ @Override
+ public void onDismiss() {
+ MenuPopupHelper.this.onDismiss();
+ }
+ };
}
diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java
index 3b7bce4..a694aca 100644
--- a/core/java/com/android/internal/widget/ButtonBarLayout.java
+++ b/core/java/com/android/internal/widget/ButtonBarLayout.java
@@ -30,6 +30,10 @@
* orientation when it can't fit its child views horizontally.
*/
public class ButtonBarLayout extends LinearLayout {
+ // Whether to allow vertically stacked button bars. This is disabled for
+ // configurations with a small (e.g. less than 320dp) screen height. -->
+ private static final int ALLOW_STACKING_MIN_HEIGHT_DP = 320;
+
/** Whether the current configuration allows stacking. */
private boolean mAllowStacking;
@@ -38,8 +42,12 @@
public ButtonBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ final boolean allowStackingDefault =
+ context.getResources().getConfiguration().screenHeightDp
+ >= ALLOW_STACKING_MIN_HEIGHT_DP;
final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
- mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
+ mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking,
+ allowStackingDefault);
ta.recycle();
}
diff --git a/core/res/res/layout/alert_dialog_button_bar_material.xml b/core/res/res/layout/alert_dialog_button_bar_material.xml
index 6e102f3..f7974a5 100644
--- a/core/res/res/layout/alert_dialog_button_bar_material.xml
+++ b/core/res/res/layout/alert_dialog_button_bar_material.xml
@@ -27,7 +27,6 @@
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:gravity="bottom"
- android:allowStacking="@bool/allow_stacked_button_bar"
style="?attr/buttonBarStyle">
<Button
diff --git a/core/res/res/values-h320dp/bools.xml b/core/res/res/values-h320dp/bools.xml
deleted file mode 100644
index 3bbfe96..0000000
--- a/core/res/res/values-h320dp/bools.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resources>
- <bool name="allow_stacked_button_bar">true</bool>
-</resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 7c63950..457131a 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -25,8 +25,4 @@
<bool name="show_ongoing_ime_switcher">true</bool>
<bool name="action_bar_expanded_action_views_exclusive">true</bool>
<bool name="target_honeycomb_needs_options_menu">true</bool>
-
- <!-- Whether to allow vertically stacked button bars. This is disabled for
- configurations with a small (e.g. less than 320dp) screen height. -->
- <bool name="allow_stacked_button_bar">false</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 057790a..539baa5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1946,9 +1946,9 @@
See {@link com.android.server.notification.NotificationSignalExtractor} -->
<string-array name="config_notificationSignalExtractors">
<item>com.android.server.notification.ValidateNotificationPeople</item>
- <item>com.android.server.notification.PackagePriorityExtractor</item>
+ <item>com.android.server.notification.TopicPriorityExtractor</item>
<item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
- <item>com.android.server.notification.PackageVisibilityExtractor</item>
+ <item>com.android.server.notification.TopicVisibilityExtractor</item>
</string-array>
<!-- Flag indicating that this device does not rotate and will always remain in its default
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 00c0fe8..1964bec 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4072,4 +4072,6 @@
<item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> selected</item>
<item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
</plurals>
+
+ <string name="default_notification_topic_label">Miscellaneous</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1e325b1..edd3555 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2321,7 +2321,6 @@
<java-symbol type="string" name="lockscreen_access_pattern_area" />
- <java-symbol type="bool" name="allow_stacked_button_bar" />
<java-symbol type="bool" name="config_eap_sim_based_auth_supported" />
<java-symbol type="array" name="config_cell_retries_per_error_code" />
@@ -2337,4 +2336,5 @@
<java-symbol type="string" name="config_iccHotswapPromptForRestartDialogComponent" />
<java-symbol type="string" name="config_packagedKeyboardName" />
+ <java-symbol type="string" name="default_notification_topic_label" />
</resources>
diff --git a/data/keyboards/qwerty.kl b/data/keyboards/qwerty.kl
index 58bf654..4186007 100644
--- a/data/keyboards/qwerty.kl
+++ b/data/keyboards/qwerty.kl
@@ -81,7 +81,7 @@
key 39 SEMICOLON
key 40 APOSTROPHE
key 14 DEL
-
+
key 44 Z
key 45 X
key 46 C
@@ -93,7 +93,7 @@
key 52 PERIOD
key 53 SLASH
key 28 ENTER
-
+
key 56 ALT_LEFT
key 100 ALT_RIGHT
key 42 SHIFT_LEFT
@@ -101,7 +101,7 @@
key 15 TAB
key 57 SPACE
key 150 EXPLORER
-key 155 ENVELOPE
+key 155 ENVELOPE
key 12 MINUS
key 13 EQUALS
@@ -110,3 +110,16 @@
# On an AT keyboard: ESC, F10
key 1 BACK
key 68 MENU
+
+# App switch = Overview key
+key 580 APP_SWITCH
+
+# Media control keys
+key 160 MEDIA_CLOSE
+key 161 MEDIA_EJECT
+key 163 MEDIA_NEXT
+key 164 MEDIA_PLAY_PAUSE
+key 165 MEDIA_PREVIOUS
+key 166 MEDIA_STOP
+key 167 MEDIA_RECORD
+key 168 MEDIA_REWIND
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index b9a9c24..2e96f18 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -159,6 +159,7 @@
String key_mgmt = "";
boolean certUsed = false;
boolean hasWepKey = false;
+ boolean isEap = false;
final ArrayList<String> rawLines = new ArrayList<String>();
public static Network readFromStream(BufferedReader in) {
@@ -189,6 +190,9 @@
ssid = line;
} else if (line.startsWith("key_mgmt=")) {
key_mgmt = line;
+ if (line.contains("EAP")) {
+ isEap = true;
+ }
} else if (line.startsWith("client_cert=")) {
certUsed = true;
} else if (line.startsWith("ca_cert=")) {
@@ -197,6 +201,8 @@
certUsed = true;
} else if (line.startsWith("wep_")) {
hasWepKey = true;
+ } else if (line.startsWith("eap=")) {
+ isEap = true;
}
}
@@ -325,6 +331,13 @@
continue;
}
}
+ // Don't propagate EAP network definitions
+ if (net.isEap) {
+ if (DEBUG_BACKUP) {
+ Log.v(TAG, "Skipping EAP network " + net.ssid + " / " + net.key_mgmt);
+ }
+ continue;
+ }
if (! mKnownNetworks.contains(net)) {
if (DEBUG_BACKUP) {
Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
@@ -353,6 +366,12 @@
continue;
}
+ if (net.isEap) {
+ // Similarly, omit EAP network definitions to avoid propagating
+ // controlled enterprise network definitions.
+ continue;
+ }
+
net.write(w);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 8b1caf9..3971706 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -904,12 +904,12 @@
}
}
- // Enforce what the calling package can mutate the system settings.
- enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name, runAsUserId);
-
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId);
+ // Enforce what the calling package can mutate the system settings.
+ enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name, callingUserId);
+
// Determine the owning user as some profile settings are cloned from the parent.
final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9f1dc0a..3d358ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -64,21 +64,17 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Pools.Pool;
-import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
import android.view.InputDevice;
-import android.view.InputEventConsistencyVerifier;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerInternal;
-import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
@@ -146,8 +142,6 @@
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
- private static final int MAX_POOL_SIZE = 10;
-
private static final int WINDOW_ID_UNKNOWN = -1;
private static int sIdCounter = 0;
@@ -158,9 +152,6 @@
private final Object mLock = new Object();
- private final Pool<PendingEvent> mPendingEventPool =
- new SimplePool<>(MAX_POOL_SIZE);
-
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
@@ -193,6 +184,8 @@
private boolean mHasInputFilter;
+ private KeyEventDispatcher mKeyEventDispatcher;
+
private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();
private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
@@ -756,12 +749,11 @@
boolean notifyKeyEvent(KeyEvent event, int policyFlags) {
synchronized (mLock) {
- KeyEvent localClone = KeyEvent.obtain(event);
- boolean handled = notifyKeyEventLocked(localClone, policyFlags, false);
- if (!handled) {
- handled = notifyKeyEventLocked(localClone, policyFlags, true);
+ List<Service> boundServices = getCurrentUserStateLocked().mBoundServices;
+ if (boundServices.isEmpty()) {
+ return false;
}
- return handled;
+ return getKeyEventDispatcher().notifyKeyEventLocked(event, policyFlags, boundServices);
}
}
@@ -935,31 +927,6 @@
return false;
}
- private boolean notifyKeyEventLocked(KeyEvent event, int policyFlags, boolean isDefault) {
- // TODO: Now we are giving the key events to the last enabled
- // service that can handle them Ideally, the user should
- // make the call which service handles key events. However,
- // only one service should handle key events to avoid user
- // frustration when different behavior is observed from
- // different combinations of enabled accessibility services.
- UserState state = getCurrentUserStateLocked();
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- Service service = state.mBoundServices.get(i);
- // Key events are handled only by services that declared
- // this capability and requested to filter key events.
- if (!service.mRequestFilterKeyEvents ||
- (service.mAccessibilityServiceInfo.getCapabilities() & AccessibilityServiceInfo
- .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) {
- continue;
- }
- if (service.mIsDefault == isDefault) {
- service.notifyKeyEvent(event, policyFlags);
- return true;
- }
- }
- return false;
- }
-
private void notifyClearAccessibilityCacheLocked() {
UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
@@ -1754,6 +1721,14 @@
return null;
}
+ private KeyEventDispatcher getKeyEventDispatcher() {
+ if (mKeyEventDispatcher == null) {
+ mKeyEventDispatcher = new KeyEventDispatcher(
+ mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock);
+ }
+ return mKeyEventDispatcher;
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
@@ -1954,22 +1929,6 @@
}
}
- private PendingEvent obtainPendingEventLocked(KeyEvent event, int policyFlags, int sequence) {
- PendingEvent pendingEvent = mPendingEventPool.acquire();
- if (pendingEvent == null) {
- pendingEvent = new PendingEvent();
- }
- pendingEvent.event = event;
- pendingEvent.policyFlags = policyFlags;
- pendingEvent.sequence = sequence;
- return pendingEvent;
- }
-
- private void recyclePendingEventLocked(PendingEvent pendingEvent) {
- pendingEvent.clear();
- mPendingEventPool.release(pendingEvent);
- }
-
private int findWindowIdLocked(IBinder token) {
final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
if (globalIndex >= 0) {
@@ -2082,8 +2041,6 @@
final SparseArray<AccessibilityEvent> mPendingEvents =
new SparseArray<>();
- final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
-
boolean mWasConnectedAndDied;
// Handler only for dispatching accessibility events since we use event
@@ -2195,7 +2152,7 @@
return false;
}
UserState userState = getUserStateLocked(mUserId);
- mKeyEventDispatcher.flush();
+ getKeyEventDispatcher().flush(this);
if (!mIsAutomation) {
mContext.unbindService(this);
} else {
@@ -2212,7 +2169,7 @@
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
- mKeyEventDispatcher.setOnKeyEventResult(handled, sequence);
+ getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
}
@Override
@@ -2926,7 +2883,7 @@
return;
}
mWasConnectedAndDied = true;
- mKeyEventDispatcher.flush();
+ getKeyEventDispatcher().flush(this);
UserState userState = getUserStateLocked(mUserId);
// The death recipient is unregistered in removeServiceLocked
removeServiceLocked(this, userState);
@@ -3035,11 +2992,6 @@
gestureId, 0).sendToTarget();
}
- public void notifyKeyEvent(KeyEvent event, int policyFlags) {
- mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_KEY_EVENT,
- policyFlags, 0, event).sendToTarget();
- }
-
public void notifyClearAccessibilityNodeInfoCache() {
mInvocationHandler.sendEmptyMessage(
InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
@@ -3084,10 +3036,6 @@
}
}
- private void notifyKeyEventInternal(KeyEvent event, int policyFlags) {
- mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
- }
-
private void notifyClearAccessibilityCacheInternal() {
final IAccessibilityServiceClient listener;
synchronized (mLock) {
@@ -3205,9 +3153,7 @@
private final class InvocationHandler extends Handler {
public static final int MSG_ON_GESTURE = 1;
- public static final int MSG_ON_KEY_EVENT = 2;
- public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
- public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
+ public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
private static final int MSG_ON_MAGNIFICATION_CHANGED = 5;
@@ -3226,21 +3172,10 @@
notifyGestureInternal(gestureId);
} break;
- case MSG_ON_KEY_EVENT: {
- KeyEvent event = (KeyEvent) message.obj;
- final int policyFlags = message.arg1;
- notifyKeyEventInternal(event, policyFlags);
- } break;
-
case MSG_CLEAR_ACCESSIBILITY_CACHE: {
notifyClearAccessibilityCacheInternal();
} break;
- case MSG_ON_KEY_EVENT_TIMEOUT: {
- PendingEvent eventState = (PendingEvent) message.obj;
- setOnKeyEventResult(false, eventState.sequence);
- } break;
-
case MSG_ON_MAGNIFICATION_CHANGED: {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
@@ -3278,140 +3213,6 @@
}
}
- private final class KeyEventDispatcher {
-
- private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
-
- private PendingEvent mPendingEvents;
-
- private final InputEventConsistencyVerifier mSentEventsVerifier =
- InputEventConsistencyVerifier.isInstrumentationEnabled()
- ? new InputEventConsistencyVerifier(
- this, 0, KeyEventDispatcher.class.getSimpleName()) : null;
-
- public void notifyKeyEvent(KeyEvent event, int policyFlags) {
- final PendingEvent pendingEvent;
-
- synchronized (mLock) {
- pendingEvent = addPendingEventLocked(event, policyFlags);
- }
-
- Message message = mInvocationHandler.obtainMessage(
- InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
- mInvocationHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
-
- try {
- // Accessibility services are exclusively not in the system
- // process, therefore no need to clone the motion event to
- // prevent tampering. It will be cloned in the IPC call.
- mServiceInterface.onKeyEvent(pendingEvent.event, pendingEvent.sequence);
- } catch (RemoteException re) {
- setOnKeyEventResult(false, pendingEvent.sequence);
- }
- }
-
- public void setOnKeyEventResult(boolean handled, int sequence) {
- synchronized (mLock) {
- PendingEvent pendingEvent = removePendingEventLocked(sequence);
- if (pendingEvent != null) {
- mInvocationHandler.removeMessages(
- InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
- pendingEvent);
- pendingEvent.handled = handled;
- finishPendingEventLocked(pendingEvent);
- }
- }
- }
-
- public void flush() {
- synchronized (mLock) {
- cancelAllPendingEventsLocked();
- if (mSentEventsVerifier != null) {
- mSentEventsVerifier.reset();
- }
- }
- }
-
- private PendingEvent addPendingEventLocked(KeyEvent event, int policyFlags) {
- final int sequence = event.getSequenceNumber();
- PendingEvent pendingEvent = obtainPendingEventLocked(event, policyFlags, sequence);
- pendingEvent.next = mPendingEvents;
- mPendingEvents = pendingEvent;
- return pendingEvent;
- }
-
- private PendingEvent removePendingEventLocked(int sequence) {
- PendingEvent previous = null;
- PendingEvent current = mPendingEvents;
-
- while (current != null) {
- if (current.sequence == sequence) {
- if (previous != null) {
- previous.next = current.next;
- } else {
- mPendingEvents = current.next;
- }
- current.next = null;
- return current;
- }
- previous = current;
- current = current.next;
- }
- return null;
- }
-
- private void finishPendingEventLocked(PendingEvent pendingEvent) {
- if (!pendingEvent.handled) {
- sendKeyEventToInputFilter(pendingEvent.event, pendingEvent.policyFlags);
- }
- // Nullify the event since we do not want it to be
- // recycled yet. It will be sent to the input filter.
- pendingEvent.event = null;
- recyclePendingEventLocked(pendingEvent);
- }
-
- private void sendKeyEventToInputFilter(KeyEvent event, int policyFlags) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "Injecting event: " + event);
- }
- if (mSentEventsVerifier != null) {
- mSentEventsVerifier.onKeyEvent(event, 0);
- }
- policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- mMainHandler.obtainMessage(MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER,
- policyFlags, 0, event).sendToTarget();
- }
-
- private void cancelAllPendingEventsLocked() {
- while (mPendingEvents != null) {
- PendingEvent pendingEvent = removePendingEventLocked(mPendingEvents.sequence);
- pendingEvent.handled = false;
- mInvocationHandler.removeMessages(InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
- pendingEvent);
- finishPendingEventLocked(pendingEvent);
- }
- }
- }
- }
-
- private static final class PendingEvent {
- PendingEvent next;
-
- KeyEvent event;
- int policyFlags;
- int sequence;
- boolean handled;
-
- public void clear() {
- if (event != null) {
- event.recycle();
- event = null;
- }
- next = null;
- policyFlags = 0;
- sequence = 0;
- handled = false;
- }
}
final class WindowsForAccessibilityCallback implements
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
new file mode 100644
index 0000000..3469565
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
@@ -0,0 +1,285 @@
+/*
+ ** Copyright 2015, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Pools;
+import android.util.Pools.Pool;
+import android.util.Slog;
+import android.view.InputEventConsistencyVerifier;
+import android.view.KeyEvent;
+import android.view.WindowManagerPolicy;
+
+import com.android.server.accessibility.AccessibilityManagerService.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Dispatcher to send KeyEvents to all accessibility services that are able to process them.
+ * Events that are handled by one or more services are consumed. Events that are not processed
+ * by any service (or time out before a service reports them as handled) are passed along to
+ * the rest of the system.
+ *
+ * The class assumes that services report their return values in order, which is valid because
+ * they process each call to {@code AccessibilityService.onKeyEvent} on a single thread, and so
+ * don't see the N+1th event until they have processed the Nth event.
+ */
+public class KeyEventDispatcher {
+ // Debugging
+ private static final String LOG_TAG = "KeyEventDispatcher";
+ private static final boolean DEBUG = false;
+ /* KeyEvents must be processed in this time interval */
+ private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
+ private static final int MSG_ON_KEY_EVENT_TIMEOUT = 1;
+ private static final int MAX_POOL_SIZE = 10;
+
+ private final Pool<PendingKeyEvent> mPendingEventPool = new Pools.SimplePool<>(MAX_POOL_SIZE);
+ private final Object mLock;
+
+ /*
+ * Track events sent to each service. If a KeyEvent is to be sent to at least one service,
+ * a corresponding PendingKeyEvent is created for it. This PendingKeyEvent is placed in
+ * the list for each service its KeyEvent is sent to. It is removed from the list when
+ * the service calls setOnKeyEventResult, or when we time out waiting for the service to
+ * respond.
+ */
+ private final Map<Service, ArrayList<PendingKeyEvent>> mPendingEventsMap = new ArrayMap<>();
+
+ private final InputEventConsistencyVerifier mSentEventsVerifier;
+ private final Handler mHandlerToSendKeyEventsToInputFilter;
+ private final int mMessageTypeForSendKeyEvent;
+ private final Handler mKeyEventTimeoutHandler;
+
+ /**
+ * @param handlerToSendKeyEventsToInputFilter The handler to which to post {@code KeyEvent}s
+ * that have not been handled by any accessibility service.
+ * @param messageTypeForSendKeyEvent The field to populate {@code message.what} for the
+ * message that carries a {@code KeyEvent} to be sent to the input filter
+ * @param lock The lock used for all synchronization in this package. This lock must be held
+ * when calling {@code notifyKeyEventLocked}
+ */
+ public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter,
+ int messageTypeForSendKeyEvent, Object lock) {
+ if (InputEventConsistencyVerifier.isInstrumentationEnabled()) {
+ mSentEventsVerifier = new InputEventConsistencyVerifier(
+ this, 0, KeyEventDispatcher.class.getSimpleName());
+ } else {
+ mSentEventsVerifier = null;
+ }
+ mHandlerToSendKeyEventsToInputFilter = handlerToSendKeyEventsToInputFilter;
+ mMessageTypeForSendKeyEvent = messageTypeForSendKeyEvent;
+ mKeyEventTimeoutHandler =
+ new Handler(mHandlerToSendKeyEventsToInputFilter.getLooper(), new Callback());
+ mLock = lock;
+ }
+
+ /**
+ * Notify that a new KeyEvent is available to accessibility services. Must be called with the
+ * lock used to construct this object held. The boundServices list must also be protected
+ * by a lock.
+ *
+ * @param event The new key event
+ * @param policyFlags Flags for the event
+ * @param boundServices A list of currently bound AccessibilityServices
+ *
+ * @return {@code true} if the event was passed to at least one AccessibilityService,
+ * {@code false} otherwise.
+ */
+ // TODO: The locking policy for boundServices needs some thought.
+ public boolean notifyKeyEventLocked(
+ KeyEvent event, int policyFlags, List<Service> boundServices) {
+ PendingKeyEvent pendingKeyEvent = null;
+ KeyEvent localClone = KeyEvent.obtain(event);
+ for (int i = 0; i < boundServices.size(); i++) {
+ Service service = boundServices.get(i);
+ // Key events are handled only by services that declared
+ // this capability and requested to filter key events.
+ if (!service.mRequestFilterKeyEvents) {
+ continue;
+ }
+ int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities()
+ & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
+ if (filterKeyEventBit == 0) {
+ continue;
+ }
+
+ try {
+ // The event will be cloned in the IPC call, so it doesn't need to be here.
+ service.mServiceInterface.onKeyEvent(localClone, localClone.getSequenceNumber());
+ } catch (RemoteException re) {
+ continue;
+ }
+
+ if (pendingKeyEvent == null) {
+ pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags);
+ }
+ ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(service);
+ if (pendingEventList == null) {
+ pendingEventList = new ArrayList<>();
+ mPendingEventsMap.put(service, pendingEventList);
+ }
+ pendingEventList.add(pendingKeyEvent);
+ pendingKeyEvent.referenceCount++;
+ }
+
+ if (pendingKeyEvent == null) {
+ localClone.recycle();
+ return false;
+ }
+
+ Message message = mKeyEventTimeoutHandler.obtainMessage(
+ MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent);
+ mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
+ return true;
+ }
+
+ /**
+ * Set the result from onKeyEvent from one service.
+ *
+ * @param service The service setting the result
+ * @param handled {@code true} if the service handled the {@code KeyEvent}
+ * @param sequence The sequence number of the {@code KeyEvent}
+ */
+ public void setOnKeyEventResult(Service service, boolean handled, int sequence) {
+ synchronized (mLock) {
+ PendingKeyEvent pendingEvent =
+ removeEventFromListLocked(mPendingEventsMap.get(service), sequence);
+ if (pendingEvent != null) {
+ pendingEvent.handled |= handled;
+ removeReferenceToPendingEventLocked(pendingEvent);
+ }
+ }
+ }
+
+ /**
+ * Flush all pending key events for a service, treating all of them as unhandled
+ *
+ * @param service The service for which to flush events
+ */
+ public void flush(Service service) {
+ synchronized (mLock) {
+ List<PendingKeyEvent> pendingEvents = mPendingEventsMap.get(service);
+ if (pendingEvents != null) {
+ for (int i = 0; i < pendingEvents.size(); i++) {
+ PendingKeyEvent pendingEvent = pendingEvents.get(i);
+ removeReferenceToPendingEventLocked(pendingEvent);
+ }
+ mPendingEventsMap.remove(service);
+ }
+ }
+ }
+
+ private PendingKeyEvent obtainPendingEventLocked(KeyEvent event, int policyFlags) {
+ PendingKeyEvent pendingEvent = mPendingEventPool.acquire();
+ if (pendingEvent == null) {
+ pendingEvent = new PendingKeyEvent();
+ }
+ pendingEvent.event = event;
+ pendingEvent.policyFlags = policyFlags;
+ pendingEvent.referenceCount = 0;
+ pendingEvent.handled = false;
+ return pendingEvent;
+ }
+
+ private static PendingKeyEvent removeEventFromListLocked(
+ List<PendingKeyEvent> listOfEvents, int sequence) {
+ /* In normal operation, the event should be first */
+ for (int i = 0; i < listOfEvents.size(); i++) {
+ PendingKeyEvent pendingKeyEvent = listOfEvents.get(i);
+ if (pendingKeyEvent.event.getSequenceNumber() == sequence) {
+ /*
+ * Removing the first element of the ArrayList can be slow if there are a lot
+ * of events backed up, but for a handful of events it's better than incurring
+ * the fixed overhead of LinkedList. An ArrayList optimized for removing the
+ * first element (by treating the underlying array as a circular buffer) would
+ * be ideal.
+ */
+ listOfEvents.remove(pendingKeyEvent);
+ return pendingKeyEvent;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param pendingEvent The event whose reference count should be decreased
+ * @return {@code true} if the event was release, {@code false} if not.
+ */
+ private boolean removeReferenceToPendingEventLocked(PendingKeyEvent pendingEvent) {
+ if (--pendingEvent.referenceCount > 0) {
+ return false;
+ }
+ mKeyEventTimeoutHandler.removeMessages(MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
+ if (!pendingEvent.handled) {
+ /* Pass event to input filter */
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injecting event: " + pendingEvent.event);
+ }
+ if (mSentEventsVerifier != null) {
+ mSentEventsVerifier.onKeyEvent(pendingEvent.event, 0);
+ }
+ int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER;
+ mHandlerToSendKeyEventsToInputFilter
+ .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event)
+ .sendToTarget();
+ } else {
+ pendingEvent.event.recycle();
+ }
+ mPendingEventPool.release(pendingEvent);
+ return true;
+ }
+
+ private static final class PendingKeyEvent {
+ /* Event and policyFlag provided in notifyKeyEventLocked */
+ KeyEvent event;
+ int policyFlags;
+ /*
+ * The referenceCount optimizes the process of determining the number of services
+ * still holding a KeyEvent. It must be equal to the number of times the PendingEvent
+ * appears in mPendingEventsMap, or PendingEvents will leak.
+ */
+ int referenceCount;
+ /* Whether or not at least one service had handled this event */
+ boolean handled;
+ }
+
+ private class Callback implements Handler.Callback {
+ @Override
+ public boolean handleMessage(Message message) {
+ if (message.what != MSG_ON_KEY_EVENT_TIMEOUT) {
+ throw new IllegalArgumentException("Unknown message: " + message.what);
+ }
+ PendingKeyEvent pendingKeyEvent = (PendingKeyEvent) message.obj;
+ synchronized (mLock) {
+ for (ArrayList<PendingKeyEvent> listForService : mPendingEventsMap.values()) {
+ if (listForService.remove(pendingKeyEvent)) {
+ if(removeReferenceToPendingEventLocked(pendingKeyEvent)) {
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 3fc6846..02cf87a 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1277,7 +1277,7 @@
}
final Configuration serviceConfig = mService.mConfiguration;
- mOverrideConfig = new Configuration(serviceConfig);
+ mOverrideConfig = new Configuration(Configuration.EMPTY);
// TODO(multidisplay): Update Dp to that of display stack is on.
final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
mOverrideConfig.screenWidthDp =
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4424838..946fbb1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1211,29 +1211,36 @@
}
@Override
- public void setPackagePriority(String pkg, int uid, int priority) {
+ public ParceledListSlice<Notification.Topic> getTopics(String pkg, int uid) {
checkCallerIsSystem();
- mRankingHelper.setPackagePriority(pkg, uid, priority);
+ return new ParceledListSlice<Notification.Topic>(mRankingHelper.getTopics(pkg, uid));
+ }
+
+ @Override
+ public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) {
+ checkCallerIsSystem();
+ mRankingHelper.setTopicPriority(pkg, uid, topic, priority);
savePolicyFile();
}
@Override
- public int getPackagePriority(String pkg, int uid) {
+ public int getTopicPriority(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getPackagePriority(pkg, uid);
+ return mRankingHelper.getTopicPriority(pkg, uid, topic);
}
@Override
- public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
+ public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic,
+ int visibility) {
checkCallerIsSystem();
- mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
+ mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility);
savePolicyFile();
}
@Override
- public int getPackageVisibilityOverride(String pkg, int uid) {
+ public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
+ return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
}
/**
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index aea137b..7ee29e4 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -15,12 +15,20 @@
*/
package com.android.server.notification;
+import android.app.Notification;
+
+import java.util.List;
+
public interface RankingConfig {
- int getPackagePriority(String packageName, int uid);
- void setPackagePriority(String packageName, int uid, int priority);
+ List<Notification.Topic> getTopics(String packageName, int uid);
- int getPackageVisibilityOverride(String packageName, int uid);
+ int getTopicPriority(String packageName, int uid, Notification.Topic topic);
- void setPackageVisibilityOverride(String packageName, int uid, int visibility);
+ void setTopicPriority(String packageName, int uid, Notification.Topic topic, int priority);
+
+ int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic);
+
+ void setTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic,
+ int visibility);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f8b661f..4d33248 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -27,6 +27,8 @@
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -35,6 +37,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
public class RankingHelper implements RankingConfig {
@@ -45,11 +49,14 @@
private static final String TAG_RANKING = "ranking";
private static final String TAG_PACKAGE = "package";
private static final String ATT_VERSION = "version";
+ private static final String TAG_TOPIC = "topic";
private static final String ATT_NAME = "name";
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
private static final String ATT_VISIBILITY = "visibility";
+ private static final String ATT_TOPIC_ID = "id";
+ private static final String ATT_TOPIC_LABEL = "label";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
private static final int DEFAULT_VISIBILITY =
@@ -161,12 +168,14 @@
} else {
r = getOrCreateRecord(name, uid);
}
- if (priority != DEFAULT_PRIORITY) {
- r.priority = priority;
- }
- if (vis != DEFAULT_VISIBILITY) {
- r.visibility = vis;
- }
+
+ // Migrate package level settings to the default topic.
+ // Might be overwritten by parseTopics.
+ Topic defaultTopic = r.topics.get(Notification.TOPIC_DEFAULT);
+ defaultTopic.priority = priority;
+ defaultTopic.visibility = vis;
+
+ parseTopics(r, parser);
}
}
}
@@ -174,6 +183,38 @@
throw new IllegalStateException("Failed to reach END_DOCUMENT");
}
+ public void parseTopics(Record r, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ final int innerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (TAG_TOPIC.equals(tagName)) {
+ int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
+ int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ String id = parser.getAttributeValue(null, ATT_TOPIC_ID);
+ CharSequence label = parser.getAttributeValue(null, ATT_TOPIC_LABEL);
+
+ if (!TextUtils.isEmpty(id)) {
+ Topic topic = new Topic(new Notification.Topic(id, label));
+
+ if (priority != DEFAULT_PRIORITY) {
+ topic.priority = priority;
+ }
+ if (vis != DEFAULT_VISIBILITY) {
+ topic.visibility = vis;
+ }
+ r.topics.put(id, topic);
+ }
+ }
+ }
+ }
+
private static String recordKey(String pkg, int uid) {
return pkg + "|" + uid;
}
@@ -185,21 +226,14 @@
r = new Record();
r.pkg = pkg;
r.uid = uid;
+ r.topics.put(Notification.TOPIC_DEFAULT,
+ new Topic(new Notification.Topic(Notification.TOPIC_DEFAULT,
+ mContext.getString(R.string.default_notification_topic_label))));
mRecords.put(key, r);
}
return r;
}
- private void removeDefaultRecords() {
- final int N = mRecords.size();
- for (int i = N - 1; i >= 0; i--) {
- final Record r = mRecords.valueAt(i);
- if (r.priority == DEFAULT_PRIORITY && r.visibility == DEFAULT_VISIBILITY) {
- mRecords.remove(i);
- }
- }
- }
-
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
out.startTag(null, TAG_RANKING);
out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
@@ -213,20 +247,32 @@
}
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
- if (r.priority != DEFAULT_PRIORITY) {
- out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
- }
+
if (!forBackup) {
out.attribute(null, ATT_UID, Integer.toString(r.uid));
}
+
+ writeTopicsXml(out, r);
out.endTag(null, TAG_PACKAGE);
}
out.endTag(null, TAG_RANKING);
}
+ public void writeTopicsXml(XmlSerializer out, Record r) throws IOException {
+ for (Topic t : r.topics.values()) {
+ out.startTag(null, TAG_TOPIC);
+ out.attribute(null, ATT_TOPIC_ID, t.topic.getId());
+ out.attribute(null, ATT_TOPIC_LABEL, t.topic.getLabel().toString());
+ if (t.priority != DEFAULT_PRIORITY) {
+ out.attribute(null, ATT_PRIORITY, Integer.toString(t.priority));
+ }
+ if (t.visibility != DEFAULT_VISIBILITY) {
+ out.attribute(null, ATT_VISIBILITY, Integer.toString(t.visibility));
+ }
+ out.endTag(null, TAG_TOPIC);
+ }
+ }
+
private void updateConfig() {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
@@ -322,37 +368,54 @@
}
@Override
- public int getPackagePriority(String packageName, int uid) {
- final Record r = mRecords.get(recordKey(packageName, uid));
- return r != null ? r.priority : DEFAULT_PRIORITY;
+ public List<Notification.Topic> getTopics(String packageName, int uid) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ List<Notification.Topic> topics = new ArrayList<>();
+ for (Topic t : r.topics.values()) {
+ topics.add(t.topic);
+ }
+ return topics;
}
@Override
- public void setPackagePriority(String packageName, int uid, int priority) {
- if (priority == getPackagePriority(packageName, uid)) {
- return;
- }
- getOrCreateRecord(packageName, uid).priority = priority;
- removeDefaultRecords();
+ public int getTopicPriority(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ return getOrCreateTopic(r, topic).priority;
+ }
+
+ @Override
+ public void setTopicPriority(String packageName, int uid, Notification.Topic topic,
+ int priority) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ getOrCreateTopic(r, topic).priority = priority;
updateConfig();
}
@Override
- public int getPackageVisibilityOverride(String packageName, int uid) {
- final Record r = mRecords.get(recordKey(packageName, uid));
- return r != null ? r.visibility : DEFAULT_VISIBILITY;
+ public int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ return getOrCreateTopic(r, topic).visibility;
}
@Override
- public void setPackageVisibilityOverride(String packageName, int uid, int visibility) {
- if (visibility == getPackageVisibilityOverride(packageName, uid)) {
- return;
- }
- getOrCreateRecord(packageName, uid).visibility = visibility;
- removeDefaultRecords();
+ public void setTopicVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
+ int visibility) {
+ final Record r = getOrCreateRecord(pkgName, uid);
+ getOrCreateTopic(r, topic).visibility = visibility;
updateConfig();
}
+ private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
+ Topic t = r.topics.get(topic.getId());
+ if (t != null) {
+ return t;
+ } else {
+ t = new Topic(topic);
+ r.topics.put(topic.getId(), t);
+ return t;
+ }
+ }
+
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
if (filter == null) {
final int N = mSignalExtractors.length;
@@ -385,15 +448,22 @@
pw.print(" (");
pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
- if (r.priority != DEFAULT_PRIORITY) {
- pw.print(" priority=");
- pw.print(Notification.priorityToString(r.priority));
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- pw.print(" visibility=");
- pw.print(Notification.visibilityToString(r.visibility));
- }
pw.println();
+ for (Topic t : r.topics.values()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(" ");
+ pw.print(t.topic.getId());
+ if (t.priority != DEFAULT_PRIORITY) {
+ pw.print(" priority=");
+ pw.print(Notification.priorityToString(t.priority));
+ }
+ if (t.visibility != DEFAULT_VISIBILITY) {
+ pw.print(" visibility=");
+ pw.print(Notification.visibilityToString(t.visibility));
+ }
+ pw.println();
+ }
}
}
}
@@ -429,8 +499,16 @@
String pkg;
int uid = UNKNOWN_UID;
+ Map<String, Topic> topics = new ArrayMap<>();
+ }
+
+ private static class Topic {
+ Notification.Topic topic;
int priority = DEFAULT_PRIORITY;
int visibility = DEFAULT_VISIBILITY;
- }
+ public Topic(Notification.Topic topic) {
+ this.topic = topic;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
similarity index 79%
rename from services/core/java/com/android/server/notification/PackagePriorityExtractor.java
rename to services/core/java/com/android/server/notification/TopicPriorityExtractor.java
index 6beed9c..5bf989ae 100644
--- a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
@@ -18,8 +18,11 @@
import android.content.Context;
import android.util.Slog;
-public class PackagePriorityExtractor implements NotificationSignalExtractor {
- private static final String TAG = "ImportantPackageExtractor";
+/**
+ * Determines if the given notification can bypass Do Not Disturb.
+ */
+public class TopicPriorityExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "ImportantTopicExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
@@ -39,8 +42,8 @@
return null;
}
- final int packagePriority = mConfig.getPackagePriority(
- record.sbn.getPackageName(), record.sbn.getUid());
+ final int packagePriority = mConfig.getTopicPriority(record.sbn.getPackageName(),
+ record.sbn.getUid(), record.sbn.getNotification().getTopic());
record.setPackagePriority(packagePriority);
return null;
diff --git a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
similarity index 80%
rename from services/core/java/com/android/server/notification/PackageVisibilityExtractor.java
rename to services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
index af99db7..e053382 100644
--- a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
@@ -18,8 +18,11 @@
import android.content.Context;
import android.util.Slog;
-public class PackageVisibilityExtractor implements NotificationSignalExtractor {
- private static final String TAG = "PackageVisibilityExtractor";
+/**
+ * Determines if the given notification can display sensitive content on the lockscreen.
+ */
+public class TopicVisibilityExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "TopicVisibilityExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
@@ -39,8 +42,9 @@
return null;
}
- final int packageVisibility = mConfig.getPackageVisibilityOverride(
- record.sbn.getPackageName(), record.sbn.getUid());
+ final int packageVisibility = mConfig.getTopicVisibilityOverride(
+ record.sbn.getPackageName(), record.sbn.getUid(),
+ record.sbn.getNotification().getTopic());
record.setPackageVisibilityOverride(packageVisibility);
return null;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8085f13..04ab544 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -515,10 +515,11 @@
for (int i = 0; i < count; i++) {
final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
final int otherStackId = otherStack.mStackId;
- if (StackId.isResizeableByDockedStack(otherStackId)) {
+ if (StackId.isResizeableByDockedStack(otherStackId)
+ && !otherStack.mBounds.equals(bounds)) {
mService.mH.sendMessage(
mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
- 1 /*allowResizeInDockedMode*/, bounds));
+ 1 /*allowResizeInDockedMode*/, fullscreen ? null : bounds));
}
}
}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 67b9d77..8eb30d2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -160,6 +160,36 @@
}
},
+ new Test("with topic Hello") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("hihi")
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent2())
+ .setTopic(new Notification.Topic("hello", "Hello"))
+ .build();
+
+ mNM.notify(999, n);
+ }
+ },
+
+ new Test("with topic GoodBye") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("byebye")
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent2())
+ .setTopic(new Notification.Topic("bye", "Goodbye"))
+ .build();
+
+ mNM.notify(9999, n);
+ }
+ },
+
new Test("Whens") {
public void run()
{