summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java35
-rw-r--r--core/java/android/app/ActivityManagerInternal.java5
-rw-r--r--core/java/android/app/ActivityOptions.java2
-rw-r--r--core/java/android/content/Context.java18
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/os/Handler.java20
-rw-r--r--core/java/android/os/Looper.java15
-rw-r--r--core/java/android/os/MessageQueue.java23
-rwxr-xr-xcore/java/android/provider/Settings.java15
-rw-r--r--core/java/android/service/autofill/FillContext.java32
-rw-r--r--core/java/android/service/autofill/SaveInfo.java34
-rw-r--r--core/java/android/view/Surface.java39
-rw-r--r--core/java/android/view/SurfaceView.java10
-rw-r--r--core/java/android/view/ThreadedRenderer.java3
-rw-r--r--core/java/android/view/View.java87
-rw-r--r--core/java/android/view/ViewGroup.java21
-rw-r--r--core/java/android/view/ViewRootImpl.java5
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java10
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/view/autofill/AutofillManager.java16
-rw-r--r--core/java/android/widget/DayPickerView.java19
-rw-r--r--core/java/android/widget/Editor.java18
-rw-r--r--core/java/android/widget/RemoteViews.java30
-rw-r--r--core/jni/android_view_Surface.cpp12
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp5
-rw-r--r--core/res/res/values-da/strings.xml8
-rw-r--r--core/res/res/values-de/strings.xml2
-rw-r--r--libs/hwui/Properties.cpp1
-rw-r--r--libs/hwui/Properties.h6
-rw-r--r--libs/hwui/renderthread/EglManager.cpp3
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp5
-rw-r--r--libs/hwui/renderthread/RenderProxy.h2
-rw-r--r--media/jni/android_media_ImageReader.cpp1
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java124
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java23
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java17
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java33
-rw-r--r--packages/SystemUI/res/layout/tv_pip_controls.xml10
-rw-r--r--packages/SystemUI/res/layout/tv_pip_custom_control.xml23
-rw-r--r--packages/SystemUI/res/values-tvdpi/dimens.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/CellTileView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java120
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java100
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java12
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java168
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java57
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/ActivityMetricsLogger.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java18
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java7
-rw-r--r--services/core/java/com/android/server/am/UserState.java2
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayWindow.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java60
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java6
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java3
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java10
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java20
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java18
-rw-r--r--services/core/java/com/android/server/pm/Settings.java4
-rw-r--r--services/core/java/com/android/server/policy/BarController.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java4
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java5
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java92
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerListener.java4
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java44
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java27
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java40
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java74
-rw-r--r--services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java78
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java3
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java3
97 files changed, 1617 insertions, 561 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3574f8d05055..24c144c89ad2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -719,7 +719,7 @@ public class Activity extends ContextThemeWrapper
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
- private static final String LAST_ACCESSIBILITY_ID = "android:lastAccessibilityId";
+ private static final String LAST_AUTOFILL_ID = "android:lastAutofillId";
private static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded";
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
@@ -853,8 +853,8 @@ public class Activity extends ContextThemeWrapper
private boolean mAutoFillResetNeeded;
- /** The last accessibility id that was returned from {@link #getNextAccessibilityId()} */
- private int mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID;
+ /** The last autofill id that was returned from {@link #getNextAutofillId()} */
+ private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
private AutofillPopupWindow mAutofillPopupWindow;
@@ -999,7 +999,7 @@ public class Activity extends ContextThemeWrapper
}
if (savedInstanceState != null) {
mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
- mLastAccessibilityId = savedInstanceState.getInt(LAST_ACCESSIBILITY_ID, View.NO_ID);
+ mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID, View.NO_ID);
if (mAutoFillResetNeeded) {
getAutofillManager().onCreate(savedInstanceState);
@@ -1348,24 +1348,23 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Gets the next accessibility ID.
+ * Gets the next autofill ID.
*
- * <p>All IDs will be bigger than {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs returned
+ * <p>All IDs will be bigger than {@link View#LAST_APP_AUTOFILL_ID}. All IDs returned
* will be unique.
*
* @return A ID that is unique in the activity
*
* {@hide}
*/
- @Override
- public int getNextAccessibilityId() {
- if (mLastAccessibilityId == Integer.MAX_VALUE - 1) {
- mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID;
+ public int getNextAutofillId() {
+ if (mLastAutofillId == Integer.MAX_VALUE - 1) {
+ mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
}
- mLastAccessibilityId++;
+ mLastAutofillId++;
- return mLastAccessibilityId;
+ return mLastAutofillId;
}
/**
@@ -1563,7 +1562,7 @@ public class Activity extends ContextThemeWrapper
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
- outState.putInt(LAST_ACCESSIBILITY_ID, mLastAccessibilityId);
+ outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
@@ -7455,7 +7454,7 @@ public class Activity extends ContextThemeWrapper
/** @hide */
@Override
- @NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) {
+ @NonNull public View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds) {
final View[] views = new View[viewIds.length];
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
@@ -7466,7 +7465,7 @@ public class Activity extends ContextThemeWrapper
if (rootView != null) {
for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
if (views[viewNum] == null) {
- views[viewNum] = rootView.findViewByAccessibilityIdTraversal(
+ views[viewNum] = rootView.findViewByAutofillIdTraversal(
viewIds[viewNum]);
}
}
@@ -7478,14 +7477,14 @@ public class Activity extends ContextThemeWrapper
/** @hide */
@Override
- @Nullable public View findViewByAccessibilityIdTraversal(int viewId) {
+ @Nullable public View findViewByAutofillIdTraversal(int viewId) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
final View rootView = roots.get(rootNum).getView();
if (rootView != null) {
- final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
+ final View view = rootView.findViewByAutofillIdTraversal(viewId);
if (view != null) {
return view;
}
@@ -7499,7 +7498,7 @@ public class Activity extends ContextThemeWrapper
@Override
@NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
final boolean[] isVisible = new boolean[viewIds.length];
- final View views[] = findViewsByAccessibilityIdTraversal(viewIds);
+ final View views[] = findViewsByAutofillIdTraversal(viewIds);
for (int i = 0; i < viewIds.length; i++) {
View view = views[i];
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d3b4b403e1ac..e5fe2402dae1 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
import android.util.SparseIntArray;
@@ -134,8 +135,10 @@ public abstract class ActivityManagerInternal {
*
* @param reasons A map from stack id to a reason integer why the transition was started,, which
* must be one of the APP_TRANSITION_* values.
+ * @param timestamp The time at which the app transition started in
+ * {@link SystemClock#uptimeMillis()} timebase.
*/
- public abstract void notifyAppTransitionStarting(SparseIntArray reasons);
+ public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
/**
* Callback for window manager to let activity manager know that the app transition was
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index cbb93a059613..6dead3e6c0dc 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1244,7 +1244,7 @@ public class ActivityOptions {
// Once we parcel the thumbnail for transfering over to the system, create a copy of
// the bitmap to a hardware bitmap and pass through the GraphicBuffer
if (mThumbnail != null) {
- final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, true /* immutable */);
+ final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, false /* isMutable */);
if (hwBitmap != null) {
b.putParcelable(KEY_ANIM_THUMBNAIL, hwBitmap.createGraphicBufferHandle());
} else {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index db80c7259179..2303a3839789 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -488,27 +488,27 @@ public abstract class Context {
*/
public abstract Context getApplicationContext();
- /** Non-activity related accessibility ids are unique in the app */
- private static int sLastAccessibilityId = View.NO_ID;
+ /** Non-activity related autofill ids are unique in the app */
+ private static int sLastAutofillId = View.NO_ID;
/**
- * Gets the next accessibility ID.
+ * Gets the next autofill ID.
*
- * <p>All IDs will be smaller or the same as {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs
+ * <p>All IDs will be smaller or the same as {@link View#LAST_APP_AUTOFILL_ID}. All IDs
* returned will be unique.
*
* @return A ID that is unique in the process
*
* {@hide}
*/
- public int getNextAccessibilityId() {
- if (sLastAccessibilityId == View.LAST_APP_ACCESSIBILITY_ID - 1) {
- sLastAccessibilityId = View.NO_ID;
+ public int getNextAutofillId() {
+ if (sLastAutofillId == View.LAST_APP_AUTOFILL_ID - 1) {
+ sLastAutofillId = View.NO_ID;
}
- sLastAccessibilityId++;
+ sLastAutofillId++;
- return sLastAccessibilityId;
+ return sLastAutofillId;
}
/**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e127ca319bd8..3b27905d2c11 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -956,8 +956,7 @@ public class ContextWrapper extends Context {
/**
* @hide
*/
- @Override
- public int getNextAccessibilityId() {
- return mBase.getNextAccessibilityId();
+ public int getNextAutofillId() {
+ return mBase.getNextAutofillId();
}
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 8678d95db17d..b69a23aa5854 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -696,6 +696,14 @@ public class Handler {
}
/**
+ * Return whether there are any messages or callbacks currently scheduled on this handler.
+ * @hide
+ */
+ public final boolean hasMessagesOrCallbacks() {
+ return mQueue.hasMessages(this);
+ }
+
+ /**
* Check if there are any pending posts of messages with code 'what' and
* whose obj is 'object' in the message queue.
*/
@@ -728,6 +736,18 @@ public class Handler {
}
}
+ /**
+ * @hide
+ */
+ public final void dumpMine(Printer pw, String prefix) {
+ pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
+ if (mLooper == null) {
+ pw.println(prefix + "looper uninitialized");
+ } else {
+ mLooper.dump(pw, prefix + " ", this);
+ }
+ }
+
@Override
public String toString() {
return "Handler (" + getClass().getName() + ") {"
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 44dbcfb09582..04cceb8e80ba 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -310,7 +310,20 @@ public final class Looper {
*/
public void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + toString());
- mQueue.dump(pw, prefix + " ");
+ mQueue.dump(pw, prefix + " ", null);
+ }
+
+ /**
+ * Dumps the state of the looper for debugging purposes.
+ *
+ * @param pw A printer to receive the contents of the dump.
+ * @param prefix A prefix to prepend to each line which is printed.
+ * @param handler Only dump messages for this Handler.
+ * @hide
+ */
+ public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
+ pw.println(prefix + toString());
+ mQueue.dump(pw, prefix + " ", handler);
}
/** @hide */
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 2a8c52e92c60..624e28a67ae6 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -620,6 +620,23 @@ public final class MessageQueue {
}
}
+ boolean hasMessages(Handler h) {
+ if (h == null) {
+ return false;
+ }
+
+ synchronized (this) {
+ Message p = mMessages;
+ while (p != null) {
+ if (p.target == h) {
+ return true;
+ }
+ p = p.next;
+ }
+ return false;
+ }
+ }
+
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
@@ -759,12 +776,14 @@ public final class MessageQueue {
}
}
- void dump(Printer pw, String prefix) {
+ void dump(Printer pw, String prefix, Handler h) {
synchronized (this) {
long now = SystemClock.uptimeMillis();
int n = 0;
for (Message msg = mMessages; msg != null; msg = msg.next) {
- pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+ if (h == null || h == msg.target) {
+ pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+ }
n++;
}
pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f1ce9d52e920..ee3e986cc323 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9029,7 +9029,10 @@ public final class Settings {
* <pre>
* max_cached_processes (int)
* background_settle_time (long)
- * foreground_service_ui_min_time (long)
+ * fgservice_min_shown_time (long)
+ * fgservice_min_report_time (long)
+ * fgservice_screen_on_before_time (long)
+ * fgservice_screen_on_after_time (long)
* content_provider_retain_time (long)
* gc_timeout (long)
* gc_min_interval (long)
@@ -9818,6 +9821,16 @@ public final class Settings {
public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
/**
+ * Toggle to enable/disable dexopt for instant applications. The default is for dexopt
+ * to be disabled.
+ * <p>
+ * Type: int (0 to disable, 1 to enable)
+ *
+ * @hide
+ */
+ public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled";
+
+ /**
* The min period for caching installed instant apps in milliseconds.
* <p>
* Type: long
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
index 6956c8ac7135..f8a87516854d 100644
--- a/core/java/android/service/autofill/FillContext.java
+++ b/core/java/android/service/autofill/FillContext.java
@@ -106,15 +106,15 @@ public final class FillContext implements Parcelable {
}
/**
- * Finds {@link ViewNode}s that have the requested ids.
+ * Finds {@link ViewNode ViewNodes} that have the requested ids.
*
- * @param ids The ids of the node to find
+ * @param ids The ids of the node to find.
*
- * @return The nodes indexed in the same way as the ids
+ * @return The nodes indexed in the same way as the ids.
*
* @hide
*/
- @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId... ids) {
+ @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) {
final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length];
@@ -178,6 +178,30 @@ public final class FillContext implements Parcelable {
return foundNodes;
}
+ /**
+ * Finds the {@link ViewNode} that has the requested {@code id}, if any.
+ *
+ * @hide
+ */
+ @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) {
+ final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
+ final int numWindowNodes = mStructure.getWindowNodeCount();
+ for (int i = 0; i < numWindowNodes; i++) {
+ nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode());
+ }
+ while (!nodesToProcess.isEmpty()) {
+ final ViewNode node = nodesToProcess.removeFirst();
+ if (id.equals(node.getAutofillId())) {
+ return node;
+ }
+ for (int i = 0; i < node.getChildCount(); i++) {
+ nodesToProcess.addLast(node.getChildAt(i));
+ }
+ }
+
+ return null;
+ }
+
public static final Parcelable.Creator<FillContext> CREATOR =
new Parcelable.Creator<FillContext>() {
@Override
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index fa3f55b99917..6ea7d5edb496 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -273,13 +273,24 @@ public final class SaveInfo implements Parcelable {
*
* <p>See {@link SaveInfo} for more info.
*
- * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty.
+ * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty, or if
+ * it contains any {@code null} entry.
*/
public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
- Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0,
- "must have at least one required id: " + Arrays.toString(requiredIds));
+ // TODO: add CTS unit tests (not integration) to assert the null cases
mType = type;
- mRequiredIds = requiredIds;
+ mRequiredIds = assertValid(requiredIds);
+ }
+
+ private AutofillId[] assertValid(AutofillId[] ids) {
+ Preconditions.checkArgument(ids != null && ids.length > 0,
+ "must have at least one id: " + Arrays.toString(ids));
+ for (int i = 0; i < ids.length; i++) {
+ final AutofillId id = ids[i];
+ Preconditions.checkArgument(id != null,
+ "cannot have null id: " + Arrays.toString(ids));
+ }
+ return ids;
}
/**
@@ -302,12 +313,14 @@ public final class SaveInfo implements Parcelable {
*
* @param ids The ids of the optional views.
* @return This builder.
+ *
+ * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
+ * it contains any {@code null} entry.
*/
- public @NonNull Builder setOptionalIds(@Nullable AutofillId[] ids) {
+ public @NonNull Builder setOptionalIds(@NonNull AutofillId[] ids) {
+ // TODO: add CTS unit tests (not integration) to assert the null cases
throwIfDestroyed();
- if (ids != null && ids.length != 0) {
- mOptionalIds = ids;
- }
+ mOptionalIds = assertValid(ids);
return this;
}
@@ -421,7 +434,10 @@ public final class SaveInfo implements Parcelable {
final Builder builder = new Builder(parcel.readInt(),
parcel.readParcelableArray(null, AutofillId.class));
builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null));
- builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class));
+ final AutofillId[] optionalIds = parcel.readParcelableArray(null, AutofillId.class);
+ if (optionalIds != null) {
+ builder.setOptionalIds(optionalIds);
+ }
builder.setDescription(parcel.readCharSequence());
builder.setFlags(parcel.readInt());
return builder.build();
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 8bb3fa988a45..4f9dbd5ad7a0 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -52,7 +52,9 @@ public class Surface implements Parcelable {
private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
throws OutOfResourcesException;
+
private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
+ private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject);
private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
throws OutOfResourcesException;
@@ -410,6 +412,9 @@ public class Surface implements Parcelable {
* back from a client, converting it from the representation being managed
* by the window manager to the representation the client uses to draw
* in to it.
+ *
+ * @param other {@link SurfaceControl} to copy from.
+ *
* @hide
*/
public void copyFrom(SurfaceControl other) {
@@ -420,7 +425,39 @@ public class Surface implements Parcelable {
long surfaceControlPtr = other.mNativeObject;
if (surfaceControlPtr == 0) {
throw new NullPointerException(
- "SurfaceControl native object is null. Are you using a released SurfaceControl?");
+ "null SurfaceControl native object. Are you using a released SurfaceControl?");
+ }
+ long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
+
+ synchronized (mLock) {
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
+ }
+ setNativeObjectLocked(newNativeObject);
+ }
+ }
+
+ /**
+ * Gets a reference a surface created from this one. This surface now holds a reference
+ * to the same data as the original surface, and is -not- the owner.
+ * This is for use by the window manager when returning a window surface
+ * back from a client, converting it from the representation being managed
+ * by the window manager to the representation the client uses to draw
+ * in to it.
+ *
+ * @param other {@link SurfaceControl} to create surface from.
+ *
+ * @hide
+ */
+ public void createFrom(SurfaceControl other) {
+ if (other == null) {
+ throw new IllegalArgumentException("other must not be null");
+ }
+
+ long surfaceControlPtr = other.mNativeObject;
+ if (surfaceControlPtr == 0) {
+ throw new NullPointerException(
+ "null SurfaceControl native object. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 679a9cd92bc2..34ceeb7bcc0d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -641,6 +641,16 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
mSurface.copyFrom(mSurfaceControl);
}
+ if (getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.O) {
+ // Some legacy applications use the underlying native {@link Surface} object
+ // as a key to whether anything has changed. In these cases, updates to the
+ // existing {@link Surface} will be ignored when the size changes.
+ // Therefore, we must explicitly recreate the {@link Surface} in these
+ // cases.
+ mSurface.createFrom(mSurfaceControl);
+ }
+
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 7cec957adf41..a140f280e7c3 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -970,6 +970,9 @@ public final class ThreadedRenderer {
observer.mNative = null;
}
+ /** Not actually public - internal use only. This doc to make lint happy */
+ public static native void disableVsync();
+
static native void setupShadersDiskCache(String cacheFile);
private static native void nRotateProcessStatsBuffer();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b093284ae9ff..57409d204aca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -802,7 +802,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* {@hide}
*/
- public static final int LAST_APP_ACCESSIBILITY_ID = Integer.MAX_VALUE / 2;
+ public static final int LAST_APP_AUTOFILL_ID = Integer.MAX_VALUE / 2;
/**
* Attribute to find the autofilled highlight
@@ -2045,6 +2045,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private SparseArray<Object> mKeyedTags;
/**
+ * The next available accessibility id.
+ */
+ private static int sNextAccessibilityViewId;
+
+ /**
* The animation currently associated with this view.
* @hide
*/
@@ -2086,16 +2091,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@ViewDebug.ExportedProperty(resolveId = true)
int mID = NO_ID;
- /** The ID of this view for accessibility and autofill purposes.
+ /** The ID of this view for autofill purposes.
* <ul>
* <li>== {@link #NO_ID}: ID has not been assigned yet
- * <li>&le; {@link #LAST_APP_ACCESSIBILITY_ID}: View is not part of a activity. The ID is
+ * <li>&le; {@link #LAST_APP_AUTOFILL_ID}: View is not part of a activity. The ID is
* unique in the process. This might change
* over activity lifecycle events.
- * <li>&gt; {@link #LAST_APP_ACCESSIBILITY_ID}: View is part of a activity. The ID is
+ * <li>&gt; {@link #LAST_APP_AUTOFILL_ID}: View is part of a activity. The ID is
* unique in the activity. This stays the same
* over activity lifecycle events.
*/
+ private int mAutofillViewId = NO_ID;
+
+ // ID for accessibility purposes. This ID must be unique for every window
private int mAccessibilityViewId = NO_ID;
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
@@ -7723,7 +7731,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mAutofillId == null) {
// The autofill id needs to be unique, but its value doesn't matter,
// so it's better to reuse the accessibility id to save space.
- mAutofillId = new AutofillId(getAccessibilityViewId());
+ mAutofillId = new AutofillId(getAutofillViewId());
}
return mAutofillId;
}
@@ -7956,7 +7964,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private boolean isAutofillable() {
return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill()
- && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID;
+ && getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
private void populateVirtualStructure(ViewStructure structure,
@@ -8474,12 +8482,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public int getAccessibilityViewId() {
if (mAccessibilityViewId == NO_ID) {
- mAccessibilityViewId = mContext.getNextAccessibilityId();
+ mAccessibilityViewId = sNextAccessibilityViewId++;
}
return mAccessibilityViewId;
}
/**
+ * Gets the unique identifier of this view on the screen for autofill purposes.
+ *
+ * @return The view autofill id.
+ *
+ * @hide
+ */
+ public int getAutofillViewId() {
+ if (mAutofillViewId == NO_ID) {
+ mAutofillViewId = mContext.getNextAutofillId();
+ }
+ return mAutofillViewId;
+ }
+
+ /**
* Gets the unique identifier of the window in which this View reseides.
*
* @return The window accessibility id.
@@ -12117,7 +12139,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (isAutofillable()) {
AutofillManager afm = getAutofillManager();
- if (afm != null && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID) {
+ if (afm != null && getAutofillViewId() > LAST_APP_AUTOFILL_ID) {
if (mVisibilityChangeForAutofillHandler != null) {
mVisibilityChangeForAutofillHandler.removeMessages(0);
}
@@ -17547,16 +17569,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save.
- * @see #onRestoreInstanceState(android.os.Parcelable)
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(Parcelable)
+ * @see #saveHierarchyState(SparseArray)
+ * @see #dispatchSaveInstanceState(SparseArray)
* @see #setSaveEnabled(boolean)
*/
@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null || isAutofilled()
- || mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
+ || mAutofillViewId > LAST_APP_AUTOFILL_ID) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
if (mStartActivityRequestWho != null) {
@@ -17567,13 +17589,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
}
- if (mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
- state.mSavedData |= BaseSavedState.ACCESSIBILITY_ID;
+ if (mAutofillViewId > LAST_APP_AUTOFILL_ID) {
+ state.mSavedData |= BaseSavedState.AUTOFILL_ID;
}
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
state.mIsAutofilled = isAutofilled();
- state.mAccessibilityViewId = mAccessibilityViewId;
+ state.mAutofillViewId = mAutofillViewId;
return state;
}
return BaseSavedState.EMPTY_STATE;
@@ -17651,8 +17673,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
setAutofilled(baseState.mIsAutofilled);
}
- if ((baseState.mSavedData & BaseSavedState.ACCESSIBILITY_ID) != 0) {
- mAccessibilityViewId = baseState.mAccessibilityViewId;
+ if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) {
+ mAutofillViewId = baseState.mAutofillViewId;
}
}
}
@@ -21476,7 +21498,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param accessibilityId The searched accessibility id.
* @return The found view.
*/
- final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
+ final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId < 0) {
return null;
}
@@ -21488,11 +21510,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Performs the traversal to find a view by its unuque and stable accessibility id.
+ * Performs the traversal to find a view by its unique and stable accessibility id.
*
* <strong>Note:</strong>This method does not stop at the root namespace
* boundary since the user can touch the screen at an arbitrary location
- * potentially crossing the root namespace bounday which will send an
+ * potentially crossing the root namespace boundary which will send an
* accessibility event to accessibility services and they should be able
* to obtain the event source. Also accessibility ids are guaranteed to be
* unique in the window.
@@ -21509,6 +21531,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Performs the traversal to find a view by its autofill id.
+ *
+ * <strong>Note:</strong>This method does not stop at the root namespace
+ * boundary.
+ *
+ * @param autofillId The autofill id.
+ * @return The found view.
+ * @hide
+ */
+ public <T extends View> T findViewByAutofillIdTraversal(int autofillId) {
+ if (getAutofillViewId() == autofillId) {
+ return (T) this;
+ }
+ return null;
+ }
+
+ /**
* Look for a child view with the given tag. If this view has the given
* tag, return this view.
*
@@ -24974,13 +25013,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static class BaseSavedState extends AbsSavedState {
static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1;
static final int IS_AUTOFILLED = 0b10;
- static final int ACCESSIBILITY_ID = 0b100;
+ static final int AUTOFILL_ID = 0b100;
// Flags that describe what data in this state is valid
int mSavedData;
String mStartActivityRequestWhoSaved;
boolean mIsAutofilled;
- int mAccessibilityViewId;
+ int mAutofillViewId;
/**
* Constructor used when reading from a parcel. Reads the state of the superclass.
@@ -25003,7 +25042,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mSavedData = source.readInt();
mStartActivityRequestWhoSaved = source.readString();
mIsAutofilled = source.readBoolean();
- mAccessibilityViewId = source.readInt();
+ mAutofillViewId = source.readInt();
}
/**
@@ -25022,7 +25061,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
out.writeInt(mSavedData);
out.writeString(mStartActivityRequestWhoSaved);
out.writeBoolean(mIsAutofilled);
- out.writeInt(mAccessibilityViewId);
+ out.writeInt(mAutofillViewId);
}
public static final Parcelable.Creator<BaseSavedState> CREATOR
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4b79b8c5be9a..66df335f07da 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1361,6 +1361,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return null;
}
+ /** @hide */
+ @Override
+ public View findViewByAutofillIdTraversal(int autofillId) {
+ View foundView = super.findViewByAutofillIdTraversal(autofillId);
+ if (foundView != null) {
+ return foundView;
+ }
+
+ final int childrenCount = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < childrenCount; i++) {
+ View child = children[i];
+ foundView = child.findViewByAutofillIdTraversal(autofillId);
+ if (foundView != null) {
+ return foundView;
+ }
+ }
+
+ return null;
+ }
+
@Override
public void dispatchWindowFocusChanged(boolean hasFocus) {
super.dispatchWindowFocusChanged(hasFocus);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2605b4a2e50c..fd950dbffb4b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2471,6 +2471,9 @@ public final class ViewRootImpl implements ViewParent,
mInLayout = true;
final View host = mView;
+ if (host == null) {
+ return;
+ }
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
@@ -2778,6 +2781,8 @@ public final class ViewRootImpl implements ViewParent,
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
+ } else if (mView == null) {
+ return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 41a8b8a4b750..bdadcc7cd987 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -912,14 +912,14 @@ public final class AccessibilityManager {
}
/**
- * Notifies that the availability of the accessibility button in the system's navigation area
+ * Notifies that the visibility of the accessibility button in the system's navigation area
* has changed.
*
- * @param available {@code true} if the accessibility button is available within the system
+ * @param shown {@code true} if the accessibility button is visible within the system
* navigation area, {@code false} otherwise
* @hide
*/
- public void notifyAccessibilityButtonAvailabilityChanged(boolean available) {
+ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -928,9 +928,9 @@ public final class AccessibilityManager {
}
}
try {
- service.notifyAccessibilityButtonAvailabilityChanged(available);
+ service.notifyAccessibilityButtonVisibilityChanged(shown);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while dispatching accessibility button availability change", re);
+ Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re);
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 06cb5dca8afa..3f499abd2e4d 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -64,7 +64,7 @@ interface IAccessibilityManager {
void notifyAccessibilityButtonClicked();
- void notifyAccessibilityButtonAvailabilityChanged(boolean available);
+ void notifyAccessibilityButtonVisibilityChanged(boolean available);
// Requires WRITE_SECURE_SETTINGS
void performAccessibilityShortcut();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 310ec1c938d0..5b04f41c7ee2 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -246,20 +246,20 @@ public final class AutofillManager {
/**
* Finds views by traversing the hierarchies of the client.
*
- * @param viewIds The accessibility ids of the views to find
+ * @param viewIds The autofill ids of the views to find
*
* @return And array containing the views (empty if no views found).
*/
- @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
+ @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
/**
* Finds a view by traversing the hierarchies of the client.
*
- * @param viewId The accessibility id of the views to find
+ * @param viewId The autofill id of the views to find
*
* @return The view, or {@code null} if not found
*/
- @Nullable View findViewByAccessibilityIdTraversal(int viewId);
+ @Nullable View findViewByAutofillIdTraversal(int viewId);
/**
* Runs the specified action on the UI thread.
@@ -795,11 +795,11 @@ public final class AutofillManager {
}
private static AutofillId getAutofillId(View view) {
- return new AutofillId(view.getAccessibilityViewId());
+ return new AutofillId(view.getAutofillViewId());
}
private static AutofillId getAutofillId(View parent, int virtualId) {
- return new AutofillId(parent.getAccessibilityViewId(), virtualId);
+ return new AutofillId(parent.getAutofillViewId(), virtualId);
}
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@@ -1039,7 +1039,7 @@ public final class AutofillManager {
final int itemCount = ids.size();
int numApplied = 0;
ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
- final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
+ final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
for (int i = 0; i < itemCount; i++) {
final AutofillId id = ids.get(i);
@@ -1232,7 +1232,7 @@ public final class AutofillManager {
return null;
}
- return client.findViewByAccessibilityIdTraversal(autofillId.getViewId());
+ return client.findViewByAutofillIdTraversal(autofillId.getViewId());
}
/** @hide */
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 1e8207a2e7b3..f712d5fa65ca 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -293,9 +293,19 @@ class DayPickerView extends ViewGroup {
* @param setSelected whether to set the specified day as selected
*/
private void setDate(long timeInMillis, boolean animate, boolean setSelected) {
+ boolean dateClamped = false;
+ // Clamp the target day in milliseconds to the min or max if outside the range.
+ if (timeInMillis < mMinDate.getTimeInMillis()) {
+ timeInMillis = mMinDate.getTimeInMillis();
+ dateClamped = true;
+ } else if (timeInMillis > mMaxDate.getTimeInMillis()) {
+ timeInMillis = mMaxDate.getTimeInMillis();
+ dateClamped = true;
+ }
+
getTempCalendarForTime(timeInMillis);
- if (setSelected) {
+ if (setSelected || dateClamped) {
mSelectedDay.setTimeInMillis(timeInMillis);
}
@@ -353,13 +363,6 @@ class DayPickerView extends ViewGroup {
public void onRangeChanged() {
mAdapter.setRange(mMinDate, mMaxDate);
- // Clamp the selected day to the new min/max.
- if (mSelectedDay.before(mMinDate)) {
- mSelectedDay.setTimeInMillis(mMinDate.getTimeInMillis());
- } else if (mSelectedDay.after(mMaxDate)) {
- mSelectedDay.setTimeInMillis(mMaxDate.getTimeInMillis());
- }
-
// Changing the min/max date changes the selection position since we
// don't really have stable IDs. Jumps immediately to the new position.
setDate(mSelectedDay.getTimeInMillis(), false, false);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0e6e3aee28b1..45e5f8adc468 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2584,14 +2584,18 @@ public class Editor {
if (offset == -1) {
return;
}
+
stopTextActionModeWithPreservingSelection();
- final boolean isOnSelection = mTextView.hasSelection()
- && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
- if (!isOnSelection) {
- // Right clicked position is not on the selection. Remove the selection and move the
- // cursor to the right clicked position.
- Selection.setSelection((Spannable) mTextView.getText(), offset);
- stopTextActionMode();
+ if (mTextView.canSelectText()) {
+ final boolean isOnSelection = mTextView.hasSelection()
+ && offset >= mTextView.getSelectionStart()
+ && offset <= mTextView.getSelectionEnd();
+ if (!isOnSelection) {
+ // Right clicked position is not on the selection. Remove the selection and move the
+ // cursor to the right clicked position.
+ Selection.setSelection((Spannable) mTextView.getText(), offset);
+ stopTextActionMode();
+ }
}
if (shouldOfferToShowSuggestions()) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7117137ce2f8..aa6ffceea9f3 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2342,24 +2342,26 @@ public class RemoteViews implements Parcelable, Filter {
}
- public synchronized RemoteViews clone() {
- Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
- + "May only clone the root of a RemoteView hierarchy.");
+ public RemoteViews clone() {
+ synchronized (this) {
+ Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
+ + "May only clone the root of a RemoteView hierarchy.");
- Parcel p = Parcel.obtain();
+ Parcel p = Parcel.obtain();
- // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps.
- // Instead pretend we're not owning the cache while parceling.
- mIsRoot = false;
- writeToParcel(p, PARCELABLE_ELIDE_DUPLICATES);
- p.setDataPosition(0);
- mIsRoot = true;
+ // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps.
+ // Instead pretend we're not owning the cache while parceling.
+ mIsRoot = false;
+ writeToParcel(p, PARCELABLE_ELIDE_DUPLICATES);
+ p.setDataPosition(0);
+ mIsRoot = true;
- RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication, 0);
- rv.mIsRoot = true;
+ RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication, 0);
+ rv.mIsRoot = true;
- p.recycle();
- return rv;
+ p.recycle();
+ return rv;
+ }
}
public String getPackage() {
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 5839fd50d79a..7744e0cd6b5d 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -394,6 +394,16 @@ static void nativeAllocateBuffers(JNIEnv* /* env */ , jclass /* clazz */,
static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,
jlong surfaceControlNativeObj) {
+ sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
+ sp<Surface> surface(ctrl->createSurface());
+ if (surface != NULL) {
+ surface->incStrong(&sRefBaseOwner);
+ }
+ return reinterpret_cast<jlong>(surface.get());
+}
+
+static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz,
+ jlong surfaceControlNativeObj) {
/*
* This is used by the WindowManagerService just after constructing
* a Surface and is necessary for returning the Surface reference to
@@ -590,6 +600,8 @@ static const JNINativeMethod gSurfaceMethods[] = {
(void*)nativeAllocateBuffers },
{"nativeCreateFromSurfaceControl", "(J)J",
(void*)nativeCreateFromSurfaceControl },
+ {"nativeGetFromSurfaceControl", "(J)J",
+ (void*)nativeGetFromSurfaceControl },
{"nativeReadFromParcel", "(JLandroid/os/Parcel;)J",
(void*)nativeReadFromParcel },
{"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 438b1233a286..c9251bcac835 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -932,6 +932,10 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(
return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable);
}
+static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) {
+ RenderProxy::disableVsync();
+}
+
// ----------------------------------------------------------------------------
// FrameMetricsObserver
// ----------------------------------------------------------------------------
@@ -1030,6 +1034,7 @@ static const JNINativeMethod gMethods[] = {
(void*)android_view_ThreadedRenderer_copySurfaceInto },
{ "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
(void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode },
+ { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 325177126f04..8139176a7732 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1288,10 +1288,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="1610714069627824309">"Tryk for at administrere netværket."</string>
<string name="vpn_text_long" msgid="4907843483284977618">"Forbundet til <xliff:g id="SESSION">%s</xliff:g>. Tryk for at administrere netværket."</string>
- <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til altid aktiveret VPN…"</string>
- <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altid aktiveret VPN er forbundet"</string>
- <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til altid aktiveret VPN er afbrudt"</string>
- <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i altid aktiveret VPN"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til konstant VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Konstant VPN er forbundet"</string>
+ <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til konstant VPN er afbrudt"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i konstant VPN"</string>
<string name="vpn_lockdown_config" msgid="5099330695245008680">"Tryk for at konfigurere"</string>
<string name="upload_file" msgid="2897957172366730416">"Vælg fil"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 778163c411e2..a341a8a8f8e0 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -447,7 +447,7 @@
<string name="permlab_changeTetherState" msgid="5952584964373017960">"Tethering-Konnektivität ändern"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Ermöglicht der App, den Status der Tethering-Konnektivität zu ändern"</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"WLAN-Verbindungen abrufen"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Ermöglicht der App, Informationen zu WLANs abzurufen, etwa ob ein WLAN aktiviert ist, und den Namen verbundener WLAN-Geräte."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Ermöglicht der App, Informationen zu WLAN-Netzwerken abzurufen, etwa ob ein WLAN aktiviert ist, und den Namen verbundener WLAN-Geräte."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"WLAN-Verbindungen herstellen und trennen"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Ermöglicht der App, eine Verbindung zu WLAN-Zugangspunkten herzustellen und solche zu trennen und Änderungen an der Gerätekonfiguration für WLAN-Netzwerke vorzunehmen."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"WLAN-Multicast-Empfang zulassen"</string>
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index aad81df0b7e5..b5872485b136 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -69,6 +69,7 @@ bool Properties::waitForGpuCompletion = false;
bool Properties::forceDrawFrame = false;
bool Properties::filterOutTestOverhead = false;
+bool Properties::disableVsync = false;
static int property_get_int(const char* key, int defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {'\0',};
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 9db64493928a..91b4a2d440e2 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -318,6 +318,12 @@ public:
// any overhead they add
static bool filterOutTestOverhead;
+ // Workaround a device lockup in edge cases by switching to async mode
+ // instead of the default vsync (b/38372997). Only system_server should hit this.
+ // Any existing RenderProxy & Surface combination will be unaffected, only things
+ // created after changing this.
+ static bool disableVsync;
+
// Used for testing only to change the render pipeline.
#ifdef HWUI_GLES_WRAP_ENABLED
static void overrideRenderPipelineType(RenderPipelineType);
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 44af5fd8d0dd..ed3070887b8b 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -279,6 +279,9 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
}
}
mCurrentSurface = surface;
+ if (Properties::disableVsync) {
+ eglSwapInterval(mEglDisplay, 0);
+ }
return true;
}
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index eed523810403..d842be9e7d6e 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -18,6 +18,7 @@
#include "DeferredLayerUpdater.h"
#include "DisplayList.h"
+#include "Properties.h"
#include "Readback.h"
#include "Rect.h"
#include "renderthread/CanvasContext.h"
@@ -708,6 +709,10 @@ void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) {
thread.queue(task);
}
+void RenderProxy::disableVsync() {
+ Properties::disableVsync = true;
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index b21772cd88de..6f4e8cef4502 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -137,6 +137,8 @@ public:
static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap);
static void onBitmapDestroyed(uint32_t pixelRefId);
+
+ ANDROID_API static void disableVsync();
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 163c4b012262..f408e57e29e4 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -177,6 +177,7 @@ BufferItem* JNIImageReaderContext::getBufferItem() {
}
void JNIImageReaderContext::returnBufferItem(BufferItem* buffer) {
+ buffer->mGraphicBuffer = nullptr;
mBuffers.push_back(buffer);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 6140428d353f..41952dfe7f70 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -16,6 +16,8 @@
package com.android.printspooler.model;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -29,21 +31,27 @@ import android.os.AsyncTask;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.print.PageRange;
import android.print.PrintAttributes;
-import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
import android.print.PrintDocumentInfo;
import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
+
import com.android.internal.annotations.GuardedBy;
import com.android.printspooler.renderer.IPdfRenderer;
import com.android.printspooler.renderer.PdfManipulationService;
import com.android.printspooler.util.BitmapSerializeUtils;
+import com.android.printspooler.util.PageRangeUtils;
+
import dalvik.system.CloseGuard;
+
import libcore.io.IoUtils;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -69,8 +77,9 @@ public final class PageContentRepository {
private RenderSpec mLastRenderSpec;
- private int mScheduledPreloadFirstShownPage = INVALID_PAGE_INDEX;
- private int mScheduledPreloadLastShownPage = INVALID_PAGE_INDEX;
+ @Nullable private PageRange mScheduledPreloadVisiblePages;
+ @Nullable private PageRange[] mScheduledPreloadSelectedPages;
+ @Nullable private PageRange[] mScheduledPreloadWrittenPages;
private int mState;
@@ -129,14 +138,24 @@ public final class PageContentRepository {
}
}
- public void startPreload(int firstShownPage, int lastShownPage) {
+ /**
+ * Preload selected, written pages around visiblePages.
+ *
+ * @param visiblePages The pages currently visible
+ * @param selectedPages The pages currently selected (e.g. they might become visible by
+ * scrolling)
+ * @param writtenPages The pages currently in the document
+ */
+ public void startPreload(@NonNull PageRange visiblePages, @NonNull PageRange[] selectedPages,
+ @NonNull PageRange[] writtenPages) {
// If we do not have a render spec we have no clue what size the
// preloaded bitmaps should be, so just take a note for what to do.
if (mLastRenderSpec == null) {
- mScheduledPreloadFirstShownPage = firstShownPage;
- mScheduledPreloadLastShownPage = lastShownPage;
+ mScheduledPreloadVisiblePages = visiblePages;
+ mScheduledPreloadSelectedPages = selectedPages;
+ mScheduledPreloadWrittenPages = writtenPages;
} else if (mState == STATE_OPENED) {
- mRenderer.startPreload(firstShownPage, lastShownPage, mLastRenderSpec);
+ mRenderer.startPreload(visiblePages, selectedPages, writtenPages, mLastRenderSpec);
}
}
@@ -222,11 +241,12 @@ public final class PageContentRepository {
// We tired to preload but didn't know the bitmap size, now
// that we know let us do the work.
- if (mScheduledPreloadFirstShownPage != INVALID_PAGE_INDEX
- && mScheduledPreloadLastShownPage != INVALID_PAGE_INDEX) {
- startPreload(mScheduledPreloadFirstShownPage, mScheduledPreloadLastShownPage);
- mScheduledPreloadFirstShownPage = INVALID_PAGE_INDEX;
- mScheduledPreloadLastShownPage = INVALID_PAGE_INDEX;
+ if (mScheduledPreloadVisiblePages != null) {
+ startPreload(mScheduledPreloadVisiblePages, mScheduledPreloadSelectedPages,
+ mScheduledPreloadWrittenPages);
+ mScheduledPreloadVisiblePages = null;
+ mScheduledPreloadSelectedPages = null;
+ mScheduledPreloadWrittenPages = null;
}
if (mState == STATE_OPENED) {
@@ -523,10 +543,45 @@ public final class PageContentRepository {
mDestroyed = true;
}
- public void startPreload(int firstShownPage, int lastShownPage, RenderSpec renderSpec) {
+ /**
+ * How many pages are {@code pages} before pageNum. E.g. page 5 in [0-1], [4-7] has the
+ * index 4.
+ *
+ * @param pageNum The number of the page to find
+ * @param pages A normalized array of page ranges
+ *
+ * @return The index or {@link #INVALID_PAGE_INDEX} if not found
+ */
+ private int findIndexOfPage(int pageNum, @NonNull PageRange[] pages) {
+ int pagesBefore = 0;
+ for (int i = 0; i < pages.length; i++) {
+ if (pages[i].contains(pageNum)) {
+ return pagesBefore + pageNum - pages[i].getStart();
+ } else {
+ pagesBefore += pages[i].getSize();
+ }
+ }
+
+ return INVALID_PAGE_INDEX;
+ }
+
+ void startPreload(@NonNull PageRange visiblePages, @NonNull PageRange[] selectedPages,
+ @NonNull PageRange[] writtenPages, RenderSpec renderSpec) {
+ if (PageRangeUtils.isAllPages(selectedPages)) {
+ selectedPages = new PageRange[]{new PageRange(0, mPageCount - 1)};
+ }
+
if (DEBUG) {
- Log.i(LOG_TAG, "Preloading pages around [" + firstShownPage
- + "-" + lastShownPage + "]");
+ Log.i(LOG_TAG, "Preloading pages around " + visiblePages + " from "
+ + Arrays.toString(selectedPages));
+ }
+
+ int firstVisiblePageIndex = findIndexOfPage(visiblePages.getStart(), selectedPages);
+ int lastVisiblePageIndex = findIndexOfPage(visiblePages.getEnd(), selectedPages);
+
+ if (firstVisiblePageIndex == INVALID_PAGE_INDEX
+ || lastVisiblePageIndex == INVALID_PAGE_INDEX) {
+ return;
}
final int bitmapSizeInBytes = renderSpec.bitmapWidth * renderSpec.bitmapHeight
@@ -534,28 +589,33 @@ public final class PageContentRepository {
final int maxCachedPageCount = mPageContentCache.getMaxSizeInBytes()
/ bitmapSizeInBytes;
final int halfPreloadCount = (maxCachedPageCount
- - (lastShownPage - firstShownPage)) / 2 - 1;
+ - (lastVisiblePageIndex - firstVisiblePageIndex)) / 2 - 1;
- final int excessFromStart;
- if (firstShownPage - halfPreloadCount < 0) {
- excessFromStart = halfPreloadCount - firstShownPage;
- } else {
- excessFromStart = 0;
- }
+ final int fromIndex = Math.max(firstVisiblePageIndex - halfPreloadCount, 0);
+ final int toIndex = lastVisiblePageIndex + halfPreloadCount;
- final int excessFromEnd;
- if (lastShownPage + halfPreloadCount >= mPageCount) {
- excessFromEnd = (lastShownPage + halfPreloadCount) - mPageCount;
- } else {
- excessFromEnd = 0;
+ if (DEBUG) {
+ Log.i(LOG_TAG, "fromIndex=" + fromIndex + " toIndex=" + toIndex);
}
- final int fromIndex = Math.max(firstShownPage - halfPreloadCount - excessFromEnd, 0);
- final int toIndex = Math.min(lastShownPage + halfPreloadCount + excessFromStart,
- mPageCount - 1);
+ int previousRangeSizes = 0;
+ for (int rangeNum = 0; rangeNum < selectedPages.length; rangeNum++) {
+ PageRange range = selectedPages[rangeNum];
+
+ int thisRangeStart = Math.max(0, fromIndex - previousRangeSizes);
+ int thisRangeEnd = Math.min(range.getSize(), toIndex - previousRangeSizes + 1);
+
+ for (int i = thisRangeStart; i < thisRangeEnd; i++) {
+ if (PageRangeUtils.contains(writtenPages, range.getStart() + i)) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Preloading " + (range.getStart() + i));
+ }
+
+ renderPage(range.getStart() + i, renderSpec, null);
+ }
+ }
- for (int i = fromIndex; i <= toIndex; i++) {
- renderPage(i, renderSpec, null);
+ previousRangeSizes += range.getSize();
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 54400b3b8540..1eadb8e9765a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -16,6 +16,7 @@
package com.android.printspooler.ui;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -24,8 +25,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
-import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
import android.print.PrintDocumentInfo;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -33,11 +34,12 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.MeasureSpec;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.View.MeasureSpec;
import android.widget.TextView;
+
import com.android.printspooler.R;
import com.android.printspooler.model.OpenDocumentCallback;
import com.android.printspooler.model.PageContentRepository;
@@ -45,6 +47,7 @@ import com.android.printspooler.model.PageContentRepository.PageContentProvider;
import com.android.printspooler.util.PageRangeUtils;
import com.android.printspooler.widget.PageContentView;
import com.android.printspooler.widget.PreviewPageFrame;
+
import dalvik.system.CloseGuard;
import java.util.ArrayList;
@@ -794,14 +797,16 @@ public final class PageAdapter extends Adapter<ViewHolder> {
page.setTag(null);
}
- public void startPreloadContent(PageRange pageRangeInAdapter) {
- final int startPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getStart());
- final int startPageInFile = computePageIndexInFile(startPageInDocument);
- final int endPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getEnd());
- final int endPageInFile = computePageIndexInFile(endPageInDocument);
- if (startPageInDocument != INVALID_PAGE_INDEX && endPageInDocument != INVALID_PAGE_INDEX) {
- mPageContentRepository.startPreload(startPageInFile, endPageInFile);
+ void startPreloadContent(@NonNull PageRange visiblePagesInAdapter) {
+ int startVisibleDocument = computePageIndexInDocument(visiblePagesInAdapter.getStart());
+ int endVisibleDocument = computePageIndexInDocument(visiblePagesInAdapter.getEnd());
+ if (startVisibleDocument == INVALID_PAGE_INDEX
+ || endVisibleDocument == INVALID_PAGE_INDEX) {
+ return;
}
+
+ mPageContentRepository.startPreload(new PageRange(startVisibleDocument, endVisibleDocument),
+ mSelectedPages, mWrittenPages);
}
public void stopPreloadContent() {
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 48bf180cb0ca..f5d514053984 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -273,7 +273,7 @@
<string name="show_all_anrs" msgid="28462979638729082">"Tots els errors sense resposta"</string>
<string name="show_all_anrs_summary" msgid="641908614413544127">"Informa que una aplicació en segon pla no respon"</string>
<string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostra avisos del canal de notificacions"</string>
- <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una app publica una notificació sense canal vàlid"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una aplicació publica una notificació sense un canal vàlid"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Força permís d\'aplicacions a l\'emmagatzem. extern"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Permet que qualsevol aplicació es pugui escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Força l\'ajust de la mida de les activitats"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 00f0b5eb9192..6889e0155396 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -272,8 +272,8 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"Limite proc. em 2º plano"</string>
<string name="show_all_anrs" msgid="28462979638729082">"Mostrar todos os ANR"</string>
<string name="show_all_anrs_summary" msgid="641908614413544127">"Mostrar erro \"Aplic. não Resp.\" p/ aplic. 2º plano"</string>
- <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostrar avisos do canal de notif."</string>
- <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra um aviso no ecrã quando uma aplic. publica uma notific. sem um canal válido"</string>
+ <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostrar avisos do canal de notificações"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra um aviso no ecrã quando uma aplicação publica uma notificação sem o canal ser válido"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Forçar perm. de aplicações no armazenamento ext."</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Torna qualquer aplicação elegível para ser gravada no armazenamento externo, independentemente dos valores do manifesto"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Forçar as atividades a serem redimensionáveis"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 33b973d72a38..9a0b1bf6433c 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -79,7 +79,7 @@
<string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"配對完成後,所配對的裝置即可在連線後存取你的聯絡人和通話紀錄。"</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對。"</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 或密碼金鑰不正確。"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 474de9074062..1cbb7450f7e1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -88,21 +88,16 @@ public class ZoneGetter {
private static final String XMLTAG_TIMEZONE = "timezone";
public static CharSequence getTimeZoneOffsetAndName(Context context, TimeZone tz, Date now) {
- final Locale locale = Locale.getDefault();
- final CharSequence gmtText = getGmtOffsetText(context, locale, tz, now);
- final TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
- final ZoneGetterData data = new ZoneGetterData(context);
-
- final boolean useExemplarLocationForLocalNames =
- shouldUseExemplarLocationForLocalNames(data, timeZoneNames);
- final CharSequence zoneName = getTimeZoneDisplayName(data, timeZoneNames,
- useExemplarLocationForLocalNames, tz, tz.getID());
- if (zoneName == null) {
+ Locale locale = Locale.getDefault();
+ CharSequence gmtText = getGmtOffsetText(context, locale, tz, now);
+ TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
+ String zoneNameString = getZoneLongName(timeZoneNames, tz, now);
+ if (zoneNameString == null) {
return gmtText;
}
// We don't use punctuation here to avoid having to worry about localizing that too!
- return TextUtils.concat(gmtText, " ", zoneName);
+ return TextUtils.concat(gmtText, " ", zoneNameString);
}
public static List<Map<String, Object>> getZonesList(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 623d1a6716a7..0ea9d96bbe83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -511,7 +511,10 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
}
- mSeen = seen;
+ // Only replace the previous value if we have a recent scan result to use
+ if (seen != 0) {
+ mSeen = seen;
+ }
}
/**
@@ -940,8 +943,10 @@ public class AccessPoint implements Comparable<AccessPoint> {
security = getSecurity(result);
if (security == SECURITY_PSK)
pskType = getPskType(result);
- mRssi = result.level;
- mSeen = result.timestamp;
+
+ mScanResultCache.put(result.BSSID, result);
+ updateRssi();
+ mSeen = result.timestamp; // even if the timestamp is old it is still valid
}
public void saveWifiState(Bundle savedState) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 40a59cf1ab82..fcc9090fc2f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -142,6 +142,7 @@ public class WifiTracker {
@VisibleForTesting
Scanner mScanner;
+ private boolean mStaleScanResults = false;
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
@@ -330,7 +331,11 @@ public class WifiTracker {
* Stop tracking wifi networks and scores.
*
* <p>This should always be called when done with a WifiTracker (if startTracking was called) to
- * ensure proper cleanup and prevent any further callbacks from occuring.
+ * ensure proper cleanup and prevent any further callbacks from occurring.
+ *
+ * <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
+ * {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
+ * is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
*/
@MainThread
public void stopTracking() {
@@ -346,6 +351,7 @@ public class WifiTracker {
mWorkHandler.removePendingMessages();
mMainHandler.removePendingMessages();
}
+ mStaleScanResults = true;
}
private void unregisterAndClearScoreCache() {
@@ -712,6 +718,11 @@ public class WifiTracker {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+
+ if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
+ mStaleScanResults = false;
+ }
+
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN));
@@ -822,7 +833,9 @@ public class WifiTracker {
switch (msg.what) {
case MSG_UPDATE_ACCESS_POINTS:
- updateAccessPointsLocked();
+ if (!mStaleScanResults) {
+ updateAccessPointsLocked();
+ }
break;
case MSG_UPDATE_NETWORK_INFO:
updateNetworkInfo((NetworkInfo) msg.obj);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
index 703e9d29e6ac..a3345ee58b7d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
@@ -47,9 +47,9 @@ public class ZoneGetterTest {
}
@Test
- public void getTimeZoneOffsetAndName_setLondon_returnLondon() {
- // Check it will ends with 'London', not 'British Summer Time' or sth else
- testTimeZoneOffsetAndNameInner(TIME_ZONE_LONDON_ID, "London");
+ public void getTimeZoneOffsetAndName_setLondon_returnBritishSummerTime() {
+ // Check it will ends with 'British Summer Time', not 'London' or sth else
+ testTimeZoneOffsetAndNameInner(TIME_ZONE_LONDON_ID, "British Summer Time");
}
@Test
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index c52643243063..eb9a7f6d0843 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -748,4 +749,36 @@ public class WifiTrackerTest {
verifyNoMoreInteractions(mockWifiListener);
}
+
+ @Test
+ public void stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult()
+ throws Exception {
+ WifiTracker tracker = createMockedWifiTracker();
+ startTracking(tracker);
+ tracker.stopTracking();
+
+ CountDownLatch latch1 = new CountDownLatch(1);
+ tracker.mMainHandler.post(() -> {
+ latch1.countDown();
+ });
+ assertTrue("Latch 1 timed out", latch1.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ startTracking(tracker);
+
+ tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
+ tracker.mReceiver.onReceive(
+ mContext, new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION));
+ tracker.mReceiver.onReceive(
+ mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
+
+ CountDownLatch latch2 = new CountDownLatch(1);
+ tracker.mMainHandler.post(() -> {
+ latch2.countDown();
+ });
+ assertTrue("Latch 2 timed out", latch2.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ verify(mockWifiListener, never()).onAccessPointsChanged();
+
+ sendScanResultsAndProcess(tracker); // verifies onAccessPointsChanged is invoked
+ }
}
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index 61ac6f6991ff..0b7bce13d761 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -22,24 +22,24 @@
<com.android.systemui.pip.tv.PipControlButtonView
android:id="@+id/full_button"
- android:layout_width="100dp"
+ android:layout_width="@dimen/picture_in_picture_button_width"
android:layout_height="wrap_content"
android:src="@drawable/ic_fullscreen_white_24dp"
android:text="@string/pip_fullscreen" />
<com.android.systemui.pip.tv.PipControlButtonView
android:id="@+id/close_button"
- android:layout_width="100dp"
+ android:layout_width="@dimen/picture_in_picture_button_width"
android:layout_height="wrap_content"
- android:layout_marginStart="-50dp"
+ android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
android:src="@drawable/ic_close_white"
android:text="@string/pip_close" />
<com.android.systemui.pip.tv.PipControlButtonView
android:id="@+id/play_pause_button"
- android:layout_width="100dp"
+ android:layout_width="@dimen/picture_in_picture_button_width"
android:layout_height="wrap_content"
- android:layout_marginStart="-50dp"
+ android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
android:src="@drawable/ic_pause_white"
android:text="@string/pip_pause"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/tv_pip_custom_control.xml b/packages/SystemUI/res/layout/tv_pip_custom_control.xml
new file mode 100644
index 000000000000..dd0fce466576
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_custom_control.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, 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.
+*/
+-->
+<com.android.systemui.pip.tv.PipControlButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/picture_in_picture_button_width"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" />
diff --git a/packages/SystemUI/res/values-tvdpi/dimens.xml b/packages/SystemUI/res/values-tvdpi/dimens.xml
index 5327cee7cae8..4d978aacc65f 100644
--- a/packages/SystemUI/res/values-tvdpi/dimens.xml
+++ b/packages/SystemUI/res/values-tvdpi/dimens.xml
@@ -24,4 +24,8 @@
<fraction name="battery_subpixel_smoothing_right">10%</fraction>
<dimen name="battery_margin_bottom">1px</dimen>
+
+ <!-- The dimensions to user for picture-in-picture action buttons. -->
+ <dimen name="picture_in_picture_button_width">100dp</dimen>
+ <dimen name="picture_in_picture_button_start_margin">-50dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 0f69f471bc24..a5ee19824590 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -427,11 +427,7 @@ public class PipMenuActivity extends Activity {
} else {
actionsContainer.setVisibility(View.VISIBLE);
if (mActionsGroup != null) {
- // Hide extra views
- for (int i = mActions.size(); i < mActionsGroup.getChildCount(); i++) {
- mActionsGroup.getChildAt(i).setVisibility(View.GONE);
- }
- // Add needed views
+ // Ensure we have as many buttons as actions
final LayoutInflater inflater = LayoutInflater.from(this);
while (mActionsGroup.getChildCount() < mActions.size()) {
final ImageView actionView = (ImageView) inflater.inflate(
@@ -439,6 +435,13 @@ public class PipMenuActivity extends Activity {
mActionsGroup.addView(actionView);
}
+ // Update the visibility of all views
+ for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+ mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
+ ? View.VISIBLE
+ : View.GONE);
+ }
+
// Recreate the layout
final boolean isLandscapePip = stackBounds != null &&
(stackBounds.width() > stackBounds.height());
@@ -460,10 +463,9 @@ public class PipMenuActivity extends Activity {
Log.w(TAG, "Failed to send action", e);
}
});
- } else {
- actionView.setAlpha(DISABLED_ACTION_ALPHA);
}
actionView.setEnabled(action.isEnabled());
+ actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
// Update the margin between actions
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
index 40a63d7d92c0..b21cd95626a7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
@@ -20,6 +20,7 @@ import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,6 +34,7 @@ import com.android.systemui.R;
* A view containing PIP controls including fullscreen, close, and media controls.
*/
public class PipControlButtonView extends RelativeLayout {
+
private OnFocusChangeListener mFocusChangeListener;
private ImageView mIconImageView;
ImageView mButtonImageView;
@@ -122,18 +124,37 @@ public class PipControlButtonView extends RelativeLayout {
}
/**
+ * Sets the drawable for the button with the given drawable.
+ */
+ public void setImageDrawable(Drawable d) {
+ mIconImageView.setImageDrawable(d);
+ }
+
+ /**
* Sets the drawable for the button with the given resource id.
*/
public void setImageResource(int resId) {
- mIconImageView.setImageResource(resId);
+ if (resId != 0) {
+ mIconImageView.setImageResource(resId);
+ }
+ }
+
+ /**
+ * Sets the text for description the with the given string.
+ */
+ public void setText(CharSequence text) {
+ mButtonImageView.setContentDescription(text);
+ mDescriptionTextView.setText(text);
}
/**
* Sets the text for description the with the given resource id.
*/
public void setText(int resId) {
- mButtonImageView.setContentDescription(getContext().getString(resId));
- mDescriptionTextView.setText(resId);
+ if (resId != 0) {
+ mButtonImageView.setContentDescription(getContext().getString(resId));
+ mDescriptionTextView.setText(resId);
+ }
}
private static void cancelAnimator(Animator animator) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index acea3b6b12ad..10206d492e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -16,12 +16,20 @@
package com.android.systemui.pip.tv;
+import android.app.ActivityManager;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
import android.content.Context;
+import android.graphics.Color;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.View;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.util.AttributeSet;
@@ -30,11 +38,19 @@ import com.android.systemui.R;
import static android.media.session.PlaybackState.ACTION_PAUSE;
import static android.media.session.PlaybackState.ACTION_PLAY;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A view containing PIP controls including fullscreen, close, and media controls.
*/
public class PipControlsView extends LinearLayout {
+
+ private static final String TAG = PipControlsView.class.getSimpleName();
+
+ private static final float DISABLED_ACTION_ALPHA = 0.54f;
+
/**
* An interface to listen user action.
*/
@@ -47,19 +63,23 @@ public class PipControlsView extends LinearLayout {
private MediaController mMediaController;
- final PipManager mPipManager = PipManager.getInstance();
- Listener mListener;
+ private final PipManager mPipManager = PipManager.getInstance();
+ private final LayoutInflater mLayoutInflater;
+ private final Handler mHandler;
+ private Listener mListener;
private PipControlButtonView mFullButtonView;
private PipControlButtonView mCloseButtonView;
private PipControlButtonView mPlayPauseButtonView;
+ private ArrayList<PipControlButtonView> mCustomButtonViews = new ArrayList<>();
+ private List<RemoteAction> mCustomActions = new ArrayList<>();
private PipControlButtonView mFocusedChild;
private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
- updatePlayPauseView();
+ updateUserActions();
}
};
@@ -95,9 +115,10 @@ public class PipControlsView extends LinearLayout {
public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- LayoutInflater inflater = (LayoutInflater) getContext()
+ mLayoutInflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.tv_pip_controls, this);
+ mLayoutInflater.inflate(R.layout.tv_pip_controls, this);
+ mHandler = new Handler();
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
@@ -176,21 +197,74 @@ public class PipControlsView extends LinearLayout {
if (mMediaController != null) {
mMediaController.registerCallback(mMediaControllerCallback);
}
- updatePlayPauseView();
+ updateUserActions();
}
- private void updatePlayPauseView() {
- int state = mPipManager.getPlaybackState();
- if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) {
+ /**
+ * Updates the actions for the PIP. If there are no custom actions, then the media session
+ * actions are shown.
+ */
+ private void updateUserActions() {
+ if (!mCustomActions.isEmpty()) {
+ // Ensure we have as many buttons as actions
+ while (mCustomButtonViews.size() < mCustomActions.size()) {
+ PipControlButtonView buttonView = (PipControlButtonView) mLayoutInflater.inflate(
+ R.layout.tv_pip_custom_control, this, false);
+ addView(buttonView);
+ mCustomButtonViews.add(buttonView);
+ }
+
+ // Update the visibility of all views
+ for (int i = 0; i < mCustomButtonViews.size(); i++) {
+ mCustomButtonViews.get(i).setVisibility(i < mCustomActions.size()
+ ? View.VISIBLE
+ : View.GONE);
+ }
+
+ // Update the state and visibility of the action buttons, and hide the rest
+ for (int i = 0; i < mCustomActions.size(); i++) {
+ final RemoteAction action = mCustomActions.get(i);
+ PipControlButtonView actionView = mCustomButtonViews.get(i);
+
+ // TODO: Check if the action drawable has changed before we reload it
+ action.getIcon().loadDrawableAsync(getContext(), d -> {
+ d.setTint(Color.WHITE);
+ actionView.setImageDrawable(d);
+ }, mHandler);
+ actionView.setText(action.getContentDescription());
+ if (action.isEnabled()) {
+ actionView.setOnClickListener(v -> {
+ try {
+ action.getActionIntent().send();
+ } catch (CanceledException e) {
+ Log.w(TAG, "Failed to send action", e);
+ }
+ });
+ }
+ actionView.setEnabled(action.isEnabled());
+ actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+ }
+
+ // Hide the media session buttons
mPlayPauseButtonView.setVisibility(View.GONE);
} else {
- mPlayPauseButtonView.setVisibility(View.VISIBLE);
- if (state == PipManager.PLAYBACK_STATE_PLAYING) {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
- mPlayPauseButtonView.setText(R.string.pip_pause);
+ int state = mPipManager.getPlaybackState();
+ if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) {
+ mPlayPauseButtonView.setVisibility(View.GONE);
} else {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
- mPlayPauseButtonView.setText(R.string.pip_play);
+ mPlayPauseButtonView.setVisibility(View.VISIBLE);
+ if (state == PipManager.PLAYBACK_STATE_PLAYING) {
+ mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
+ mPlayPauseButtonView.setText(R.string.pip_pause);
+ } else {
+ mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
+ mPlayPauseButtonView.setText(R.string.pip_play);
+ }
+ }
+
+ // Hide all the custom action buttons
+ for (int i = 0; i < mCustomButtonViews.size(); i++) {
+ mCustomButtonViews.get(i).setVisibility(View.GONE);
}
}
}
@@ -203,6 +277,9 @@ public class PipControlsView extends LinearLayout {
mCloseButtonView.reset();
mPlayPauseButtonView.reset();
mFullButtonView.requestFocus();
+ for (int i = 0; i < mCustomButtonViews.size(); i++) {
+ mCustomButtonViews.get(i).reset();
+ }
}
/**
@@ -213,6 +290,15 @@ public class PipControlsView extends LinearLayout {
}
/**
+ * Updates the set of activity-defined actions.
+ */
+ public void setActions(List<RemoteAction> actions) {
+ mCustomActions.clear();
+ mCustomActions.addAll(actions);
+ updateUserActions();
+ }
+
+ /**
* Returns the focused control button view to animate focused button.
*/
PipControlButtonView getFocusedButton() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index f98310dd7aa1..ca58080c0260 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
+import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -124,6 +125,7 @@ public class PipManager implements BasePipManager {
private MediaController mPipMediaController;
private String[] mLastPackagesResourceGranted;
private PipNotification mPipNotification;
+ private ParceledListSlice mCustomActions;
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
@@ -187,7 +189,14 @@ public class PipManager implements BasePipManager {
}
@Override
- public void onActionsChanged(ParceledListSlice actions) {}
+ public void onActionsChanged(ParceledListSlice actions) {
+ mCustomActions = actions;
+ mHandler.post(() -> {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onPipMenuActionsChanged(mCustomActions);
+ }
+ });
+ }
}
private PipManager() { }
@@ -432,6 +441,7 @@ public class PipManager implements BasePipManager {
}
Intent intent = new Intent(mContext, PipMenuActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(PipMenuActivity.EXTRA_CUSTOM_ACTIONS, mCustomActions);
mContext.startActivity(intent);
}
@@ -690,6 +700,8 @@ public class PipManager implements BasePipManager {
void onPipActivityClosed();
/** Invoked when the PIP menu gets shown. */
void onShowPipMenu();
+ /** Invoked when the PIP menu actions change. */
+ void onPipMenuActionsChanged(ParceledListSlice actions);
/** Invoked when the PIPed activity is about to return back to the fullscreen. */
void onMoveToFullscreen();
/** Invoked when we are above to start resizing the Pip. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index ce1bea19ef60..82018ce9ddbe 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -19,22 +19,27 @@ package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.view.View;
import com.android.systemui.R;
+import java.util.Collections;
/**
* Activity to show the PIP menu to control PIP.
*/
public class PipMenuActivity extends Activity implements PipManager.Listener {
private static final String TAG = "PipMenuActivity";
+ static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
+
private final PipManager mPipManager = PipManager.getInstance();
private Animator mFadeInAnimation;
private Animator mFadeOutAnimation;
- private View mPipControlsView;
+ private PipControlsView mPipControlsView;
private boolean mRestorePipSizeWhenClose;
@Override
@@ -51,6 +56,15 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
mFadeOutAnimation = AnimatorInflater.loadAnimator(
this, R.anim.tv_pip_menu_fade_out_animation);
mFadeOutAnimation.setTarget(mPipControlsView);
+
+ onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
}
private void restorePipAndFinish() {
@@ -96,6 +110,12 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
}
@Override
+ public void onPipMenuActionsChanged(ParceledListSlice actions) {
+ boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
+ mPipControlsView.setActions(hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
+ }
+
+ @Override
public void onShowPipMenu() { }
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index c8f418554904..f0745a0791ef 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -23,6 +23,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
@@ -81,6 +82,11 @@ public class PipNotification {
}
@Override
+ public void onPipMenuActionsChanged(ParceledListSlice actions) {
+ // no-op.
+ }
+
+ @Override
public void onMoveToFullscreen() {
dismissPipNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
index eaf715ffc4c9..5b3ec08ce752 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
@@ -35,9 +35,7 @@ public class CellTileView extends SignalTileView {
public CellTileView(Context context) {
super(context);
mSignalDrawable = new SignalDrawable(mContext);
- float dark = Utils.getColorAttr(context, android.R.attr.colorForeground) == 0xff000000
- ? 1 : 0;
- mSignalDrawable.setDarkIntensity(dark);
+ mSignalDrawable.setDarkIntensity(isDark(mContext));
mSignalDrawable.setIntrinsicSize(context.getResources().getDimensionPixelSize(
R.dimen.qs_tile_icon_size));
}
@@ -50,6 +48,10 @@ public class CellTileView extends SignalTileView {
}
}
+ private static int isDark(Context context) {
+ return Utils.getColorAttr(context, android.R.attr.colorForeground) == 0xff000000 ? 1 : 0;
+ }
+
public static class SignalIcon extends Icon {
private final int mState;
@@ -64,7 +66,11 @@ public class CellTileView extends SignalTileView {
@Override
public Drawable getDrawable(Context context) {
- return null;
+ //TODO: Not the optimal solution to create this drawable
+ SignalDrawable d = new SignalDrawable(context);
+ d.setDarkIntensity(isDark(context));
+ d.setLevel(getState());
+ return d;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index bd59fb03d59a..82e6a3562c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -106,6 +106,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
mPanel.setPanelScrimMinFraction((float) expandedHeight
/ mPanel.getMaxPanelHeight());
mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
+ mPanel.startExpandingFromPeek();
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
mHeadsUpManager.unpinAll();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index b1d82b1198a3..0b46c2111c88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -406,6 +406,10 @@ public abstract class PanelView extends FrameLayout {
return Math.abs(yDiff) >= Math.abs(xDiff);
}
+ protected void startExpandingFromPeek() {
+ mStatusBar.handlePeekToExpandTransistion();
+ }
+
protected void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
mInitialOffsetOnTouch = expandedHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index cfbdc9241a98..f58fe8290a43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -648,7 +648,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// Tracks notifications currently visible in mNotificationStackScroller and
// emits visibility events via NoMan on changes.
- private final Runnable mVisibilityReporter = new Runnable() {
+ protected final Runnable mVisibilityReporter = new Runnable() {
private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications =
new ArraySet<>();
private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications =
@@ -3755,6 +3755,17 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
+ void handlePeekToExpandTransistion() {
+ try {
+ // consider the transition from peek to expanded to be a panel open,
+ // but not one that clears notification effects.
+ int notificationLoad = mNotificationData.getActiveNotifications().size();
+ mBarService.onPanelRevealed(false, notificationLoad);
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ }
+
/**
* The LEDs are turned off when the notification panel is shown, even just a little bit.
* See also StatusBar.setPanelExpanded for another place where we attempt to do this.
@@ -3770,8 +3781,6 @@ public class StatusBar extends SystemUI implements DemoMode,
int notificationLoad = mNotificationData.getActiveNotifications().size();
if (pinnedHeadsUp && isPanelFullyCollapsed()) {
notificationLoad = 1;
- } else {
- mMetricsLogger.histogram("note_load", notificationLoad);
}
mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
} else {
@@ -5041,6 +5050,12 @@ public class StatusBar extends SystemUI implements DemoMode,
mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ // When in wake-and-unlock we may not have received a change to mState
+ // but we still should not be dozing, manually set to false.
+ if (mFingerprintUnlockController.getMode() ==
+ FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ mDozing = false;
+ }
mStatusBarWindowManager.setDozing(mDozing);
updateDozingState();
Trace.endSection();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index db6647c10aca..0e3ea7a07e7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -20,37 +20,54 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
import android.app.Notification;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IPowerManager;
-import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.support.test.metricshelper.MetricsAsserts;
import android.support.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.MessageHandler;
+import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -58,21 +75,26 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
public class StatusBarTest extends SysuiTestCase {
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
UnlockMethodCache mUnlockMethodCache;
KeyguardIndicationController mKeyguardIndicationController;
NotificationStackScrollLayout mStackScroller;
- StatusBar mStatusBar;
+ TestableStatusBar mStatusBar;
FakeMetricsLogger mMetricsLogger;
HeadsUpManager mHeadsUpManager;
NotificationData mNotificationData;
PowerManager mPowerManager;
SystemServicesProxy mSystemServicesProxy;
NotificationPanelView mNotificationPanelView;
+ IStatusBarService mBarService;
+ ArrayList<Entry> mNotificationList;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@Before
@@ -86,18 +108,20 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationData = mock(NotificationData.class);
mSystemServicesProxy = mock(SystemServicesProxy.class);
mNotificationPanelView = mock(NotificationPanelView.class);
+ mNotificationList = mock(ArrayList.class);
IPowerManager powerManagerService = mock(IPowerManager.class);
HandlerThread handlerThread = new HandlerThread("TestThread");
handlerThread.start();
mPowerManager = new PowerManager(mContext, powerManagerService,
new Handler(handlerThread.getLooper()));
when(powerManagerService.isInteractive()).thenReturn(true);
+ mBarService = mock(IStatusBarService.class);
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
- mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView);
-
+ mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
+ mBarService);
doAnswer(invocation -> {
OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
onDismissAction.onDismiss();
@@ -111,6 +135,15 @@ public class StatusBarTest extends SysuiTestCase {
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
when(mStackScroller.getActivatedChild()).thenReturn(null);
+ TestableLooper.get(this).setMessageHandler(new MessageHandler() {
+ @Override
+ public boolean onMessageHandled(Message m) {
+ if (m.getCallback() == mStatusBar.mVisibilityReporter) {
+ return false;
+ }
+ return true;
+ }
+ });
}
@Test
@@ -284,11 +317,80 @@ public class StatusBarTest extends SysuiTestCase {
assertFalse(mStatusBar.shouldPeek(entry, sbn));
}
+ @Test
+ public void testLogHidden() {
+ try {
+ mStatusBar.handleVisibleToUserChanged(false);
+ verify(mBarService, times(1)).onPanelHidden();
+ verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
+ } catch (RemoteException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void testPanelOpenForPeek() {
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+ when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
+ when(mNotificationList.size()).thenReturn(5);
+ when(mNotificationPanelView.isFullyCollapsed()).thenReturn(true);
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+
+ try {
+ mStatusBar.handleVisibleToUserChanged(true);
+
+ verify(mBarService, never()).onPanelHidden();
+ verify(mBarService, times(1)).onPanelRevealed(false, 1);
+ } catch (RemoteException e) {
+ fail();
+ }
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ @Test
+ public void testPanelOpenAndClear() {
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+ when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
+ when(mNotificationList.size()).thenReturn(5);
+ when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false);
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+
+ try {
+ mStatusBar.handleVisibleToUserChanged(true);
+
+ verify(mBarService, never()).onPanelHidden();
+ verify(mBarService, times(1)).onPanelRevealed(true, 5);
+ } catch (RemoteException e) {
+ fail();
+ }
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ @Test
+ public void testPanelOpenAndNoClear() {
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+ when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
+ when(mNotificationList.size()).thenReturn(5);
+ when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false);
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+
+ try {
+ mStatusBar.handleVisibleToUserChanged(true);
+
+ verify(mBarService, never()).onPanelHidden();
+ verify(mBarService, times(1)).onPanelRevealed(false, 5);
+ } catch (RemoteException e) {
+ fail();
+ }
+ TestableLooper.get(this).processAllMessages();
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
- PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView) {
+ PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
+ IStatusBarService barService) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
@@ -299,11 +401,11 @@ public class StatusBarTest extends SysuiTestCase {
mPowerManager = pm;
mSystemServicesProxy = ssp;
mNotificationPanel = panelView;
+ mBarService = barService;
}
- @Override
- protected H createHandler() {
- return null;
+ public void setBarStateForTest(int state) {
+ mState = state;
}
}
} \ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fa78f10f3e85..7ddc1a226194 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -243,6 +243,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback;
+ private boolean mIsAccessibilityButtonShown;
+
private UserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -881,21 +883,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
/**
- * Invoked remotely over AIDL by SysUi when the availability of the accessibility
+ * Invoked remotely over AIDL by SysUi when the visibility of the accessibility
* button within the system's navigation area has changed.
*
- * @param available {@code true} if the accessibility button is available to the
+ * @param shown {@code true} if the accessibility button is shown to the
* user, {@code false} otherwise
*/
@Override
- public void notifyAccessibilityButtonAvailabilityChanged(boolean available) {
+ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
+ android.Manifest.permission.STATUS_BAR_SERVICE);
}
synchronized (mLock) {
- notifyAccessibilityButtonAvailabilityChangedLocked(available);
+ notifyAccessibilityButtonVisibilityChangedLocked(shown);
}
}
@@ -1200,13 +1202,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- private void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
+ private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
final UserState state = getCurrentUserStateLocked();
- state.mIsAccessibilityButtonAvailable = available;
+ mIsAccessibilityButtonShown = available;
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final Service service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
- service.notifyAccessibilityButtonAvailabilityChangedLocked(available);
+ service.notifyAccessibilityButtonAvailabilityChangedLocked(
+ service.isAccessibilityButtonAvailableLocked(state));
}
}
}
@@ -1733,7 +1736,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
- updateAccessibilityButtonTargets(userState);
+ updateAccessibilityButtonTargetsLocked(userState);
}
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -2183,18 +2186,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- private void updateAccessibilityButtonTargets(UserState userState) {
- final List<Service> services;
- synchronized (mLock) {
- services = userState.mBoundServices;
- int numServices = services.size();
- for (int i = 0; i < numServices; i++) {
- final Service service = services.get(i);
- if (service.mRequestAccessibilityButton) {
- boolean available = service.mComponentName.equals(
- userState.mServiceAssignedToAccessibilityButton);
- service.notifyAccessibilityButtonAvailabilityChangedLocked(available);
- }
+ private void updateAccessibilityButtonTargetsLocked(UserState userState) {
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = userState.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ service.notifyAccessibilityButtonAvailabilityChangedLocked(
+ service.isAccessibilityButtonAvailableLocked(userState));
}
}
}
@@ -2501,7 +2498,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: {
showAccessibilityButtonTargetSelection();
- }
+ } break;
}
}
@@ -2656,6 +2653,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mRequestAccessibilityButton;
+ boolean mReceivedAccessibilityButtonCallbackSinceBind;
+
+ boolean mLastAccessibilityButtonCallbackState;
+
int mFetchFlags;
long mNotificationTimeout;
@@ -3596,9 +3597,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
userState = getCurrentUserStateLocked();
+ return isAccessibilityButtonAvailableLocked(userState);
}
-
- return mRequestAccessibilityButton && userState.mIsAccessibilityButtonAvailable;
}
@Override
@@ -3656,6 +3656,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mService = null;
}
mServiceInterface = null;
+ mReceivedAccessibilityButtonCallbackSinceBind = false;
}
public boolean isConnectedLocked() {
@@ -3728,6 +3729,48 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private boolean isAccessibilityButtonAvailableLocked(UserState userState) {
+ // If the service does not request the accessibility button, it isn't available
+ if (!mRequestAccessibilityButton) {
+ return false;
+ }
+
+ // If the accessibility button isn't currently shown, it cannot be available to services
+ if (!mIsAccessibilityButtonShown) {
+ return false;
+ }
+
+ // If magnification is on and assigned to the accessibility button, services cannot be
+ if (userState.mIsNavBarMagnificationEnabled
+ && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ return false;
+ }
+
+ int requestingServices = 0;
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = userState.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ requestingServices++;
+ }
+ }
+
+ if (requestingServices == 1) {
+ // If only a single service is requesting, it must be this service, and the
+ // accessibility button is available to it
+ return true;
+ } else {
+ // With more than one active service, we derive the target from the user's settings
+ if (userState.mServiceAssignedToAccessibilityButton == null) {
+ // If the user has not made an assignment, we treat the button as available to
+ // all services until the user interacts with the button to make an assignment
+ return true;
+ } else {
+ // If an assignment was made, it defines availability
+ return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
+ }
+ }
+ }
+
/**
* Notifies an accessibility service client for a scheduled event given the event type.
*
@@ -3875,6 +3918,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) {
+ // Only notify the service if it's not been notified or the state has changed
+ if (mReceivedAccessibilityButtonCallbackSinceBind
+ && (mLastAccessibilityButtonCallbackState == available)) {
+ return;
+ }
+ mReceivedAccessibilityButtonCallbackSinceBind = true;
+ mLastAccessibilityButtonCallbackState = available;
final IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
@@ -4874,7 +4924,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public int mSoftKeyboardShowMode = 0;
- public boolean mIsAccessibilityButtonAvailable;
public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
public ComponentName mServiceAssignedToAccessibilityButton;
@@ -4954,9 +5003,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
mSoftKeyboardShowMode = 0;
-
- // Clear state tracked from system UI
- mIsAccessibilityButtonAvailable = false;
}
public void destroyUiAutomationService() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 073d7b20ed64..3ae05115217a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -255,7 +255,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ViewNode node = nodes[i];
if (node == null) {
- Slog.w(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id);
+ if (sVerbose) {
+ Slog.v(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id);
+ }
continue;
}
@@ -862,11 +864,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final int numContexts = mContexts.size();
for (int i = 0; i < numContexts; i++) {
final FillContext context = mContexts.get(i);
- // TODO: create a function that gets just one node so it doesn't create an array
- // unnecessarily
- final ViewNode[] nodes = context.findViewNodesByAutofillIds(id);
- if (nodes != null) {
- AutofillValue candidate = nodes[0].getAutofillValue();
+ final ViewNode node = context.findViewNodeByAutofillId(id);
+ if (node != null) {
+ final AutofillValue candidate = node.getAutofillValue();
if (sDebug) {
Slog.d(TAG, "getValueFromContexts(" + id + ") at " + i + ": " + candidate);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 73f1705e35df..4810f4fe8c82 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -47,6 +47,7 @@ import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.IInterface;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -345,7 +346,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private static boolean isCallerSystem() {
- return getCallingUserId() == UserHandle.USER_SYSTEM;
+ return Binder.getCallingUid() == Process.SYSTEM_UID;
}
private ServiceConnection createServiceConnection(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c41748424b10..756e2748b8cb 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -81,6 +81,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.EventLog;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -175,6 +176,9 @@ public final class ActiveServices {
long mStartVisibleTime;
long mEndTime;
int mNumActive;
+
+ // Temp output of foregroundAppShownEnoughLocked
+ long mHideTime;
}
/**
@@ -622,19 +626,19 @@ public final class ActiveServices {
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
- String compName = service.name.flattenToShortString();
- EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
- StringBuilder sb = new StringBuilder(64);
- sb.append("Stopping service due to app idle: ");
- UserHandle.formatUid(sb, service.appInfo.uid);
- sb.append(" ");
- TimeUtils.formatDuration(service.createTime
- - SystemClock.elapsedRealtime(), sb);
- sb.append(" ");
- sb.append(compName);
- Slog.w(TAG, sb.toString());
- stopping.add(service);
}
+ String compName = service.name.flattenToShortString();
+ EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("Stopping service due to app idle: ");
+ UserHandle.formatUid(sb, service.appInfo.uid);
+ sb.append(" ");
+ TimeUtils.formatDuration(service.createTime
+ - SystemClock.elapsedRealtime(), sb);
+ sb.append(" ");
+ sb.append(compName);
+ Slog.w(TAG, sb.toString());
+ stopping.add(service);
}
}
}
@@ -736,50 +740,90 @@ public final class ActiveServices {
}
}
+ boolean foregroundAppShownEnoughLocked(ActiveForegroundApp aa, long nowElapsed) {
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Shown enough: pkg=" + aa.mPackageName + ", uid="
+ + aa.mUid);
+ boolean canRemove = false;
+ aa.mHideTime = Long.MAX_VALUE;
+ if (aa.mShownWhileTop) {
+ // If the app was ever at the top of the screen while the foreground
+ // service was running, then we can always just immediately remove it.
+ canRemove = true;
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - shown while on top");
+ } else if (mScreenOn || aa.mShownWhileScreenOn) {
+ final long minTime = aa.mStartVisibleTime
+ + (aa.mStartTime != aa.mStartVisibleTime
+ ? mAm.mConstants.FGSERVICE_SCREEN_ON_AFTER_TIME
+ : mAm.mConstants.FGSERVICE_MIN_SHOWN_TIME);
+ if (nowElapsed >= minTime) {
+ // If shown while the screen is on, and it has been shown for
+ // at least the minimum show time, then we can now remove it.
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - shown long enough with screen on");
+ canRemove = true;
+ } else {
+ // This is when we will be okay to stop telling the user.
+ long reportTime = nowElapsed + mAm.mConstants.FGSERVICE_MIN_REPORT_TIME;
+ aa.mHideTime = reportTime > minTime ? reportTime : minTime;
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "NO -- wait " + (aa.mHideTime-nowElapsed)
+ + " with screen on");
+ }
+ } else {
+ final long minTime = aa.mEndTime
+ + mAm.mConstants.FGSERVICE_SCREEN_ON_BEFORE_TIME;
+ if (nowElapsed >= minTime) {
+ // If the foreground service has only run while the screen is
+ // off, but it has been gone now for long enough that we won't
+ // care to tell the user about it when the screen comes back on,
+ // then we can remove it now.
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - gone long enough with screen off");
+ canRemove = true;
+ } else {
+ // This is when we won't care about this old fg service.
+ aa.mHideTime = minTime;
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "NO -- wait " + (aa.mHideTime-nowElapsed)
+ + " with screen off");
+ }
+ }
+ return canRemove;
+ }
+
void updateForegroundApps(ServiceMap smap) {
// This is called from the handler without the lock held.
ArrayList<ActiveForegroundApp> active = null;
synchronized (mAm) {
final long now = SystemClock.elapsedRealtime();
- final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
long nextUpdateTime = Long.MAX_VALUE;
if (smap != null) {
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Updating foreground apps for user "
+ + smap.mUserId);
for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i);
- if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) {
- if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
- // Check to see if this should still be displayed... we continue
- // until it has been shown for at least the timeout duration.
- if (nowPlusMin >= aa.mStartVisibleTime) {
- // All over!
- smap.mActiveForegroundApps.removeAt(i);
- smap.mActiveForegroundAppsChanged = true;
- continue;
- } else {
- long hideTime = aa.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
- if (hideTime < nextUpdateTime) {
- nextUpdateTime = hideTime;
- }
- }
- } else {
+ if (aa.mEndTime != 0) {
+ boolean canRemove = foregroundAppShownEnoughLocked(aa, now);
+ if (canRemove) {
// This was up for longer than the timeout, so just remove immediately.
smap.mActiveForegroundApps.removeAt(i);
smap.mActiveForegroundAppsChanged = true;
continue;
}
+ if (aa.mHideTime < nextUpdateTime) {
+ nextUpdateTime = aa.mHideTime;
+ }
}
if (!aa.mAppOnTop) {
if (active == null) {
active = new ArrayList<>();
}
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Adding active: pkg="
+ + aa.mPackageName + ", uid=" + aa.mUid);
active.add(aa);
}
}
smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
if (nextUpdateTime < Long.MAX_VALUE) {
- Message msg = smap.obtainMessage();
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Next update time in: "
+ + (nextUpdateTime-now));
+ Message msg = smap.obtainMessage(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
smap.sendMessageAtTime(msg, nextUpdateTime
+ SystemClock.uptimeMillis() - SystemClock.elapsedRealtime());
}
@@ -882,15 +926,14 @@ public final class ActiveServices {
active.mNumActive--;
if (active.mNumActive <= 0) {
active.mEndTime = SystemClock.elapsedRealtime();
- if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Ended running of service");
+ if (foregroundAppShownEnoughLocked(active, active.mEndTime)) {
// Have been active for long enough that we will remove it immediately.
smap.mActiveForegroundApps.remove(r.packageName);
smap.mActiveForegroundAppsChanged = true;
requestUpdateActiveForegroundAppsLocked(smap, 0);
- } else {
- requestUpdateActiveForegroundAppsLocked(smap, active.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME);
+ } else if (active.mHideTime < Long.MAX_VALUE){
+ requestUpdateActiveForegroundAppsLocked(smap, active.mHideTime);
}
}
}
@@ -904,26 +947,44 @@ public final class ActiveServices {
// services that were started while the screen was off.
if (screenOn) {
final long nowElapsed = SystemClock.elapsedRealtime();
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Screen turned on");
for (int i = mServiceMap.size()-1; i >= 0; i--) {
ServiceMap smap = mServiceMap.valueAt(i);
+ long nextUpdateTime = Long.MAX_VALUE;
boolean changed = false;
for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
- if (!active.mShownWhileScreenOn) {
- changed = true;
- active.mShownWhileScreenOn = mScreenOn;
- active.mStartVisibleTime = nowElapsed;
- if (active.mEndTime != 0) {
- active.mEndTime = nowElapsed;
+ if (active.mEndTime == 0) {
+ if (!active.mShownWhileScreenOn) {
+ active.mShownWhileScreenOn = true;
+ active.mStartVisibleTime = nowElapsed;
+ }
+ } else {
+ if (!active.mShownWhileScreenOn
+ && active.mStartVisibleTime == active.mStartTime) {
+ // If this was never shown while the screen was on, then we will
+ // count the time it started being visible as now, to tell the user
+ // about it now that they have a screen to look at.
+ active.mEndTime = active.mStartVisibleTime = nowElapsed;
+ }
+ if (foregroundAppShownEnoughLocked(active, nowElapsed)) {
+ // Have been active for long enough that we will remove it
+ // immediately.
+ smap.mActiveForegroundApps.remove(active.mPackageName);
+ smap.mActiveForegroundAppsChanged = true;
+ changed = true;
+ } else {
+ if (active.mHideTime < nextUpdateTime) {
+ nextUpdateTime = active.mHideTime;
+ }
}
}
}
if (changed) {
- requestUpdateActiveForegroundAppsLocked(smap,
- nowElapsed + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME);
- } else if (smap.mActiveForegroundApps.size() > 0) {
- // Just being paranoid.
+ // Need to immediately update.
requestUpdateActiveForegroundAppsLocked(smap, 0);
+ } else if (nextUpdateTime < Long.MAX_VALUE) {
+ requestUpdateActiveForegroundAppsLocked(smap, nextUpdateTime);
}
}
}
@@ -2318,7 +2379,7 @@ public final class ActiveServices {
return true;
}
- // Is someone still bound to us keepign us running?
+ // Is someone still bound to us keeping us running?
if (!knowConn) {
hasConn = r.hasAutoCreateConnections();
}
@@ -3741,6 +3802,17 @@ public final class ActiveServices {
pw.println();
}
}
+ if (smap.hasMessagesOrCallbacks()) {
+ if (needSep) {
+ pw.println();
+ }
+ printedAnything = true;
+ needSep = true;
+ pw.print(" Handler - user ");
+ pw.print(user);
+ pw.println(":");
+ smap.dumpMine(new PrintWriterPrinter(pw), " ");
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 5749f31ddb2e..6c3fe915f386 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -35,8 +35,14 @@ final class ActivityManagerConstants extends ContentObserver {
// Key names stored in the settings value.
private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
- private static final String KEY_FOREGROUND_SERVICE_UI_MIN_TIME
- = "foreground_service_ui_min_time";
+ private static final String KEY_FGSERVICE_MIN_SHOWN_TIME
+ = "fgservice_min_shown_time";
+ private static final String KEY_FGSERVICE_MIN_REPORT_TIME
+ = "fgservice_min_report_time";
+ private static final String KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME
+ = "fgservice_screen_on_before_time";
+ private static final String KEY_FGSERVICE_SCREEN_ON_AFTER_TIME
+ = "fgservice_screen_on_after_time";
private static final String KEY_CONTENT_PROVIDER_RETAIN_TIME = "content_provider_retain_time";
private static final String KEY_GC_TIMEOUT = "gc_timeout";
private static final String KEY_GC_MIN_INTERVAL = "gc_min_interval";
@@ -58,7 +64,10 @@ final class ActivityManagerConstants extends ContentObserver {
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
- private static final long DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME = 30*1000;
+ private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
+ private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
+ private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
+ private static final long DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME = 5*1000;
private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
private static final long DEFAULT_GC_TIMEOUT = 5*1000;
private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000;
@@ -85,8 +94,26 @@ final class ActivityManagerConstants extends ContentObserver {
// before we start restricting what it can do.
public long BACKGROUND_SETTLE_TIME = DEFAULT_BACKGROUND_SETTLE_TIME;
- // The minimum time a foreground service will be shown as running in the notification UI.
- public long FOREGROUND_SERVICE_UI_MIN_TIME = DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME;
+ // The minimum time we allow a foreground service to run with a notification and the
+ // screen on without otherwise telling the user about it. (If it runs for less than this,
+ // it will still be reported to the user as a running app for at least this amount of time.)
+ public long FGSERVICE_MIN_SHOWN_TIME = DEFAULT_FGSERVICE_MIN_SHOWN_TIME;
+
+ // If a foreground service is shown for less than FGSERVICE_MIN_SHOWN_TIME, we will display
+ // the background app running notification about it for at least this amount of time (if it
+ // is larger than the remaining shown time).
+ public long FGSERVICE_MIN_REPORT_TIME = DEFAULT_FGSERVICE_MIN_REPORT_TIME;
+
+ // The minimum amount of time the foreground service needs to have remain being shown
+ // before the screen goes on for us to consider it not worth showing to the user. That is
+ // if an app has a foreground service that stops itself this amount of time or more before
+ // the user turns on the screen, we will just let it go without the user being told about it.
+ public long FGSERVICE_SCREEN_ON_BEFORE_TIME = DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME;
+
+ // The minimum amount of time a foreground service should remain reported to the user if
+ // it is stopped when the screen turns on. This is the time from when the screen turns
+ // on until we will stop reporting it.
+ public long FGSERVICE_SCREEN_ON_AFTER_TIME = DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME;
// How long we will retain processes hosting content providers in the "last activity"
// state before allowing them to drop down to the regular cached LRU list. This is
@@ -225,8 +252,14 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_MAX_CACHED_PROCESSES);
BACKGROUND_SETTLE_TIME = mParser.getLong(KEY_BACKGROUND_SETTLE_TIME,
DEFAULT_BACKGROUND_SETTLE_TIME);
- FOREGROUND_SERVICE_UI_MIN_TIME = mParser.getLong(KEY_FOREGROUND_SERVICE_UI_MIN_TIME,
- DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME);
+ FGSERVICE_MIN_SHOWN_TIME = mParser.getLong(KEY_FGSERVICE_MIN_SHOWN_TIME,
+ DEFAULT_FGSERVICE_MIN_SHOWN_TIME);
+ FGSERVICE_MIN_REPORT_TIME = mParser.getLong(KEY_FGSERVICE_MIN_REPORT_TIME,
+ DEFAULT_FGSERVICE_MIN_REPORT_TIME);
+ FGSERVICE_SCREEN_ON_BEFORE_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME,
+ DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME);
+ FGSERVICE_SCREEN_ON_AFTER_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME,
+ DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME);
CONTENT_PROVIDER_RETAIN_TIME = mParser.getLong(KEY_CONTENT_PROVIDER_RETAIN_TIME,
DEFAULT_CONTENT_PROVIDER_RETAIN_TIME);
GC_TIMEOUT = mParser.getLong(KEY_GC_TIMEOUT,
@@ -284,8 +317,14 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(MAX_CACHED_PROCESSES);
pw.print(" "); pw.print(KEY_BACKGROUND_SETTLE_TIME); pw.print("=");
pw.println(BACKGROUND_SETTLE_TIME);
- pw.print(" "); pw.print(KEY_FOREGROUND_SERVICE_UI_MIN_TIME); pw.print("=");
- pw.println(FOREGROUND_SERVICE_UI_MIN_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_MIN_SHOWN_TIME); pw.print("=");
+ pw.println(FGSERVICE_MIN_SHOWN_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_MIN_REPORT_TIME); pw.print("=");
+ pw.println(FGSERVICE_MIN_REPORT_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME); pw.print("=");
+ pw.println(FGSERVICE_SCREEN_ON_BEFORE_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME); pw.print("=");
+ pw.println(FGSERVICE_SCREEN_ON_AFTER_TIME);
pw.print(" "); pw.print(KEY_CONTENT_PROVIDER_RETAIN_TIME); pw.print("=");
pw.println(CONTENT_PROVIDER_RETAIN_TIME);
pw.print(" "); pw.print(KEY_GC_TIMEOUT); pw.print("=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index ff5efde27f93..f440100b7621 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -79,6 +79,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_SCREENSHOTS = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
+ static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
static final boolean DEBUG_STACK = DEBUG_ALL || false;
static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac68a9ea6dc2..14be25b6690f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10254,11 +10254,11 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
synchronized(this) {
- moveTaskToFrontLocked(taskId, flags, bOptions);
+ moveTaskToFrontLocked(taskId, flags, bOptions, false /* fromRecents */);
}
}
- void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions) {
+ void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions, boolean fromRecents) {
ActivityOptions options = ActivityOptions.fromBundle(bOptions);
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
@@ -10291,7 +10291,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// We are reshowing a task, use a starting window to hide the initial draw delay
// so the transition can start earlier.
topActivity.showStartingWindow(null /* prev */, false /* newTask */,
- true /* taskSwitch */);
+ true /* taskSwitch */, fromRecents);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -23752,9 +23752,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void notifyAppTransitionStarting(SparseIntArray reasons) {
+ public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
synchronized (ActivityManagerService.this) {
- mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reasons);
+ mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(
+ reasons, timestamp);
}
}
@@ -24014,6 +24015,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (reason != null) {
pw.println(" Reason: " + reason);
}
+ pw.println(" mLastHomeActivityStartResult: "
+ + mActivityStarter.mLastHomeActivityStartResult);
+ final ActivityRecord r = mActivityStarter.mLastHomeActivityStartRecord[0];
+ if (r != null) {
+ pw.println(" mLastHomeActivityStartRecord:");
+ r.dump(pw, " ");
+ }
pw.println();
dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index bf7b663454b8..98815d7e18c7 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -230,12 +230,12 @@ class ActivityMetricsLogger {
/**
* Notifies the tracker that all windows of the app have been drawn.
*/
- void notifyWindowsDrawn(int stackId) {
+ void notifyWindowsDrawn(int stackId, long timestamp) {
final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
if (info == null || info.loggedWindowsDrawn) {
return;
}
- info.windowsDrawnDelayMs = calculateCurrentDelay();
+ info.windowsDrawnDelayMs = calculateDelay(timestamp);
info.loggedWindowsDrawn = true;
if (allStacksWindowsDrawn() && mLoggedTransitionStarting) {
reset(false /* abort */);
@@ -245,13 +245,13 @@ class ActivityMetricsLogger {
/**
* Notifies the tracker that the starting window was drawn.
*/
- void notifyStartingWindowDrawn(int stackId) {
+ void notifyStartingWindowDrawn(int stackId, long timestamp) {
final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
if (info == null || info.loggedStartingWindowDrawn) {
return;
}
info.loggedStartingWindowDrawn = true;
- info.startingWindowDelayMs = calculateCurrentDelay();
+ info.startingWindowDelayMs = calculateDelay(timestamp);
}
/**
@@ -260,11 +260,11 @@ class ActivityMetricsLogger {
* @param stackIdReasons A map from stack id to a reason integer, which must be on of
* ActivityManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(SparseIntArray stackIdReasons) {
+ void notifyTransitionStarting(SparseIntArray stackIdReasons, long timestamp) {
if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
return;
}
- mCurrentTransitionDelayMs = calculateCurrentDelay();
+ mCurrentTransitionDelayMs = calculateDelay(timestamp);
mLoggedTransitionStarting = true;
for (int index = stackIdReasons.size() - 1; index >= 0; index--) {
final int stackId = stackIdReasons.keyAt(index);
@@ -344,6 +344,11 @@ class ActivityMetricsLogger {
return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
}
+ private int calculateDelay(long timestamp) {
+ // Shouldn't take more than 25 days to launch an app, so int is fine here.
+ return (int) (timestamp - mCurrentTransitionStartTime);
+ }
+
private void logAppTransitionMultiEvents() {
for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
final StackTransitionInfo info = mStackTransitionInfo.valueAt(index);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index ec6a4f6b7f7c..68f4d0d99f93 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1928,18 +1928,19 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
@Override
- public void onStartingWindowDrawn() {
+ public void onStartingWindowDrawn(long timestamp) {
synchronized (service) {
- mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(getStackId());
+ mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(
+ getStackId(), timestamp);
}
}
@Override
- public void onWindowsDrawn() {
+ public void onWindowsDrawn(long timestamp) {
synchronized (service) {
- mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId());
+ mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId(), timestamp);
if (displayStartTime != 0) {
- reportLaunchTimeLocked(SystemClock.uptimeMillis());
+ reportLaunchTimeLocked(timestamp);
}
mStackSupervisor.sendWaitingVisibleReportLocked(this);
startTime = 0;
@@ -2153,6 +2154,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
+ showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */);
+ }
+
+ void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
+ boolean fromRecents) {
if (mWindowContainerController == null) {
return;
}
@@ -2167,7 +2173,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
allowTaskSnapshot(),
- state.ordinal() >= RESUMED.ordinal() && state.ordinal() <= STOPPED.ordinal());
+ state.ordinal() >= RESUMED.ordinal() && state.ordinal() <= STOPPED.ordinal(),
+ fromRecents);
if (shown) {
mStartingWindowState = STARTING_WINDOW_SHOWN;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 53afe78f2ea3..7de56fa17877 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -5131,7 +5131,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
&& task.getRootActivity() != null) {
mService.mActivityStarter.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */);
mActivityMetricsLogger.notifyActivityLaunching();
- mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
+ mService.moveTaskToFrontLocked(task.taskId, 0, bOptions, true /* fromRecents */);
mActivityMetricsLogger.notifyActivityLaunched(ActivityManager.START_TASK_TO_FRONT,
task.getTopActivity());
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 1ed2ac19ea9d..d74d1d6dc954 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -188,6 +188,11 @@ class ActivityStarter {
private boolean mUsingVr2dDisplay;
+ // Last home activity record we attempted to start
+ final ActivityRecord[] mLastHomeActivityStartRecord = new ActivityRecord[1];
+ // The result of the last home activity we attempted to start.
+ int mLastHomeActivityStartResult;
+
private void reset() {
mStartActivity = null;
mIntent = null;
@@ -592,12 +597,13 @@ class ActivityStarter {
void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
- startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
- null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
- null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
- 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
- 0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
- false /*ignoreTargetSecurity*/, false /*componentSpecified*/, null /*outActivity*/,
+ mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent,
+ null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
+ null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
+ null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
+ null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
+ 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
+ false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/,
null /*container*/, null /*inTask*/);
if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 639b7a911998..b3a2c291760c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -614,9 +614,10 @@ public final class BroadcastQueue {
skip = true;
}
- if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) {
+ if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
+ || filter.receiverList.app.crashing)) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
- + " to " + filter.receiverList + ": process crashing");
+ + " to " + filter.receiverList + ": process gone or crashing");
skip = true;
}
@@ -1317,7 +1318,7 @@ public final class BroadcastQueue {
}
// Is this receiver's application already running?
- if (app != null && app.thread != null) {
+ if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 9970c82e2c75..b89586dc59dc 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -69,7 +69,6 @@ public final class UserState {
public boolean setState(int oldState, int newState) {
if (state == oldState) {
setState(newState);
- EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState);
return true;
} else {
Slog.w(TAG, "Expected user " + mHandle.getIdentifier() + " in state "
@@ -84,6 +83,7 @@ public final class UserState {
}
Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from "
+ stateToString(state) + " to " + stateToString(newState));
+ EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState);
lastState = state;
state = newState;
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index f23caf2e7a76..0fdf2daf1c8d 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -30,6 +30,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.TextureView;
+import android.view.ThreadedRenderer;
import android.view.View;
import android.view.WindowManager;
import android.view.TextureView.SurfaceTextureListener;
@@ -95,6 +96,8 @@ final class OverlayDisplayWindow implements DumpUtils.Dump {
public OverlayDisplayWindow(Context context, String name,
int width, int height, int densityDpi, int gravity, boolean secure,
Listener listener) {
+ // Workaround device freeze (b/38372997)
+ ThreadedRenderer.disableVsync();
mContext = context;
mName = name;
mGravity = gravity;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 19d5f3c76ce7..bdea24704ef5 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -307,11 +307,15 @@ public class NotificationManagerService extends SystemService {
// used as a mutex for access to all active notifications & listeners
final Object mNotificationLock = new Object();
+ @GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
+ @GuardedBy("mNotificationLock")
final ArrayMap<String, NotificationRecord> mNotificationsByKey =
new ArrayMap<String, NotificationRecord>();
+ @GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
+ @GuardedBy("mNotificationLock")
final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
@@ -607,7 +611,7 @@ public class NotificationManagerService extends SystemService {
@Override
public void onPanelRevealed(boolean clearEffects, int items) {
MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
- MetricsLogger.histogram(getContext(), "notification_load", items);
+ MetricsLogger.histogram(getContext(), "note_load", items);
EventLogTags.writeNotificationPanelRevealed(items);
if (clearEffects) {
clearEffects();
@@ -2806,7 +2810,8 @@ public class NotificationManagerService extends SystemService {
// Clear summary.
final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
if (removed != null) {
- cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
+ boolean wasPosted = removeFromNotificationListsLocked(removed);
+ cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
}
}
}
@@ -3420,7 +3425,8 @@ public class NotificationManagerService extends SystemService {
.setType(MetricsEvent.TYPE_CLOSE)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
- cancelNotificationLocked(r, false, REASON_SNOOZED);
+ boolean wasPosted = removeFromNotificationListsLocked(r);
+ cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
updateLightsLocked();
if (mSnoozeCriterionId != null) {
mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
@@ -4206,15 +4212,18 @@ public class NotificationManagerService extends SystemService {
manager.sendAccessibilityEvent(event);
}
+ /**
+ * Removes all NotificationsRecords with the same key as the given notification record
+ * from both lists. Do not call this method while iterating over either list.
+ */
@GuardedBy("mNotificationLock")
- private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
- final String canceledKey = r.getKey();
-
- // Remove from both lists, either list could have a separate Record for what is effectively
- // the same notification.
+ private boolean removeFromNotificationListsLocked(NotificationRecord r) {
+ // Remove from both lists, either list could have a separate Record for what is
+ // effectively the same notification.
boolean wasPosted = false;
NotificationRecord recordInList = null;
- if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey())) != null) {
+ if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
+ != null) {
mNotificationList.remove(recordInList);
mNotificationsByKey.remove(recordInList.sbn.getKey());
wasPosted = true;
@@ -4223,6 +4232,13 @@ public class NotificationManagerService extends SystemService {
!= null) {
mEnqueuedNotifications.remove(recordInList);
}
+ return wasPosted;
+ }
+
+ @GuardedBy("mNotificationLock")
+ private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
+ boolean wasPosted) {
+ final String canceledKey = r.getKey();
// Record caller.
recordCallerLocked(r);
@@ -4363,7 +4379,8 @@ public class NotificationManagerService extends SystemService {
}
// Cancel the notification.
- cancelNotificationLocked(r, sendDelete, reason);
+ boolean wasPosted = removeFromNotificationListsLocked(r);
+ cancelNotificationLocked(r, sendDelete, reason, wasPosted);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
sendDelete);
updateLightsLocked();
@@ -4440,11 +4457,11 @@ public class NotificationManagerService extends SystemService {
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
- listenerName);
+ listenerName, true /* wasPosted */);
cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
flagChecker, false /*includeCurrentProfiles*/, userId,
- false /*sendDelete*/, reason, listenerName);
+ false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
mSnoozeHelper.cancel(userId, pkg);
}
}
@@ -4460,7 +4477,7 @@ public class NotificationManagerService extends SystemService {
private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
- boolean sendDelete, int reason, String listenerName) {
+ boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
ArrayList<NotificationRecord> canceledNotifications = null;
for (int i = notificationList.size() - 1; i >= 0; --i) {
NotificationRecord r = notificationList.get(i);
@@ -4488,8 +4505,9 @@ public class NotificationManagerService extends SystemService {
if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>();
}
+ notificationList.remove(i);
canceledNotifications.add(r);
- cancelNotificationLocked(r, sendDelete, reason);
+ cancelNotificationLocked(r, sendDelete, reason, wasPosted);
}
if (canceledNotifications != null) {
final int M = canceledNotifications.size();
@@ -4548,11 +4566,11 @@ public class NotificationManagerService extends SystemService {
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
includeCurrentProfiles, userId, true /*sendDelete*/, reason,
- listenerName);
+ listenerName, true);
cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
- reason, listenerName);
+ reason, listenerName, false);
mSnoozeHelper.cancel(userId, includeCurrentProfiles);
}
}
@@ -4569,7 +4587,6 @@ public class NotificationManagerService extends SystemService {
}
String pkg = r.sbn.getPackageName();
- int userId = r.getUserId();
if (pkg == null) {
if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
@@ -4577,15 +4594,15 @@ public class NotificationManagerService extends SystemService {
}
cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
- sendDelete);
+ sendDelete, true);
cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
- listenerName, sendDelete);
+ listenerName, sendDelete, false);
}
@GuardedBy("mNotificationLock")
private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
NotificationRecord parentNotification, int callingUid, int callingPid,
- String listenerName, boolean sendDelete) {
+ String listenerName, boolean sendDelete, boolean wasPosted) {
final String pkg = parentNotification.sbn.getPackageName();
final int userId = parentNotification.getUserId();
final int reason = REASON_GROUP_SUMMARY_CANCELED;
@@ -4597,7 +4614,8 @@ public class NotificationManagerService extends SystemService {
&& (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName);
- cancelNotificationLocked(childR, sendDelete, reason);
+ notificationList.remove(i);
+ cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 8952870b8585..6953ffddaf33 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -170,6 +171,11 @@ public final class NotificationRecord {
private Uri calculateSound() {
final Notification n = sbn.getNotification();
+ // No notification sounds on tv
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ return null;
+ }
+
Uri sound = mChannel.getSound();
if (mPreChannelsNotification && (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 4eadfd168640..d7b36aaa8008 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -53,6 +53,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -1199,6 +1200,6 @@ public class RankingHelper implements RankingConfig {
boolean showBadge = DEFAULT_SHOW_BADGE;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
- ArrayMap<String, NotificationChannelGroup> groups = new ArrayMap<>();
+ Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ef3e7bcefc17..2940a6e3fc8d 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -714,11 +714,17 @@ public final class OverlayManagerService extends SystemService {
final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
+ final List<String> frameworkOverlays =
+ mImpl.getEnabledOverlayPackageNames("android", userId);
final int N = targetPackageNames.size();
for (int i = 0; i < N; i++) {
final String targetPackageName = targetPackageNames.get(i);
- pendingChanges.put(targetPackageName,
- mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ List<String> list = new ArrayList<>();
+ if (!"android".equals(targetPackageName)) {
+ list.addAll(frameworkOverlays);
+ }
+ list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list);
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 261bcc5838f8..db6e9749535b 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -170,6 +170,7 @@ final class OverlayManagerServiceImpl {
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
updateAllOverlaysForTarget(packageName, userId, targetPackage);
+ mListener.onOverlaysChanged(packageName, userId);
}
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -178,7 +179,9 @@ final class OverlayManagerServiceImpl {
}
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- updateAllOverlaysForTarget(packageName, userId, targetPackage);
+ if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
@@ -186,7 +189,9 @@ final class OverlayManagerServiceImpl {
Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, null);
+ if (updateAllOverlaysForTarget(packageName, userId, null)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
@@ -195,7 +200,9 @@ final class OverlayManagerServiceImpl {
}
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- updateAllOverlaysForTarget(packageName, userId, targetPackage);
+ if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4540d2dfd692..f111db1434c3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -856,8 +856,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mResolvedInstructionSets.add(archSubDir.getName());
List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
- if (!oatFiles.isEmpty()) {
- mResolvedInheritedFiles.addAll(oatFiles);
+
+ // Only add compiled files associated with the base.
+ // Once b/62269291 is resolved, we can add all compiled files again.
+ for (File oatFile : oatFiles) {
+ if (oatFile.getName().equals("base.art")
+ || oatFile.getName().equals("base.odex")
+ || oatFile.getName().equals("base.vdex")) {
+ mResolvedInheritedFiles.add(oatFile);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 07d548d9d62b..6c59505c8dd0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18105,8 +18105,10 @@ public class PackageManagerService extends IPackageManager.Stub
// step during installation. Instead, we'll take extra time the first time the
// instant app starts. It's preferred to do it this way to provide continuous
// progress to the user instead of mysteriously blocking somewhere in the
- // middle of running an instant app.
- if (!instantApp) {
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ if (!instantApp || Global.getInt(
+ mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` may not be in `mPackages` yet.
@@ -19309,7 +19311,7 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
- return true;
+ return false;
}
return mSettings.getBlockUninstallLPr(userId, packageName);
}
@@ -21585,8 +21587,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
public static final int DUMP_FROZEN = 1 << 19;
public static final int DUMP_DEXOPT = 1 << 20;
public static final int DUMP_COMPILER_STATS = 1 << 21;
- public static final int DUMP_ENABLED_OVERLAYS = 1 << 22;
- public static final int DUMP_CHANGES = 1 << 23;
+ public static final int DUMP_CHANGES = 1 << 22;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -21830,8 +21831,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
dumpState.setDump(DumpState.DUMP_DEXOPT);
} else if ("compiler-stats".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
- } else if ("enabled-overlays".equals(cmd)) {
- dumpState.setDump(DumpState.DUMP_ENABLED_OVERLAYS);
} else if ("changes".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_CHANGES);
} else if ("write".equals(cmd)) {
@@ -24608,12 +24607,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
- String[] frameworkOverlayPaths = null;
- if (!"android".equals(targetPackageName)) {
- frameworkOverlayPaths =
- mSettings.mPackages.get("android").getOverlayPaths(userId);
- }
- ps.setOverlayPaths(overlayPaths, frameworkOverlayPaths, userId);
+ ps.setOverlayPaths(overlayPaths, userId);
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index d17267f38648..f68512758456 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -330,21 +330,9 @@ abstract class PackageSettingBase extends SettingBase {
modifyUserState(userId).installReason = installReason;
}
- void setOverlayPaths(List<String> overlayPaths, String[] frameworkOverlayPaths, int userId) {
- if (overlayPaths == null && frameworkOverlayPaths == null) {
- modifyUserState(userId).overlayPaths = null;
- return;
- }
- final List<String> paths;
- if (frameworkOverlayPaths == null) {
- paths = overlayPaths;
- } else {
- paths = Lists.newArrayList(frameworkOverlayPaths);
- if (overlayPaths != null) {
- paths.addAll(overlayPaths);
- }
- }
- modifyUserState(userId).overlayPaths = paths.toArray(new String[paths.size()]);
+ void setOverlayPaths(List<String> overlayPaths, int userId) {
+ modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
+ overlayPaths.toArray(new String[overlayPaths.size()]);
}
String[] getOverlayPaths(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b006c2d991e6..45d0c585627b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4861,9 +4861,9 @@ final class Settings {
String[] overlayPaths = ps.getOverlayPaths(user.id);
if (overlayPaths != null && overlayPaths.length > 0) {
- pw.println("Overlay paths:");
+ pw.print(prefix); pw.println(" overlay paths:");
for (String path : overlayPaths) {
- pw.println(path);
+ pw.print(prefix); pw.print(" "); pw.println(path);
}
}
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 7a2808146f62..b1792358be53 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -166,8 +166,14 @@ public class BarController {
return change || stateChanged;
}
- void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener) {
+ void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener,
+ boolean invokeWithState) {
mVisibilityChangeListener = listener;
+ if (invokeWithState) {
+ // Optionally report the initial window state for initialization purposes
+ mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED,
+ (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget();
+ }
}
protected boolean skipAnimation() {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 01eabd8f1403..8b28df1a3058 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1045,7 +1045,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
new BarController.OnBarVisibilityChangedListener() {
@Override
public void onBarVisibilityChanged(boolean visible) {
- mAccessibilityManager.notifyAccessibilityButtonAvailabilityChanged(visible);
+ mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
}
};
@@ -3037,7 +3037,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mNavigationBar = win;
mNavigationBarController.setWindow(win);
mNavigationBarController.setOnBarVisibilityChangedListener(
- mNavBarVisibilityListener);
+ mNavBarVisibilityListener, true);
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
case TYPE_NAVIGATION_BAR_PANEL:
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1c72cad0c6b..b1ed35867dcf 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -2044,7 +2044,10 @@ public class AppTransition implements Dump {
if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
|| mNextAppTransition == TRANSIT_NONE) {
setAppTransition(transit, flags);
- } else if (!alwaysKeepCurrent) {
+ }
+ // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
+ // relies on the fact that we always execute a Keyguard transition after preparing one.
+ else if (!alwaysKeepCurrent && !isKeyguardTransit(transit)) {
if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
// Opening a new task always supersedes a close for the anim.
setAppTransition(transit, flags);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 8cfbf68e3b5d..fe7494728ac3 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -38,8 +38,11 @@ import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.Trace;
import android.util.Slog;
+import android.view.DisplayInfo;
import android.view.IApplicationToken;
import android.view.WindowManagerPolicy.StartingSurface;
@@ -61,23 +64,38 @@ public class AppWindowContainerController
private final IApplicationToken mToken;
private final Handler mHandler;
- private final Runnable mOnStartingWindowDrawn = () -> {
- if (mListener == null) {
- return;
+ private final class H extends Handler {
+ public static final int NOTIFY_WINDOWS_DRAWN = 1;
+ public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
+
+ public H(Looper looper) {
+ super(looper);
}
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onStartingWindowDrawn();
- };
- private final Runnable mOnWindowsDrawn = () -> {
- if (mListener == null) {
- return;
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case NOTIFY_WINDOWS_DRAWN:
+ if (mListener == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ + AppWindowContainerController.this.mToken);
+ mListener.onWindowsDrawn(msg.getWhen());
+ break;
+ case NOTIFY_STARTING_WINDOW_DRAWN:
+ if (mListener == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ + AppWindowContainerController.this.mToken);
+ mListener.onStartingWindowDrawn(msg.getWhen());
+ break;
+ default:
+ break;
+ }
}
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsDrawn();
- };
+ }
private final Runnable mOnWindowsVisible = () -> {
if (mListener == null) {
@@ -212,7 +230,7 @@ public class AppWindowContainerController
int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
WindowManagerService service, Configuration overrideConfig, Rect bounds) {
super(listener, service);
- mHandler = new Handler(service.mH.getLooper());
+ mHandler = new H(service.mH.getLooper());
mToken = token;
synchronized(mWindowMap) {
AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
@@ -481,7 +499,7 @@ public class AppWindowContainerController
public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated) {
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
synchronized(mWindowMap) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
+ " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
@@ -510,11 +528,14 @@ public class AppWindowContainerController
return false;
}
+ final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
+ mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
+ false /* restoreFromDisk */, false /* reducedResolution */);
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated);
+ allowTaskSnapshot, activityCreated, fromRecents, snapshot);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
- return createSnapshot();
+ return createSnapshot(snapshot);
}
// If this is a translucent window, then don't show a starting window -- the current
@@ -582,7 +603,8 @@ public class AppWindowContainerController
}
private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated) {
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+ TaskSnapshot snapshot) {
if (mService.mAppTransition.getAppTransition() == TRANSIT_DOCK_TASK_FROM_RECENTS) {
// TODO(b/34099271): Remove this statement to add back the starting window and figure
// out why it causes flickering, the starting window appears over the thumbnail while
@@ -591,7 +613,9 @@ public class AppWindowContainerController
} else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
- return STARTING_WINDOW_TYPE_SNAPSHOT;
+ return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+ : snapshotFillsWidth(snapshot) || fromRecents ? STARTING_WINDOW_TYPE_SNAPSHOT
+ : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else {
return STARTING_WINDOW_TYPE_NONE;
}
@@ -605,11 +629,7 @@ public class AppWindowContainerController
mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
}
- private boolean createSnapshot() {
- final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
- mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
- false /* restoreFromDisk */, false /* reducedResolution */);
-
+ private boolean createSnapshot(TaskSnapshot snapshot) {
if (snapshot == null) {
return false;
}
@@ -620,6 +640,24 @@ public class AppWindowContainerController
return true;
}
+ private boolean snapshotFillsWidth(TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ return false;
+ }
+ final Rect rect = new Rect(0, 0, snapshot.getSnapshot().getWidth(),
+ snapshot.getSnapshot().getHeight());
+ rect.inset(snapshot.getContentInsets());
+ final Rect taskBoundsWithoutInsets = new Rect();
+ mContainer.getTask().getBounds(taskBoundsWithoutInsets);
+ final DisplayInfo di = mContainer.getDisplayContent().getDisplayInfo();
+ final Rect displayBounds = new Rect(0, 0, di.logicalWidth, di.logicalHeight);
+ final Rect stableInsets = new Rect();
+ mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ stableInsets);
+ displayBounds.inset(stableInsets);
+ return rect.width() >= displayBounds.width();
+ }
+
public void removeStartingWindow() {
synchronized (mWindowMap) {
if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
@@ -740,11 +778,11 @@ public class AppWindowContainerController
}
void reportStartingWindowDrawn() {
- mHandler.post(mOnStartingWindowDrawn);
+ mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
}
void reportWindowsDrawn() {
- mHandler.post(mOnWindowsDrawn);
+ mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
}
void reportWindowsVisible() {
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
index 26537f27bce8..8a39a7408058 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
@@ -19,7 +19,7 @@ package com.android.server.wm;
/** Interface used by the creator of the controller to listen to changes with the container. */
public interface AppWindowContainerListener extends WindowContainerListener {
/** Called when the windows associated app window container are drawn. */
- void onWindowsDrawn();
+ void onWindowsDrawn(long timestamp);
/** Called when the windows associated app window container are visible. */
void onWindowsVisible();
/** Called when the windows associated app window container are no longer visible. */
@@ -28,7 +28,7 @@ public interface AppWindowContainerListener extends WindowContainerListener {
/**
* Called when the starting window for this container is drawn.
*/
- void onStartingWindowDrawn();
+ void onStartingWindowDrawn(long timestamp);
/**
* Called when the key dispatching to a window associated with the app window container
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 71ecaf61da48..bd379344e18b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -394,6 +394,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
startingWindow.mPolicyVisibility = false;
startingWindow.mPolicyVisibilityAfterAnim = false;
}
+
+ // We are becoming visible, so better freeze the screen with the windows that are
+ // getting visible so we also wait for them.
+ forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true);
}
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
@@ -891,8 +895,24 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
return mPendingRelaunchCount > 0;
}
+ boolean shouldFreezeBounds() {
+ final Task task = getTask();
+
+ // For freeform windows, we can't freeze the bounds at the moment because this would make
+ // the resizing unresponsive.
+ if (task == null || task.inFreeformWorkspace()) {
+ return false;
+ }
+
+ // We freeze the bounds while drag resizing to deal with the time between
+ // the divider/drag handle being released, and the handling it's new
+ // configuration. If we are relaunched outside of the drag resizing state,
+ // we need to be careful not to do this.
+ return getTask().isDragResizing();
+ }
+
void startRelaunching() {
- if (canFreezeBounds()) {
+ if (shouldFreezeBounds()) {
freezeBounds();
}
@@ -909,9 +929,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
void finishRelaunching() {
- if (canFreezeBounds()) {
- unfreezeBounds();
- }
+ unfreezeBounds();
+
if (mPendingRelaunchCount > 0) {
mPendingRelaunchCount--;
} else {
@@ -926,9 +945,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (mPendingRelaunchCount == 0) {
return;
}
- if (canFreezeBounds()) {
- unfreezeBounds();
- }
+ unfreezeBounds();
mPendingRelaunchCount = 0;
}
@@ -1032,14 +1049,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
}
- private boolean canFreezeBounds() {
- final Task task = getTask();
-
- // For freeform windows, we can't freeze the bounds at the moment because this would make
- // the resizing unresponsive.
- return task != null && !task.inFreeformWorkspace();
- }
-
/**
* Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
* freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
@@ -1064,9 +1073,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
* Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
*/
private void unfreezeBounds() {
- if (!mFrozenBounds.isEmpty()) {
- mFrozenBounds.remove();
+ if (mFrozenBounds.isEmpty()) {
+ return;
}
+ mFrozenBounds.remove();
if (!mFrozenMergedConfig.isEmpty()) {
mFrozenMergedConfig.remove();
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 9a9e29a7eb88..6d33ce2941bc 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -155,6 +155,10 @@ class PinnedStackController {
mSnapAlgorithm = new PipSnapAlgorithm(service.mContext);
mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
reloadResources();
+ // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
+ // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
+ // triggers a configuration change and the resources to be reloaded.
+ mAspectRatio = mDefaultAspectRatio;
}
void onConfigurationChanged() {
@@ -171,7 +175,6 @@ class PinnedStackController {
mCurrentMinSize = mDefaultMinSize;
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
- mAspectRatio = mDefaultAspectRatio;
final String screenEdgeInsetsDpString = res.getString(
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 551e3bf13339..a96d22412918 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -125,6 +125,7 @@ class TaskSnapshotSurface implements StartingSurface {
private final Paint mBackgroundPaint = new Paint();
private final int mStatusBarColor;
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
+ private final int mOrientationOnCreation;
static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
TaskSnapshot snapshot) {
@@ -146,6 +147,7 @@ class TaskSnapshotSurface implements StartingSurface {
final int sysUiVis;
final int windowFlags;
final int windowPrivateFlags;
+ final int currentOrientation;
synchronized (service.mWindowMap) {
final WindowState mainWindow = token.findMainWindow();
if (mainWindow == null) {
@@ -183,6 +185,7 @@ class TaskSnapshotSurface implements StartingSurface {
} else {
taskBounds = null;
}
+ currentOrientation = mainWindow.getConfiguration().orientation;
}
try {
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
@@ -197,7 +200,8 @@ class TaskSnapshotSurface implements StartingSurface {
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
- navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds);
+ navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
+ currentOrientation);
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
@@ -215,7 +219,7 @@ class TaskSnapshotSurface implements StartingSurface {
TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
- Rect taskBounds) {
+ Rect taskBounds, int currentOrientation) {
mService = service;
mHandler = new Handler(mService.mH.getLooper());
mSession = WindowManagerGlobal.getWindowSession();
@@ -228,6 +232,7 @@ class TaskSnapshotSurface implements StartingSurface {
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor);
mStatusBarColor = statusBarColor;
+ mOrientationOnCreation = currentOrientation;
}
@Override
@@ -394,6 +399,7 @@ class TaskSnapshotSurface implements StartingSurface {
static class Window extends BaseIWindow {
private TaskSnapshotSurface mOuter;
+
public void setOuter(TaskSnapshotSurface outer) {
mOuter = outer;
}
@@ -403,6 +409,15 @@ class TaskSnapshotSurface implements StartingSurface {
Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId) {
+ if (mergedConfiguration != null && mOuter != null
+ && mOuter.mOrientationOnCreation
+ != mergedConfiguration.getMergedConfiguration().orientation) {
+
+ // The orientation of the screen is changing. We better remove the snapshot ASAP as
+ // we are going to wait on the new window in any case to unfreeze the screen, and
+ // the starting window is not needed anymore.
+ sHandler.post(mOuter::remove);
+ }
if (reportDraw) {
sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index aabf2bed1dd4..fe5b7f23d5e0 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -228,7 +228,10 @@ public class WindowAnimator {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
if (transactionOpen) {
- mService.closeSurfaceTransaction();
+
+ // Do not hold window manager lock while closing the transaction, as this might be
+ // blocking until the next frame, which can lead to total lock starvation.
+ mService.closeSurfaceTransaction(false /* withLockHeld */);
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9e3edef768d7..ec7ab23f251b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -893,10 +893,26 @@ public class WindowManagerService extends IWindowManager.Stub
}
void closeSurfaceTransaction() {
+ closeSurfaceTransaction(true /* withLockHeld */);
+ }
+
+ /**
+ * Closes a surface transaction.
+ *
+ * @param withLockHeld Whether to acquire the window manager while doing so. In some cases
+ * holding the lock my lead to starvation in WM in case closeTransaction
+ * blocks and we call it repeatedly, like we do for animations.
+ */
+ void closeSurfaceTransaction(boolean withLockHeld) {
synchronized (mWindowMap) {
if (mRoot.mSurfaceTraceEnabled) {
mRoot.mRemoteEventTrace.closeSurfaceTransaction();
}
+ if (withLockHeld) {
+ SurfaceControl.closeTransaction();
+ }
+ }
+ if (!withLockHeld) {
SurfaceControl.closeTransaction();
}
}
@@ -1270,14 +1286,6 @@ public class WindowManagerService extends IWindowManager.Stub
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
- if (rootType == TYPE_APPLICATION_STARTING
- && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0
- && atoken.firstWindowDrawn) {
- // No need for this guy!
- if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v(
- TAG_WM, "**** NO NEED TO START: " + attrs.getTitle());
- return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
- }
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
@@ -5150,7 +5158,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
break;
case NOTIFY_APP_TRANSITION_STARTING: {
- mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj);
+ mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj,
+ msg.getWhen());
}
break;
case NOTIFY_APP_TRANSITION_CANCELLED: {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 73f8d27bc7a6..33cb9081325b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1524,7 +1524,10 @@ class WindowStateAnimator {
void prepareSurfaceLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
if (!hasSurface()) {
- if (w.mOrientationChanging) {
+
+ // There is no need to wait for an animation change if our window is gone for layout
+ // already as we'll never be visible.
+ if (w.mOrientationChanging && w.isGoneForLayoutLw()) {
if (DEBUG_ORIENTATION) {
Slog.v(TAG, "Orientation change skips hidden " + w);
}
@@ -1557,13 +1560,11 @@ class WindowStateAnimator {
hide("prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
- // If we are waiting for this window to handle an
- // orientation change, well, it is hidden, so
- // doesn't really matter. Note that this does
- // introduce a potential glitch if the window
- // becomes unhidden before it has drawn for the
- // new orientation.
- if (w.mOrientationChanging) {
+ // If we are waiting for this window to handle an orientation change. If this window is
+ // really hidden (gone for layout), there is no point in still waiting for it.
+ // Note that this does introduce a potential glitch if the window becomes unhidden
+ // before it has drawn for the new orientation.
+ if (w.mOrientationChanging && w.isGoneForLayoutLw()) {
w.mOrientationChanging = false;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation change skips hidden " + w);
@@ -1630,18 +1631,19 @@ class WindowStateAnimator {
displayed = true;
}
- if (displayed) {
- if (w.mOrientationChanging) {
- if (!w.isDrawnLw()) {
- mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
- mAnimator.mLastWindowFreezeSource = w;
- if (DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation continue waiting for draw in " + w);
- } else {
- w.mOrientationChanging = false;
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w);
- }
+ if (w.mOrientationChanging) {
+ if (!w.isDrawnLw()) {
+ mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+ mAnimator.mLastWindowFreezeSource = w;
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Orientation continue waiting for draw in " + w);
+ } else {
+ w.mOrientationChanging = false;
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w);
}
+ }
+
+ if (displayed) {
w.mToken.hasVisible = true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4442bb87ec33..82c862f6a6a8 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -25,6 +25,7 @@ import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_CLOSE;
import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN;
+import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -239,7 +240,7 @@ class WindowSurfacePlacer {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
int transit = mService.mAppTransition.getAppTransition();
- if (mService.mSkipAppTransitionAnimation) {
+ if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
transit = AppTransition.TRANSIT_UNSET;
}
mService.mSkipAppTransitionAnimation = false;
@@ -598,42 +599,47 @@ class WindowSurfacePlacer {
+ ", openingApps=" + openingApps
+ ", closingApps=" + closingApps);
mService.mAnimateWallpaperWithTarget = false;
- if (closingAppHasWallpaper && openingAppHasWallpaper) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
- switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_TASK_OPEN:
- case TRANSIT_TASK_TO_FRONT:
- transit = TRANSIT_WALLPAPER_INTRA_OPEN;
- break;
- case TRANSIT_ACTIVITY_CLOSE:
- case TRANSIT_TASK_CLOSE:
- case TRANSIT_TASK_TO_BACK:
- transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
- break;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit: " + AppTransition.appTransitionToString(transit));
- } else if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+ if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit: " + AppTransition.appTransitionToString(transit));
- } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
- && !openingApps.contains(oldWallpaper.mAppToken)
- && closingApps.contains(oldWallpaper.mAppToken)) {
- // We are transitioning from an activity with a wallpaper to one without.
- transit = TRANSIT_WALLPAPER_CLOSE;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
- + AppTransition.appTransitionToString(transit));
- } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
- openingApps.contains(wallpaperTarget.mAppToken)) {
- // We are transitioning from an activity without
- // a wallpaper to now showing the wallpaper
- transit = TRANSIT_WALLPAPER_OPEN;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
- + AppTransition.appTransitionToString(transit));
- } else {
- mService.mAnimateWallpaperWithTarget = true;
+ }
+ // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
+ // relies on the fact that we always execute a Keyguard transition after preparing one.
+ else if (!isKeyguardGoingAwayTransit(transit)) {
+ if (closingAppHasWallpaper && openingAppHasWallpaper) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
+ switch (transit) {
+ case TRANSIT_ACTIVITY_OPEN:
+ case TRANSIT_TASK_OPEN:
+ case TRANSIT_TASK_TO_FRONT:
+ transit = TRANSIT_WALLPAPER_INTRA_OPEN;
+ break;
+ case TRANSIT_ACTIVITY_CLOSE:
+ case TRANSIT_TASK_CLOSE:
+ case TRANSIT_TASK_TO_BACK:
+ transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
+ break;
+ }
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "New transit: " + AppTransition.appTransitionToString(transit));
+ } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
+ && !openingApps.contains(oldWallpaper.mAppToken)
+ && closingApps.contains(oldWallpaper.mAppToken)) {
+ // We are transitioning from an activity with a wallpaper to one without.
+ transit = TRANSIT_WALLPAPER_CLOSE;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
+ + AppTransition.appTransitionToString(transit));
+ } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
+ openingApps.contains(wallpaperTarget.mAppToken)) {
+ // We are transitioning from an activity without
+ // a wallpaper to now showing the wallpaper
+ transit = TRANSIT_WALLPAPER_OPEN;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
+ + AppTransition.appTransitionToString(transit));
+ } else {
+ mService.mAnimateWallpaperWithTarget = true;
+ }
}
return transit;
}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ec0887489fba..ae9827428179 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -20,21 +20,29 @@ import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import com.android.server.lights.Light;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
-import android.app.NotificationManager;
import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -42,30 +50,23 @@ import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.Vibrator;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.lights.Light;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BuzzBeepBlinkTest extends NotificationTestCase {
@@ -163,6 +164,11 @@ public class BuzzBeepBlinkTest extends NotificationTestCase {
true /* noisy */, false /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getInsistentBeepyLeanbackNotification() {
+ return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getBuzzyNotification() {
return getNotificationRecord(mId, false /* insistent */, false /* once */,
false /* noisy */, true /* buzzy*/, false /* lights */);
@@ -192,23 +198,30 @@ public class BuzzBeepBlinkTest extends NotificationTestCase {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, true /* buzzy*/, true /* lights */,
true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */,
- null, Notification.GROUP_ALERT_ALL);
+ null, Notification.GROUP_ALERT_ALL, false);
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights) {
return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
- null, Notification.GROUP_ALERT_ALL);
+ null, Notification.GROUP_ALERT_ALL, false);
+ }
+
+ private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
+ null, Notification.GROUP_ALERT_ALL, true);
}
private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, true, false, false, true, true, true,
- groupKey, groupAlertBehavior);
+ groupKey, groupAlertBehavior, false);
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
- boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior) {
+ boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+ boolean isLeanback) {
NotificationChannel channel =
new NotificationChannel("test", "test", IMPORTANCE_HIGH);
final Builder builder = new Builder(getContext())
@@ -257,9 +270,15 @@ public class BuzzBeepBlinkTest extends NotificationTestCase {
n.flags |= Notification.FLAG_INSISTENT;
}
+ Context context = spy(getContext());
+ PackageManager packageManager = spy(context.getPackageManager());
+ when(context.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ .thenReturn(isLeanback);
+
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ NotificationRecord r = new NotificationRecord(context, sbn, channel);
mService.addNotification(r);
return r;
}
@@ -367,6 +386,15 @@ public class BuzzBeepBlinkTest extends NotificationTestCase {
}
@Test
+ public void testNoLeanbackBeep() throws Exception {
+ NotificationRecord r = getInsistentBeepyLeanbackNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ }
+
+ @Test
public void testNoInterruptionForMin() throws Exception {
NotificationRecord r = getBeepyNotification();
r.setImportance(NotificationManager.IMPORTANCE_MIN, "foo");
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index bd6e379cb913..46c536c76d46 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -357,6 +357,43 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
}
@Test
+ public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ for (int i = 0; i < 10; i++) {
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ }
+ mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+ waitForIdle();
+ }
+
+ @Test
+ public void testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group1", true);
+ final NotificationRecord parentAsChild = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group1", false);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group1", false);
+
+ // fully post parent notification
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+ waitForIdle();
+
+ // enqueue the child several times
+ for (int i = 0; i < 10; i++) {
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+ }
+ // make the parent a child, which will cancel the child notification
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
+ parentAsChild.sbn.getUserId());
+ waitForIdle();
+ }
+
+ @Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 65a56327bb24..606088128fc4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -107,7 +107,7 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
createAppWindowController();
controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false);
+ false, false);
waitUntilHandlersIdle();
final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent);
assertHasStartingWindow(atoken);
@@ -125,7 +125,7 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
createAppWindowController();
controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false);
+ false, false);
controller.removeStartingWindow();
waitUntilHandlersIdle();
assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
@@ -140,11 +140,11 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
createAppWindowController();
controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false);
+ false, false);
waitUntilHandlersIdle();
controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
- true, true, false, true, false);
+ true, true, false, true, false, false);
waitUntilHandlersIdle();
assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
@@ -161,11 +161,11 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
// Surprise, ...! Transfer window in the middle of the creation flow.
controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
- true, true, false, true, false);
+ true, true, false, true, false, false);
});
controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false);
+ false, false);
waitUntilHandlersIdle();
assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index e2868d7056b0..4288eac0faa9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -62,7 +62,8 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
final TaskSnapshot snapshot = new TaskSnapshot(buffer,
ORIENTATION_PORTRAIT, contentInsets, false, 1.0f);
mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test",
- Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds);
+ Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
+ ORIENTATION_PORTRAIT);
}
private void setupSurface(int width, int height) {
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 272653f7fc66..4d2f97f2a5fc 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -664,7 +664,8 @@ public class AppLaunch extends InstrumentationTestCase {
if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) {
launchSuccess = true;
}
- if (launchSuccess && lineCount == 4) {
+ // Parse TotalTime which is the launch time
+ if (launchSuccess && lineCount == 5) {
String launchSplit[] = line.split(":");
launchTime = launchSplit[1].trim();
}