Merge "PIP: Improve PIP control row's focus change animation in Recents" into nyc-dev
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index c285acb..ce7a124 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1021,9 +1021,19 @@
}
/**
- * Returns True if the device supports Sustained Performance Mode.
- * Applications Should check if the device supports this mode, before
- * using {@link android.view.Window#setSustainedPerformanceMode}.
+ * This function checks if the device has implemented Sustained Performance
+ * Mode. This needs to be checked only once and is constant for a particular
+ * device/release.
+ *
+ * Sustained Performance Mode is intended to provide a consistent level of
+ * performance for prolonged amount of time.
+ *
+ * Applications should check if the device supports this mode, before using
+ * {@link android.view.Window#setSustainedPerformanceMode}.
+ *
+ * @return Returns True if the device supports it, false otherwise.
+ *
+ * @see android.view.Window#setSustainedPerformanceMode
*/
public boolean isSustainedPerformanceModeSupported() {
return mContext.getResources().getBoolean(
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 93dc625..8b02352 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -8153,14 +8153,17 @@
}
private void resetAllStatsLocked() {
+ final long uptimeMillis = mClocks.uptimeMillis();
+ final long elapsedRealtimeMillis = mClocks.elapsedRealtime();
mStartCount = 0;
- initTimes(mClocks.uptimeMillis() * 1000, mClocks.elapsedRealtime() * 1000);
+ initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
mScreenOnTimer.reset(false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false);
}
mInteractiveTimer.reset(false);
mPowerSaveModeEnabledTimer.reset(false);
+ mLastIdleTimeStart = elapsedRealtimeMillis;
mLongestLightIdleTime = 0;
mLongestFullIdleTime = 0;
mDeviceIdleModeLightTimer.reset(false);
diff --git a/core/res/res/layout/preference_material.xml b/core/res/res/layout/preference_material.xml
index bf0fda9..871b99c 100644
--- a/core/res/res/layout/preference_material.xml
+++ b/core/res/res/layout/preference_material.xml
@@ -67,7 +67,8 @@
android:layout_alignStart="@id/title"
android:textAppearance="?attr/textAppearanceListItemSecondary"
android:textColor="?attr/textColorSecondary"
- android:maxLines="10" />
+ android:maxLines="10"
+ android:ellipsize="end" />
</RelativeLayout>
diff --git a/docs/html/wear/preview/features/ui-nav-actions.jd b/docs/html/wear/preview/features/ui-nav-actions.jd
index f5e2fc4..1ba275f 100644
--- a/docs/html/wear/preview/features/ui-nav-actions.jd
+++ b/docs/html/wear/preview/features/ui-nav-actions.jd
@@ -76,6 +76,7 @@
<android.support.wearable.view.drawer.WearableDrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:deviceIds="wear">
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
index b4f1299..b8f8fba 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
@@ -308,7 +308,9 @@
String id, DocumentStack stack, List<DocumentInfo> srcs,
DocumentInfo srcParent) {
assert(!srcs.isEmpty());
- assert(stack.peek().isDirectory()); // we can't currently delete from archives.
+ // stack is empty if we delete docs from recent.
+ // we can't currently delete from archives.
+ assert(stack.isEmpty() || stack.peek().isDirectory());
return new DeleteJob(service, appContext, listener, id, stack, srcs, srcParent);
}
}
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 32839d8..63390e2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -44,7 +44,7 @@
android:layout_height="wrap_content"
/>
- <ScrollView
+ <com.android.systemui.qs.NonInterceptingScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
@@ -52,7 +52,7 @@
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- </ScrollView>
+ </com.android.systemui.qs.NonInterceptingScrollView>
<include layout="@layout/qs_detail_buttons" />
diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
new file mode 100644
index 0000000..15e1c96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.systemui.qs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.ScrollView;
+
+/**
+ * ScrollView that disallows intercepting for touches that can cause scrolling.
+ */
+public class NonInterceptingScrollView extends ScrollView {
+
+ private float mInitialY;
+
+ public NonInterceptingScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mInitialY = ev.getY();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ requestDisallowInterceptTouchEvent(false);
+ break;
+ default:
+ if (canScrollVertically(ev.getY() > mInitialY ? -1 : 1)) {
+ requestDisallowInterceptTouchEvent(true);
+ }
+ break;
+ }
+ return super.onTouchEvent(ev);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index f05aa3c..feacaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -154,9 +154,9 @@
event.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
String label = getResources()
- .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
event.setContentDescription(label);
- event.setChecked(mTileState);
+ event.setChecked(!mTileState);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index cfc6c52..b20f46f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -372,7 +372,7 @@
if (stack.getTaskCount() > 0) {
// Only preload the icon (but not the thumbnail since it may not have been taken for
// the pausing activity)
- preloadIcon(runningTask);
+ preloadIcon(runningTask.id);
// At this point, we don't know anything about the stack state. So only calculate
// the dimensions of the thumbnail that we need for the transition into Recents, but
@@ -664,10 +664,10 @@
/**
* Preloads the icon of a task.
*/
- private void preloadIcon(ActivityManager.RunningTaskInfo task) {
+ private void preloadIcon(int runningTaskId) {
// Ensure that we load the running task's icon
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.runningTaskId = task.id;
+ launchOpts.runningTaskId = runningTaskId;
launchOpts.loadThumbnails = false;
launchOpts.onlyLoadForCache = true;
Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
@@ -699,7 +699,7 @@
private ActivityOptions getThumbnailTransitionActivityOptions(
ActivityManager.RunningTaskInfo runningTask, TaskStackView stackView,
Rect windowOverrideRect) {
- if (runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
ArrayList<Task> tasks = stackView.getStack().getStackTasks();
TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
@@ -810,6 +810,10 @@
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
+ ? runningTask.id
+ : -1;
+
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
// the stacks might have changed.
@@ -818,7 +822,7 @@
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible);
+ loader.preloadTasks(sInstanceLoadPlan, runningTaskId, !isHomeStackVisible);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
@@ -830,12 +834,12 @@
launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
launchState.launchedViaDockGesture = mLaunchedWhileDocking;
launchState.launchedViaDragGesture = mDraggingInRecents;
- launchState.launchedToTaskId = (runningTask != null) ? runningTask.id : -1;
+ launchState.launchedToTaskId = runningTaskId;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
// Preload the icon (this will be a null-op if we have preloaded the icon already in
// preloadRecents())
- preloadIcon(runningTask);
+ preloadIcon(runningTaskId);
// Update the header bar if necessary
Rect windowOverrideRect = getWindowRectOverride(growTarget);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 6f6b02d..88c1503 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1491,6 +1491,14 @@
mLoggingKey = key;
}
+ public void onExpandedByGesture(boolean userExpanded) {
+ int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
+ if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
+ event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
+ }
+ MetricsLogger.action(mContext, event, userExpanded);
+ }
+
@Override
public float getIncreasedPaddingAmount() {
if (mIsSummaryWithChildren) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index bc33b30..cdfdad4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -25,6 +26,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -325,8 +327,20 @@
public static String contentDescForNotification(Context c, Notification n) {
- Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
- String appName = builder.loadHeaderAppName();
+ String appName = "";
+ try {
+ Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
+ appName = builder.loadHeaderAppName();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unable to recover builder", e);
+ // Trying to get the app name from the app info instead.
+ Parcelable appInfo = n.extras.getParcelable(
+ Notification.EXTRA_BUILDER_APPLICATION_INFO);
+ if (appInfo instanceof ApplicationInfo) {
+ appName = String.valueOf(((ApplicationInfo) appInfo).loadLabel(
+ c.getPackageManager()));
+ }
+ }
CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
CharSequence ticker = n.tickerText;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 31fa3bf..9208134 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4339,6 +4339,8 @@
// ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
+
+ /* Only ever called as a consequence of a lockscreen expansion gesture. */
@Override
public boolean onDraggedDown(View startingChild, int dragLengthY) {
if (hasActiveNotifications()) {
@@ -4349,6 +4351,10 @@
// We have notifications, go to locked shade.
goToLockedShade(startingChild);
+ if (startingChild instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
+ row.onExpandedByGesture(true /* drag down is always an open */);
+ }
return true;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 19f4074..cab2534 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -959,11 +959,13 @@
&& (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
}
+ /* Only ever called as a consequence of an expansion gesture in the shade. */
@Override
public void setUserExpandedChild(View v, boolean userExpanded) {
if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserExpanded(userExpanded,
- true /* allowChildrenExpansion */);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
+ row.onExpandedByGesture(userExpanded);
}
}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index f0ca441..badc31e 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -25,6 +25,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.util.Log;
@@ -55,6 +56,10 @@
finish();
return;
}
+ if (UserManager.get(this).hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ finish();
+ return;
+ }
View view = View.inflate(this, R.layout.confirm, null);
((TextView) view.findViewById(R.id.warning)).setText(
Html.fromHtml(getString(R.string.warning, getVpnLabel()),
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f8cdf9b..a1487e3 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2212,6 +2212,12 @@
// Notification group expansion state toggled by the expand affordance.
ACTION_NOTIFICATION_GROUP_EXPANDER = 408;
+ // Notification expansion state toggled by the expand gesture.
+ ACTION_NOTIFICATION_GESTURE_EXPANDER = 409;
+
+ // Notification group expansion state toggled by the expand gesture.
+ ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER = 410;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 95dee01..c24123a 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -753,7 +753,7 @@
public void updateServiceConnectionActivitiesLocked(ProcessRecord clientProc) {
ArraySet<ProcessRecord> updatedProcesses = null;
- for (int i=0; i<clientProc.connections.size(); i++) {
+ for (int i = 0; i < clientProc.connections.size(); i++) {
final ConnectionRecord conn = clientProc.connections.valueAt(i);
final ProcessRecord proc = conn.binding.service.app;
if (proc == null || proc == clientProc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eb4391e..7c82829 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -266,6 +266,7 @@
import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -1344,6 +1345,7 @@
boolean mSupportsMultiWindow;
boolean mSupportsFreeformWindowManagement;
boolean mSupportsPictureInPicture;
+ boolean mSupportsLeanbackOnly;
Rect mDefaultPinnedStackBounds;
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
@@ -12813,6 +12815,9 @@
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+ final boolean supportsLeanbackOnly =
+ mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
+
// Transfer any global setting for forcing RTL layout, into a System Property
SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
@@ -12828,6 +12833,7 @@
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
mLenientBackgroundCheck = lenientBackgroundCheck;
+ mSupportsLeanbackOnly = supportsLeanbackOnly;
mForceResizableActivities = forceResizable;
mWindowManager.setForceResizableTasks(mForceResizableActivities);
if (supportsMultiWindow || forceResizable) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8b637e0..ba044cc4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1832,9 +1832,9 @@
boolean isVisible =
!behindFullscreenActivity || r.mLaunchTaskBehind || activityVisibleBehind;
- if (isVisible && r.isRecentsActivity()) {
- // Recents activity can only be visible if the home stack is the focused stack or we are
- // in split-screen mode.
+ if (mService.mSupportsLeanbackOnly && isVisible && r.isRecentsActivity()) {
+ // On devices that support leanback only (Android TV), Recents activity can only be
+ // visible if the home stack is the focused stack or we are in split-screen mode.
isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
|| mStackSupervisor.isFocusedStack(this);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 01b2eb4..197d9ec 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1338,7 +1338,9 @@
// Update any services we are bound to that might care about whether
// their client may have activities.
- mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
+ if (r.app != null) {
+ mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
+ }
return true;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index dd9baf4..1698ab5 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -555,8 +555,7 @@
try {
// Restricted users are not allowed to create VPNs, they are tied to Owner
UserInfo user = mgr.getUserInfo(mUserHandle);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
- new UserHandle(mUserHandle))) {
+ if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c7148c1..695ed36 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2834,6 +2834,8 @@
}
win.mRelayoutCalled = true;
+ win.mInRelayout = true;
+
final int oldVisibility = win.mViewVisibility;
win.mViewVisibility = viewVisibility;
if (DEBUG_SCREEN_ON) {
@@ -2974,6 +2976,7 @@
if (DEBUG_LAYOUT) {
Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
}
+ win.mInRelayout = false;
}
if (configChanged) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index be27c82..ff95afc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -362,6 +362,8 @@
*/
boolean mRelayoutCalled;
+ boolean mInRelayout;
+
/**
* If the application has called relayout() with changes that can
* impact its window's size, we need to perform a layout pass on it
@@ -1630,6 +1632,12 @@
return task != null && task.inDockedWorkspace();
}
+ // TODO: Strange usage of word workspace here and above.
+ boolean inPinnedWorkspace() {
+ final Task task = getTask();
+ return task != null && task.inPinnedWorkspace();
+ }
+
boolean isDockedInEffect() {
final Task task = getTask();
return task != null && task.isDockedInEffect();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 42eddd5..9dae0f2 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1428,8 +1428,24 @@
float extraHScale = (float) 1.0;
float extraVScale = (float) 1.0;
- mSurfaceResized = mSurfaceController.setSizeInTransaction(
- mTmpSize.width(), mTmpSize.height(), recoveringMemory);
+ // Once relayout has been called at least once, we need to make sure
+ // we only resize the client surface during calls to relayout. For
+ // clients which use indeterminate measure specs (MATCH_PARENT),
+ // we may try and change their window size without a call to relayout.
+ // However, this would be unsafe, as the client may be in the middle
+ // of producing a frame at the old size, having just completed layout
+ // to find the surface size changed underneath it.
+ //
+ // TODO: For N we only apply this fix to the pinned workspace. As we
+ // aren't observing known issues here outside of PiP resizing. (Typically
+ // the other windows that use -1 are PopupWindows which aren't likely
+ // to be rendering while we resize).
+ if (!w.inPinnedWorkspace() || (!w.mRelayoutCalled || w.mInRelayout)) {
+ mSurfaceResized = mSurfaceController.setSizeInTransaction(
+ mTmpSize.width(), mTmpSize.height(), recoveringMemory);
+ } else {
+ mSurfaceResized = false;
+ }
mForceScaleUntilResize = mForceScaleUntilResize && !mSurfaceResized;
@@ -1437,10 +1453,12 @@
if ((task != null && task.mStack.getForceScaleToCrop()) || mForceScaleUntilResize) {
int hInsets = w.getAttrs().surfaceInsets.left + w.getAttrs().surfaceInsets.right;
int vInsets = w.getAttrs().surfaceInsets.top + w.getAttrs().surfaceInsets.bottom;
+ float surfaceWidth = mSurfaceController.getWidth();
+ float surfaceHeight = mSurfaceController.getHeight();
// We want to calculate the scaling based on the content area, not based on
// the entire surface, so that we scale in sync with windows that don't have insets.
- extraHScale = (mTmpClipRect.width() - hInsets) / (float)(mTmpSize.width() - hInsets);
- extraVScale = (mTmpClipRect.height() - vInsets) / (float)(mTmpSize.height() - vInsets);
+ extraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
+ extraVScale = (mTmpClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets);
// In the case of ForceScaleToCrop we scale entire tasks together,
// and so we need to scale our offsets relative to the task bounds
@@ -1462,7 +1480,7 @@
// Since we are scaled to fit in our previously desired crop, we can now
// expose the whole window in buffer space, and not risk extending
// past where the system would have cropped us
- mTmpClipRect.set(0, 0, mTmpSize.width(), mTmpSize.height());
+ mTmpClipRect.set(0, 0, (int)surfaceWidth, (int)surfaceHeight);
mTmpFinalClipRect.setEmpty();
// Various surfaces in the scaled stack may resize at different times.
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index af47369..9646a49 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -454,6 +454,15 @@
return mSurfaceY;
}
+ float getWidth() {
+ return mSurfaceW;
+ }
+
+ float getHeight() {
+ return mSurfaceH;
+ }
+
+
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
if (dumpAll) {
pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 538e8f8..485f2f5 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -597,12 +597,13 @@
private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv4 filter program does:
//
- // if it's multicast and we're dropping multicast:
- // drop
- // if it's not broadcast:
- // pass
- // if it's not DHCP destined to our MAC:
- // drop
+ // if filtering multicast (i.e. multicast lock not held):
+ // if it's multicast:
+ // drop
+ // if it's not broadcast:
+ // pass
+ // if it's not DHCP destined to our MAC:
+ // drop
// pass
if (mMulticastFilter) {
@@ -610,27 +611,27 @@
gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
gen.addAnd(0xf0);
gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
- }
- // Drop all broadcasts besides DHCP addressed to us
- // If not a broadcast packet, pass
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
- // If not UDP, drop
- gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
- // If fragment, drop. This matches the BPF filter installed by the DHCP client.
- gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
- gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
- // If not to DHCP client port, drop
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
- gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
- // If not DHCP to our MAC address, drop
- gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
- // NOTE: Relies on R1 containing IPv4 header offset.
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+ // Drop all broadcasts besides DHCP addressed to us
+ // If not a broadcast packet, pass
+ gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+ // If not UDP, drop
+ gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+ gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
+ // If fragment, drop. This matches the BPF filter installed by the DHCP client.
+ gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
+ gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
+ // If not to DHCP client port, drop
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
+ // If not DHCP to our MAC address, drop
+ gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
+ // NOTE: Relies on R1 containing IPv4 header offset.
+ gen.addAddR1();
+ gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+ }
// Otherwise, pass
gen.addJump(gen.PASS_LABEL);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 99b10c3..96c852b 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -84,7 +84,7 @@
public class DhcpClient extends StateMachine {
private static final String TAG = "DhcpClient";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final boolean STATE_DBG = false;
private static final boolean MSG_DBG = false;
private static final boolean PACKET_DBG = false;
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index 9e04d23..fae82ca 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -663,7 +663,7 @@
@LargeTest
public void testApfFilterIPv4() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
@@ -726,46 +726,57 @@
byte[] program = ipManagerCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
- ByteBuffer v4packet = ByteBuffer.wrap(new byte[100]);
- v4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- v4packet.position(IPV4_DEST_ADDR_OFFSET);
- v4packet.put(new byte[]{(byte)224,0,0,1});
+ ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
+ mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ mcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
+ mcastv4packet.put(new byte[]{(byte)224,0,0,1});
- ByteBuffer v6packet = ByteBuffer.wrap(new byte[100]);
- v6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
- v6packet.position(IPV6_DEST_ADDR_OFFSET);
- v6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+ ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
+ mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
+ mcastv6packet.position(IPV6_DEST_ADDR_OFFSET);
+ mcastv6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+
+ // Construct IPv4 broadcast packet.
+ ByteBuffer bcastv4packet = ByteBuffer.wrap(new byte[100]);
+ bcastv4packet.put(ETH_BROADCAST_MAC_ADDRESS);
+ bcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ bcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
+ bcastv4packet.put(new byte[]{(byte)192,(byte)0,(byte)2,(byte)63});
// Verify initially disabled multicast filter is off
- assertPass(program, v4packet.array(), 0);
- assertPass(program, v6packet.array(), 0);
+ assertPass(program, bcastv4packet.array(), 0);
+ assertPass(program, mcastv4packet.array(), 0);
+ assertPass(program, mcastv6packet.array(), 0);
// Turn on multicast filter and verify it works
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, v4packet.array(), 0);
- assertDrop(program, v6packet.array(), 0);
+ assertDrop(program, bcastv4packet.array(), 0);
+ assertDrop(program, mcastv4packet.array(), 0);
+ assertDrop(program, mcastv6packet.array(), 0);
// Turn off multicast filter and verify it's off
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
program = ipManagerCallback.getApfProgram();
- assertPass(program, v4packet.array(), 0);
- assertPass(program, v6packet.array(), 0);
+ assertPass(program, bcastv4packet.array(), 0);
+ assertPass(program, mcastv4packet.array(), 0);
+ assertPass(program, mcastv6packet.array(), 0);
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, v4packet.array(), 0);
- assertDrop(program, v6packet.array(), 0);
+ assertDrop(program, bcastv4packet.array(), 0);
+ assertDrop(program, mcastv4packet.array(), 0);
+ assertDrop(program, mcastv6packet.array(), 0);
// Verify that ICMPv6 multicast is not dropped.
- v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, v6packet.array(), 0);
+ mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+ assertPass(program, mcastv6packet.array(), 0);
apfFilter.shutdown();
}