OSDN Git Service

Merge "User action should only stop lock task mode if it is in pinned mode" into...
authorTony Mak <tonymak@google.com>
Tue, 19 Apr 2016 10:32:29 +0000 (10:32 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Tue, 19 Apr 2016 10:32:33 +0000 (10:32 +0000)
160 files changed:
api/current.txt
api/system-current.txt
api/test-current.txt
cmds/am/src/com/android/commands/am/Am.java
core/java/android/app/ActivityManagerInternal.java
core/java/android/app/ActivityThread.java
core/java/android/app/Fragment.java
core/java/android/app/ResourcesManager.java
core/java/android/app/SystemServiceRegistry.java
core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
core/java/android/bluetooth/BluetoothAdapter.java
core/java/android/bluetooth/IBluetooth.aidl
core/java/android/content/Intent.java
core/java/android/content/pm/PackageManager.java
core/java/android/hardware/camera2/CameraManager.java
core/java/android/hardware/input/InputDeviceIdentifier.java
core/java/android/net/metrics/DhcpErrorEvent.java
core/java/android/net/metrics/IpConnectivityEvent.java
core/java/android/nfc/cardemulation/NfcFCardEmulation.java
core/java/android/os/BatteryStats.java
core/java/android/os/FileUtils.java
core/java/android/os/StrictMode.java
core/java/android/os/SynchronousResultReceiver.java [new file with mode: 0644]
core/java/android/os/storage/IMountService.java
core/java/android/os/storage/StorageManager.java
core/java/android/service/notification/ZenModeConfig.java
core/java/android/view/DisplayListCanvas.java
core/java/android/view/MotionEvent.java
core/java/android/view/WindowManagerPolicy.java
core/java/com/android/internal/app/IBatteryStats.aidl
core/java/com/android/internal/inputmethod/InputMethodUtils.java
core/java/com/android/internal/net/VpnProfile.java
core/java/com/android/internal/policy/BackdropFrameRenderer.java
core/java/com/android/internal/policy/DecorContext.java
core/java/com/android/internal/policy/PhoneWindow.java
core/jni/android_view_DisplayListCanvas.cpp
core/jni/android_view_RenderNode.cpp
core/tests/benchmarks/src/android/text/SpannableStringBuilderBenchmark.java
core/tests/benchmarks/src/android/text/SpannableStringInternalCopyBenchmark.java
core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java
libs/androidfw/AssetManager.cpp
libs/androidfw/ResourceTypes.cpp
libs/androidfw/tests/ResTable_test.cpp
libs/androidfw/tests/TestHelpers.h
libs/androidfw/tests/data/system/R.h
libs/androidfw/tests/data/system/res/values-sv/values.xml [new file with mode: 0644]
libs/androidfw/tests/data/system/system_arsc.h
libs/hwui/FrameBuilder.cpp
libs/hwui/FrameBuilder.h
libs/hwui/LayerBuilder.cpp
libs/hwui/TreeInfo.h
libs/hwui/renderthread/CanvasContext.cpp
libs/hwui/renderthread/CanvasContext.h
libs/hwui/tests/common/TestUtils.h
libs/hwui/tests/microbench/FrameBuilderBench.cpp
libs/hwui/tests/unit/FrameBuilderTests.cpp
libs/hwui/tests/unit/LeakCheckTests.cpp
media/java/android/media/browse/MediaBrowser.java
media/java/android/service/media/IMediaBrowserService.aidl
media/java/android/service/media/MediaBrowserService.java
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
packages/SettingsLib/res/values/strings.xml
packages/Shell/src/com/android/shell/BugreportProgressService.java
packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml [new file with mode: 0644]
packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
packages/SystemUI/res/layout/brightness_mirror.xml [new file with mode: 0644]
packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
packages/SystemUI/res/layout/keyboard_shortcuts_category_separator.xml
packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
packages/SystemUI/res/layout/recents_stack_action_button.xml
packages/SystemUI/res/layout/signal_cluster_view.xml
packages/SystemUI/res/layout/super_status_bar.xml
packages/SystemUI/res/layout/system_icons.xml
packages/SystemUI/res/values/dimens.xml
packages/SystemUI/res/values/dimens_tv.xml
packages/SystemUI/res/values/strings.xml
packages/SystemUI/src/com/android/systemui/SwipeHelper.java
packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
rs/java/android/renderscript/ScriptGroup.java
services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/DockObserver.java
services/core/java/com/android/server/InputMethodManagerService.java
services/core/java/com/android/server/MountService.java
services/core/java/com/android/server/TelephonyRegistry.java
services/core/java/com/android/server/TextServicesManagerService.java
services/core/java/com/android/server/WiredAccessoryManager.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityRecord.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/ActivityStartInterceptor.java
services/core/java/com/android/server/am/ActivityStarter.java
services/core/java/com/android/server/am/BatteryStatsService.java
services/core/java/com/android/server/am/TaskPersister.java
services/core/java/com/android/server/am/TaskRecord.java
services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
services/core/java/com/android/server/fingerprint/FingerprintService.java
services/core/java/com/android/server/job/JobStore.java
services/core/java/com/android/server/notification/RankingHelper.java
services/core/java/com/android/server/pm/Installer.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/UserManagerService.java
services/core/java/com/android/server/policy/PhoneWindowManager.java
services/core/java/com/android/server/vr/VrManagerService.java
services/core/java/com/android/server/wm/DimLayerController.java
services/core/java/com/android/server/wm/DockedStackDividerController.java
services/core/java/com/android/server/wm/TaskStack.java
services/core/java/com/android/server/wm/WindowLayersController.java
services/core/java/com/android/server/wm/WindowManagerService.java
services/core/java/com/android/server/wm/WindowState.java
services/net/java/android/net/apf/ApfFilter.java
services/net/java/android/net/apf/ApfGenerator.java
services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
telecomm/java/android/telecom/TelecomManager.java
telephony/java/android/telephony/CarrierConfigManager.java
telephony/java/android/telephony/NeighboringCellInfo.java
telephony/java/android/telephony/SubscriptionManager.java
telephony/java/android/telephony/TelephonyManager.java
telephony/java/com/android/internal/telephony/ITelephony.aidl
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
tests/CoreTests/android/core/TestWebServer.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
wifi/java/android/net/wifi/IWifiManager.aidl
wifi/java/android/net/wifi/RttManager.java
wifi/java/android/net/wifi/WifiActivityEnergyInfo.java

index 8e38036..5df8750 100644 (file)
@@ -34649,6 +34649,7 @@ package android.service.media {
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
@@ -50090,6 +50091,8 @@ package java.lang {
 
   public abstract interface CharSequence {
     method public abstract char charAt(int);
+    method public default java.util.stream.IntStream chars();
+    method public default java.util.stream.IntStream codePoints();
     method public abstract int length();
     method public abstract java.lang.CharSequence subSequence(int, int);
     method public abstract java.lang.String toString();
@@ -53194,6 +53197,7 @@ package java.nio {
     method public final int arrayOffset();
     method public abstract java.nio.CharBuffer asReadOnlyBuffer();
     method public final char charAt(int);
+    method public java.util.stream.IntStream chars();
     method public abstract java.nio.CharBuffer compact();
     method public int compareTo(java.nio.CharBuffer);
     method public abstract java.nio.CharBuffer duplicate();
index 0299fc0..5ea800c 100644 (file)
@@ -26097,8 +26097,7 @@ package android.net.metrics {
     field public static final int IPCE_DHCP_BASE = 1024; // 0x400
     field public static final int IPCE_DHCP_PARSE_ERROR = 1025; // 0x401
     field public static final int IPCE_DHCP_RECV_ERROR = 1024; // 0x400
-    field public static final int IPCE_DHCP_STATE_CHANGE = 1027; // 0x403
-    field public static final int IPCE_DHCP_TIMEOUT = 1026; // 0x402
+    field public static final int IPCE_DHCP_STATE_CHANGE = 1026; // 0x402
     field public static final int IPCE_DNS_BASE = 5120; // 0x1400
     field public static final int IPCE_DNS_LOOKUPS = 5120; // 0x1400
     field public static final int IPCE_IPMGR_BASE = 4096; // 0x1000
@@ -37304,6 +37303,7 @@ package android.service.media {
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
@@ -53392,6 +53392,8 @@ package java.lang {
 
   public abstract interface CharSequence {
     method public abstract char charAt(int);
+    method public default java.util.stream.IntStream chars();
+    method public default java.util.stream.IntStream codePoints();
     method public abstract int length();
     method public abstract java.lang.CharSequence subSequence(int, int);
     method public abstract java.lang.String toString();
@@ -56496,6 +56498,7 @@ package java.nio {
     method public final int arrayOffset();
     method public abstract java.nio.CharBuffer asReadOnlyBuffer();
     method public final char charAt(int);
+    method public java.util.stream.IntStream chars();
     method public abstract java.nio.CharBuffer compact();
     method public int compareTo(java.nio.CharBuffer);
     method public abstract java.nio.CharBuffer duplicate();
index 61d5721..e4b11bd 100644 (file)
@@ -34724,6 +34724,7 @@ package android.service.media {
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
@@ -50168,6 +50169,8 @@ package java.lang {
 
   public abstract interface CharSequence {
     method public abstract char charAt(int);
+    method public default java.util.stream.IntStream chars();
+    method public default java.util.stream.IntStream codePoints();
     method public abstract int length();
     method public abstract java.lang.CharSequence subSequence(int, int);
     method public abstract java.lang.String toString();
@@ -53272,6 +53275,7 @@ package java.nio {
     method public final int arrayOffset();
     method public abstract java.nio.CharBuffer asReadOnlyBuffer();
     method public final char charAt(int);
+    method public java.util.stream.IntStream chars();
     method public abstract java.nio.CharBuffer compact();
     method public int compareTo(java.nio.CharBuffer);
     method public abstract java.nio.CharBuffer duplicate();
index 456be02..7d084dc 100644 (file)
@@ -1790,7 +1790,7 @@ public class Am extends BaseCommand {
 
     private void runStackStart() throws Exception {
         String displayIdStr = nextArgRequired();
-        int displayId = Integer.valueOf(displayIdStr);
+        int displayId = Integer.parseInt(displayIdStr);
         Intent intent = makeIntent(UserHandle.USER_CURRENT);
 
         try {
@@ -1804,9 +1804,9 @@ public class Am extends BaseCommand {
 
     private void runStackMoveTask() throws Exception {
         String taskIdStr = nextArgRequired();
-        int taskId = Integer.valueOf(taskIdStr);
+        int taskId = Integer.parseInt(taskIdStr);
         String stackIdStr = nextArgRequired();
-        int stackId = Integer.valueOf(stackIdStr);
+        int stackId = Integer.parseInt(stackIdStr);
         String toTopStr = nextArgRequired();
         final boolean toTop;
         if ("true".equals(toTopStr)) {
@@ -1826,7 +1826,7 @@ public class Am extends BaseCommand {
 
     private void runStackResize() throws Exception {
         String stackIdStr = nextArgRequired();
-        int stackId = Integer.valueOf(stackIdStr);
+        int stackId = Integer.parseInt(stackIdStr);
         final Rect bounds = getBounds();
         if (bounds == null) {
             System.err.println("Error: invalid input bounds");
@@ -1837,7 +1837,7 @@ public class Am extends BaseCommand {
 
     private void runStackResizeAnimated() throws Exception {
         String stackIdStr = nextArgRequired();
-        int stackId = Integer.valueOf(stackIdStr);
+        int stackId = Integer.parseInt(stackIdStr);
         final Rect bounds;
         if ("null".equals(mArgs.peekNextArg())) {
             bounds = null;
@@ -1886,11 +1886,11 @@ public class Am extends BaseCommand {
 
     private void runStackPositionTask() throws Exception {
         String taskIdStr = nextArgRequired();
-        int taskId = Integer.valueOf(taskIdStr);
+        int taskId = Integer.parseInt(taskIdStr);
         String stackIdStr = nextArgRequired();
-        int stackId = Integer.valueOf(stackIdStr);
+        int stackId = Integer.parseInt(stackIdStr);
         String positionStr = nextArgRequired();
-        int position = Integer.valueOf(positionStr);
+        int position = Integer.parseInt(positionStr);
 
         try {
             mAm.positionTaskInStack(taskId, stackId, position);
@@ -1911,7 +1911,7 @@ public class Am extends BaseCommand {
     private void runStackInfo() throws Exception {
         try {
             String stackIdStr = nextArgRequired();
-            int stackId = Integer.valueOf(stackIdStr);
+            int stackId = Integer.parseInt(stackIdStr);
             StackInfo info = mAm.getStackInfo(stackId);
             System.out.println(info);
         } catch (RemoteException e) {
@@ -1920,12 +1920,12 @@ public class Am extends BaseCommand {
 
     private void runStackRemove() throws Exception {
         String stackIdStr = nextArgRequired();
-        int stackId = Integer.valueOf(stackIdStr);
+        int stackId = Integer.parseInt(stackIdStr);
         mAm.removeStack(stackId);
     }
 
     private void runMoveTopActivityToPinnedStack() throws Exception {
-        int stackId = Integer.valueOf(nextArgRequired());
+        int stackId = Integer.parseInt(nextArgRequired());
         final Rect bounds = getBounds();
         if (bounds == null) {
             System.err.println("Error: invalid input bounds");
@@ -1943,10 +1943,10 @@ public class Am extends BaseCommand {
     }
 
     private void runStackSizeDockedStackTest() throws Exception {
-        final int stepSize = Integer.valueOf(nextArgRequired());
+        final int stepSize = Integer.parseInt(nextArgRequired());
         final String side = nextArgRequired();
         final String delayStr = nextArg();
-        final int delayMs = (delayStr != null) ? Integer.valueOf(delayStr) : 0;
+        final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
 
         Rect bounds;
         try {
@@ -2060,7 +2060,7 @@ public class Am extends BaseCommand {
             if (taskIdStr.equals("stop")) {
                 mAm.stopLockTaskMode();
             } else {
-                int taskId = Integer.valueOf(taskIdStr);
+                int taskId = Integer.parseInt(taskIdStr);
                 mAm.startLockTaskMode(taskId);
             }
             System.err.println("Activity manager is " + (mAm.isInLockTaskMode() ? "" : "not ") +
@@ -2071,9 +2071,9 @@ public class Am extends BaseCommand {
 
     private void runTaskResizeable() throws Exception {
         final String taskIdStr = nextArgRequired();
-        final int taskId = Integer.valueOf(taskIdStr);
+        final int taskId = Integer.parseInt(taskIdStr);
         final String resizeableStr = nextArgRequired();
-        final int resizeableMode = Integer.valueOf(resizeableStr);
+        final int resizeableMode = Integer.parseInt(resizeableStr);
 
         try {
             mAm.setTaskResizeable(taskId, resizeableMode);
@@ -2083,7 +2083,7 @@ public class Am extends BaseCommand {
 
     private void runTaskResize() throws Exception {
         final String taskIdStr = nextArgRequired();
-        final int taskId = Integer.valueOf(taskIdStr);
+        final int taskId = Integer.parseInt(taskIdStr);
         final Rect bounds = getBounds();
         if (bounds == null) {
             System.err.println("Error: invalid input bounds");
@@ -2104,10 +2104,10 @@ public class Am extends BaseCommand {
     }
 
     private void runTaskDragTaskTest() {
-        final int taskId = Integer.valueOf(nextArgRequired());
-        final int stepSize = Integer.valueOf(nextArgRequired());
+        final int taskId = Integer.parseInt(nextArgRequired());
+        final int stepSize = Integer.parseInt(nextArgRequired());
         final String delayStr = nextArg();
-        final int delay_ms = (delayStr != null) ? Integer.valueOf(delayStr) : 0;
+        final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
         final StackInfo stackInfo;
         Rect taskBounds;
         try {
@@ -2203,10 +2203,10 @@ public class Am extends BaseCommand {
     }
 
     private void runTaskSizeTaskTest() {
-        final int taskId = Integer.valueOf(nextArgRequired());
-        final int stepSize = Integer.valueOf(nextArgRequired());
+        final int taskId = Integer.parseInt(nextArgRequired());
+        final int stepSize = Integer.parseInt(nextArgRequired());
         final String delayStr = nextArg();
-        final int delay_ms = (delayStr != null) ? Integer.valueOf(delayStr) : 0;
+        final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
         final StackInfo stackInfo;
         final Rect initialTaskBounds;
         try {
@@ -2550,13 +2550,13 @@ public class Am extends BaseCommand {
 
     private Rect getBounds() {
         String leftStr = nextArgRequired();
-        int left = Integer.valueOf(leftStr);
+        int left = Integer.parseInt(leftStr);
         String topStr = nextArgRequired();
-        int top = Integer.valueOf(topStr);
+        int top = Integer.parseInt(topStr);
         String rightStr = nextArgRequired();
-        int right = Integer.valueOf(rightStr);
+        int right = Integer.parseInt(rightStr);
         String bottomStr = nextArgRequired();
-        int bottom = Integer.valueOf(bottomStr);
+        int bottom = Integer.parseInt(bottomStr);
         if (left < 0) {
             System.err.println("Error: bad left arg: " + leftStr);
             return null;
index 374c4f6..ee4c2f7 100644 (file)
@@ -133,4 +133,10 @@ public abstract class ActivityManagerInternal {
      * the focused activity.
      */
     public abstract List<IBinder> getTopVisibleActivities();
+
+    /**
+     * Callback for window manager to let activity manager know that docked stack changes its
+     * minimized state.
+     */
+    public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
 }
index 36e962e..97bc570 100644 (file)
@@ -5045,6 +5045,8 @@ public final class ActivityThread {
     }
 
     private void handleBindApplication(AppBindData data) {
+        // Register the UI Thread as a sensitive thread to the runtime.
+        VMRuntime.registerSensitiveThread();
         if (data.trackAllocation) {
             DdmVmInternal.enableRecentAllocations(true);
         }
index 29f594f..a221c98 100644 (file)
@@ -28,6 +28,7 @@ import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1030,14 +1031,46 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
      * This may be used by the system to prioritize operations such as fragment lifecycle updates
      * or loader ordering behavior.</p>
      *
+     * <p><strong>Note:</strong> Prior to Android N there was a platform bug that could cause
+     * <code>setUserVisibleHint</code> to bring a fragment up to the started state before its
+     * <code>FragmentTransaction</code> had been committed. As some apps relied on this behavior,
+     * it is preserved for apps that declare a <code>targetSdkVersion</code> of 23 or lower.</p>
+     *
      * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
      *                        false if it is not.
      */
     public void setUserVisibleHint(boolean isVisibleToUser) {
-        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
-                && mFragmentManager != null && isAdded()) {
+        // Prior to Android N we were simply checking if this fragment had a FragmentManager
+        // set before we would trigger a deferred start. Unfortunately this also gets set before
+        // a fragment transaction is committed, so if setUserVisibleHint was called before a
+        // transaction commit, we would start the fragment way too early. FragmentPagerAdapter
+        // triggers this situation.
+        // Unfortunately some apps relied on this timing in overrides of setUserVisibleHint
+        // on their own fragments, and expected, however erroneously, that after a call to
+        // super.setUserVisibleHint their onStart methods had been run.
+        // We preserve this behavior for apps targeting old platform versions below.
+        boolean useBrokenAddedCheck = false;
+        Context context = getContext();
+        if (mFragmentManager != null && mFragmentManager.mHost != null) {
+            context = mFragmentManager.mHost.getContext();
+        }
+        if (context != null) {
+            useBrokenAddedCheck = context.getApplicationInfo().targetSdkVersion <= VERSION_CODES.M;
+        }
+
+        final boolean performDeferredStart;
+        if (useBrokenAddedCheck) {
+            performDeferredStart = !mUserVisibleHint && isVisibleToUser && mState < STARTED
+                    && mFragmentManager != null;
+        } else {
+            performDeferredStart = !mUserVisibleHint && isVisibleToUser && mState < STARTED
+                    && mFragmentManager != null && isAdded();
+        }
+
+        if (performDeferredStart) {
             mFragmentManager.performPendingDeferredStart(this);
         }
+
         mUserVisibleHint = isVisibleToUser;
         mDeferStart = mState < STARTED && !isVisibleToUser;
     }
index 4c4f128..aef92cf 100644 (file)
@@ -29,6 +29,7 @@ import android.content.res.ResourcesImpl;
 import android.content.res.ResourcesKey;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.IBinder;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.LocaleList;
@@ -430,37 +431,44 @@ public class ResourcesManager {
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        final ResourcesKey key = new ResourcesKey(
-                resDir,
-                splitResDirs,
-                overlayDirs,
-                libDirs,
-                displayId,
-                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
-                compatInfo);
-        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#createBaseActivityResources");
+            final ResourcesKey key = new ResourcesKey(
+                    resDir,
+                    splitResDirs,
+                    overlayDirs,
+                    libDirs,
+                    displayId,
+                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    compatInfo);
+            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
 
-        if (DEBUG) {
-            Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
-                    + " with key=" + key);
-        }
+            if (DEBUG) {
+                Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+                        + " with key=" + key);
+            }
 
-        synchronized (this) {
-            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                    activityToken);
+            synchronized (this) {
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(
+                                activityToken);
 
-            if (overrideConfig != null) {
-                activityResources.overrideConfig.setTo(overrideConfig);
-            } else {
-                activityResources.overrideConfig.setToDefaults();
+                if (overrideConfig != null) {
+                    activityResources.overrideConfig.setTo(overrideConfig);
+                } else {
+                    activityResources.overrideConfig.setToDefaults();
+                }
             }
-        }
 
-        // Update any existing Activity Resources references.
-        updateResourcesForActivity(activityToken, overrideConfig);
+            // Update any existing Activity Resources references.
+            updateResourcesForActivity(activityToken, overrideConfig);
 
-        // Now request an actual Resources object.
-        return getOrCreateResources(activityToken, key, classLoader);
+            // Now request an actual Resources object.
+            return getOrCreateResources(activityToken, key, classLoader);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 
     /**
@@ -490,8 +498,8 @@ public class ResourcesManager {
             }
 
             if (activityToken != null) {
-                final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                        activityToken);
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(activityToken);
 
                 // Clean up any dead references so they don't pile up.
                 ArrayUtils.unstableRemoveIf(activityResources.activityResources,
@@ -539,6 +547,7 @@ public class ResourcesManager {
         final String[] systemLocales = findSystemLocales
                 ? AssetManager.getSystem().getLocales() : null;
         final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
+
         // Avoid checking for non-pseudo-locales if we already know there were some from a previous
         // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
         // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
@@ -613,16 +622,21 @@ public class ResourcesManager {
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        final ResourcesKey key = new ResourcesKey(
-                resDir,
-                splitResDirs,
-                overlayDirs,
-                libDirs,
-                displayId,
-                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
-                compatInfo);
-        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
-        return getOrCreateResources(activityToken, key, classLoader);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
+            final ResourcesKey key = new ResourcesKey(
+                    resDir,
+                    splitResDirs,
+                    overlayDirs,
+                    libDirs,
+                    displayId,
+                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    compatInfo);
+            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+            return getOrCreateResources(activityToken, key, classLoader);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 
     /**
@@ -636,93 +650,104 @@ public class ResourcesManager {
      */
     public void updateResourcesForActivity(@NonNull IBinder activityToken,
             @Nullable Configuration overrideConfig) {
-        synchronized (this) {
-            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                    activityToken);
-
-            if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
-                // They are the same, no work to do.
-                return;
-            }
-
-            // Grab a copy of the old configuration so we can create the delta's of each
-            // Resources object associated with this Activity.
-            final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
-
-            // Update the Activity's base override.
-            if (overrideConfig != null) {
-                activityResources.overrideConfig.setTo(overrideConfig);
-            } else {
-                activityResources.overrideConfig.setToDefaults();
-            }
-
-            if (DEBUG) {
-                Throwable here = new Throwable();
-                here.fillInStackTrace();
-                Slog.d(TAG, "updating resources override for activity=" + activityToken
-                        + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
-                        + " to newConfig="
-                        + Configuration.resourceQualifierString(activityResources.overrideConfig),
-                        here);
-            }
-
-            final boolean activityHasOverrideConfig =
-                    !activityResources.overrideConfig.equals(Configuration.EMPTY);
-
-            // Rebase each Resources associated with this Activity.
-            final int refCount = activityResources.activityResources.size();
-            for (int i = 0; i < refCount; i++) {
-                WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
-                Resources resources = weakResRef.get();
-                if (resources == null) {
-                    continue;
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#updateResourcesForActivity");
+            synchronized (this) {
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(activityToken);
+
+                if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
+                    // They are the same, no work to do.
+                    return;
                 }
 
-                // Extract the ResourcesKey that was last used to create the Resources for this
-                // activity.
-                final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
-                if (oldKey == null) {
-                    Slog.e(TAG, "can't find ResourcesKey for resources impl="
-                            + resources.getImpl());
-                    continue;
-                }
+                // Grab a copy of the old configuration so we can create the delta's of each
+                // Resources object associated with this Activity.
+                final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
 
-                // Build the new override configuration for this ResourcesKey.
-                final Configuration rebasedOverrideConfig = new Configuration();
+                // Update the Activity's base override.
                 if (overrideConfig != null) {
-                    rebasedOverrideConfig.setTo(overrideConfig);
+                    activityResources.overrideConfig.setTo(overrideConfig);
+                } else {
+                    activityResources.overrideConfig.setToDefaults();
                 }
 
-                if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
-                    // Generate a delta between the old base Activity override configuration and
-                    // the actual final override configuration that was used to figure out the real
-                    // delta this Resources object wanted.
-                    Configuration overrideOverrideConfig = Configuration.generateDelta(
-                            oldConfig, oldKey.mOverrideConfiguration);
-                    rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+                if (DEBUG) {
+                    Throwable here = new Throwable();
+                    here.fillInStackTrace();
+                    Slog.d(TAG, "updating resources override for activity=" + activityToken
+                            + " from oldConfig="
+                            + Configuration.resourceQualifierString(oldConfig)
+                            + " to newConfig="
+                            + Configuration.resourceQualifierString(
+                            activityResources.overrideConfig),
+                            here);
                 }
 
-                // Create the new ResourcesKey with the rebased override config.
-                final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
-                        oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
-                        rebasedOverrideConfig, oldKey.mCompatInfo);
+                final boolean activityHasOverrideConfig =
+                        !activityResources.overrideConfig.equals(Configuration.EMPTY);
+
+                // Rebase each Resources associated with this Activity.
+                final int refCount = activityResources.activityResources.size();
+                for (int i = 0; i < refCount; i++) {
+                    WeakReference<Resources> weakResRef = activityResources.activityResources.get(
+                            i);
+                    Resources resources = weakResRef.get();
+                    if (resources == null) {
+                        continue;
+                    }
 
-                if (DEBUG) {
-                    Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
-                            + " to newKey=" + newKey);
-                }
+                    // Extract the ResourcesKey that was last used to create the Resources for this
+                    // activity.
+                    final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+                    if (oldKey == null) {
+                        Slog.e(TAG, "can't find ResourcesKey for resources impl="
+                                + resources.getImpl());
+                        continue;
+                    }
 
-                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
-                if (resourcesImpl == null) {
-                    resourcesImpl = createResourcesImpl(newKey);
-                    mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
-                }
+                    // Build the new override configuration for this ResourcesKey.
+                    final Configuration rebasedOverrideConfig = new Configuration();
+                    if (overrideConfig != null) {
+                        rebasedOverrideConfig.setTo(overrideConfig);
+                    }
+
+                    if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
+                        // Generate a delta between the old base Activity override configuration and
+                        // the actual final override configuration that was used to figure out the
+                        // real delta this Resources object wanted.
+                        Configuration overrideOverrideConfig = Configuration.generateDelta(
+                                oldConfig, oldKey.mOverrideConfiguration);
+                        rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+                    }
+
+                    // Create the new ResourcesKey with the rebased override config.
+                    final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
+                            oldKey.mSplitResDirs,
+                            oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
+                            rebasedOverrideConfig, oldKey.mCompatInfo);
+
+                    if (DEBUG) {
+                        Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+                                + " to newKey=" + newKey);
+                    }
 
-                if (resourcesImpl != resources.getImpl()) {
-                    // Set the ResourcesImpl, updating it for all users of this Resources object.
-                    resources.setImpl(resourcesImpl);
+                    ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
+                    if (resourcesImpl == null) {
+                        resourcesImpl = createResourcesImpl(newKey);
+                        mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+                    }
+
+                    if (resourcesImpl != resources.getImpl()) {
+                        // Set the ResourcesImpl, updating it for all users of this Resources
+                        // object.
+                        resources.setImpl(resourcesImpl);
+                    }
                 }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
     }
 
@@ -745,86 +770,95 @@ public class ResourcesManager {
 
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                              @Nullable CompatibilityInfo compat) {
-        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
-            if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
-                    + mResConfiguration.seq + ", newSeq=" + config.seq);
-            return false;
-        }
-        int changes = mResConfiguration.updateFrom(config);
-        // Things might have changed in display manager, so clear the cached displays.
-        mDisplays.clear();
-        DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
-
-        if (compat != null && (mResCompatibilityInfo == null ||
-                !mResCompatibilityInfo.equals(compat))) {
-            mResCompatibilityInfo = compat;
-            changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
-                    | ActivityInfo.CONFIG_SCREEN_SIZE
-                    | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-        }
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#applyConfigurationToResourcesLocked");
+
+            if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+                        + mResConfiguration.seq + ", newSeq=" + config.seq);
+                return false;
+            }
+            int changes = mResConfiguration.updateFrom(config);
+            // Things might have changed in display manager, so clear the cached displays.
+            mDisplays.clear();
+            DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
+
+            if (compat != null && (mResCompatibilityInfo == null ||
+                    !mResCompatibilityInfo.equals(compat))) {
+                mResCompatibilityInfo = compat;
+                changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+                        | ActivityInfo.CONFIG_SCREEN_SIZE
+                        | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+            }
 
-        Configuration localeAdjustedConfig = config;
-        final LocaleList configLocales = config.getLocales();
-        if (!configLocales.isEmpty()) {
-            setDefaultLocalesLocked(configLocales);
-            final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
-            if (adjustedLocales != configLocales) { // has the same result as .equals() in this case
-                // The first locale in the list was not chosen. So we create a modified
-                // configuration with the adjusted locales (which moves the chosen locale to the
-                // front).
-                localeAdjustedConfig = new Configuration();
-                localeAdjustedConfig.setTo(config);
-                localeAdjustedConfig.setLocales(adjustedLocales);
-                // Also adjust the locale list in mResConfiguration, so that the Resources created
-                // later would have the same locale list.
-                if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
-                    mResConfiguration.setLocales(adjustedLocales);
-                    changes |= ActivityInfo.CONFIG_LOCALE;
+            Configuration localeAdjustedConfig = config;
+            final LocaleList configLocales = config.getLocales();
+            if (!configLocales.isEmpty()) {
+                setDefaultLocalesLocked(configLocales);
+                final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
+                if (adjustedLocales
+                        != configLocales) { // has the same result as .equals() in this case
+                    // The first locale in the list was not chosen. So we create a modified
+                    // configuration with the adjusted locales (which moves the chosen locale to the
+                    // front).
+                    localeAdjustedConfig = new Configuration();
+                    localeAdjustedConfig.setTo(config);
+                    localeAdjustedConfig.setLocales(adjustedLocales);
+                    // Also adjust the locale list in mResConfiguration, so that the Resources
+                    // created later would have the same locale list.
+                    if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
+                        mResConfiguration.setLocales(adjustedLocales);
+                        changes |= ActivityInfo.CONFIG_LOCALE;
+                    }
                 }
             }
-        }
 
-        Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics, compat);
-
-        ApplicationPackageManager.configurationChanged();
-        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
-
-        Configuration tmpConfig = null;
-
-        for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
-            ResourcesKey key = mResourceImpls.keyAt(i);
-            ResourcesImpl r = mResourceImpls.valueAt(i).get();
-            if (r != null) {
-                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
-                        + r + " config to: " + localeAdjustedConfig);
-                int displayId = key.mDisplayId;
-                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-                DisplayMetrics dm = defaultDisplayMetrics;
-                final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
-                if (!isDefaultDisplay || hasOverrideConfiguration) {
-                    if (tmpConfig == null) {
-                        tmpConfig = new Configuration();
-                    }
-                    tmpConfig.setTo(localeAdjustedConfig);
-                    if (!isDefaultDisplay) {
-                        dm = getDisplayMetrics(displayId);
-                        applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
-                    }
-                    if (hasOverrideConfiguration) {
-                        tmpConfig.updateFrom(key.mOverrideConfiguration);
+            Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
+                    compat);
+
+            ApplicationPackageManager.configurationChanged();
+            //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+
+            Configuration tmpConfig = null;
+
+            for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+                ResourcesKey key = mResourceImpls.keyAt(i);
+                ResourcesImpl r = mResourceImpls.valueAt(i).get();
+                if (r != null) {
+                    if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+                            + r + " config to: " + localeAdjustedConfig);
+                    int displayId = key.mDisplayId;
+                    boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+                    DisplayMetrics dm = defaultDisplayMetrics;
+                    final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+                    if (!isDefaultDisplay || hasOverrideConfiguration) {
+                        if (tmpConfig == null) {
+                            tmpConfig = new Configuration();
+                        }
+                        tmpConfig.setTo(localeAdjustedConfig);
+                        if (!isDefaultDisplay) {
+                            dm = getDisplayMetrics(displayId);
+                            applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+                        }
+                        if (hasOverrideConfiguration) {
+                            tmpConfig.updateFrom(key.mOverrideConfiguration);
+                        }
+                        r.updateConfiguration(tmpConfig, dm, compat);
+                    } else {
+                        r.updateConfiguration(localeAdjustedConfig, dm, compat);
                     }
-                    r.updateConfiguration(tmpConfig, dm, compat);
+                    //Slog.i(TAG, "Updated app resources " + v.getKey()
+                    //        + " " + r + ": " + r.getConfiguration());
                 } else {
-                    r.updateConfiguration(localeAdjustedConfig, dm, compat);
+                    //Slog.i(TAG, "Removing old resources " + v.getKey());
+                    mResourceImpls.removeAt(i);
                 }
-                //Slog.i(TAG, "Updated app resources " + v.getKey()
-                //        + " " + r + ": " + r.getConfiguration());
-            } else {
-                //Slog.i(TAG, "Removing old resources " + v.getKey());
-                mResourceImpls.removeAt(i);
             }
-        }
 
-        return changes != 0;
+            return changes != 0;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 }
index cd6e572..7cd13ea 100644 (file)
@@ -542,7 +542,8 @@ final class SystemServiceRegistry {
             public RttManager createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(Context.WIFI_RTT_SERVICE);
                 IRttManager service = IRttManager.Stub.asInterface(b);
-                return new RttManager(ctx.getOuterContext(), service);
+                return new RttManager(ctx.getOuterContext(), service,
+                        ConnectivityThread.getInstanceLooper());
             }});
 
         registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
index e32a470..84f6060 100644 (file)
@@ -156,8 +156,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable {
      * @return if the record is valid
      */
     public boolean isValid() {
-        return ((mControllerTxTimeMs !=0) ||
-                (mControllerRxTimeMs !=0) ||
-                (mControllerIdleTimeMs !=0));
+        return ((mControllerTxTimeMs >=0) &&
+                (mControllerRxTimeMs >=0) &&
+                (mControllerIdleTimeMs >=0));
     }
 }
index 2a7eff8..e748477 100644 (file)
@@ -31,11 +31,14 @@ import android.bluetooth.le.ScanRecord;
 import android.bluetooth.le.ScanResult;
 import android.bluetooth.le.ScanSettings;
 import android.content.Context;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.SynchronousResultReceiver;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Pair;
@@ -53,6 +56,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
@@ -1369,33 +1373,62 @@ public final class BluetoothAdapter {
      *
      * @return a record with {@link BluetoothActivityEnergyInfo} or null if
      * report is unavailable or unsupported
+     * @deprecated use the asynchronous
+     * {@link #requestControllerActivityEnergyInfo(int, ResultReceiver)} instead.
      * @hide
      */
+    @Deprecated
     public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
-        if (getState() != STATE_ON) return null;
+        SynchronousResultReceiver receiver = new SynchronousResultReceiver();
+        requestControllerActivityEnergyInfo(updateType, receiver);
+        try {
+            SynchronousResultReceiver.Result result = receiver.awaitResult(1000);
+            if (result.bundle != null) {
+                return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
+            }
+        } catch (TimeoutException e) {
+            Log.e(TAG, "getControllerActivityEnergyInfo timed out");
+        }
+        return null;
+    }
+
+    /**
+     * Request the record of {@link BluetoothActivityEnergyInfo} object that
+     * has the activity and energy info. This can be used to ascertain what
+     * the controller has been up to, since the last sample.
+     *
+     * A null value for the activity info object may be sent if the bluetooth service is
+     * unreachable or the device does not support reporting such information.
+     *
+     * @param updateType Type of info, cached vs refreshed.
+     * @param result The callback to which to send the activity info.
+     * @hide
+     */
+    public void requestControllerActivityEnergyInfo(int updateType, ResultReceiver result) {
+        if (getState() != STATE_ON) {
+            result.send(0, null);
+            return;
+        }
+
         try {
-            BluetoothActivityEnergyInfo record;
             if (!mService.isActivityAndEnergyReportingSupported()) {
-                return null;
+                result.send(0, null);
+                return;
             }
             synchronized(this) {
                 if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) {
                     mService.getActivityEnergyInfoFromController();
                     wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS);
                 }
-                record = mService.reportActivityInfo();
-                if (record.isValid()) {
-                    return record;
-                } else {
-                    return null;
-                }
+                mService.requestActivityInfo(result);
             }
         } catch (InterruptedException e) {
             Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e);
+            result.send(0, null);
         } catch (RemoteException e) {
             Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
+            result.send(0, null);
         }
-        return null;
     }
 
     /**
index 9cd7d05..45d8641 100644 (file)
@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice;
 import android.bluetooth.OobData;
 import android.os.ParcelUuid;
 import android.os.ParcelFileDescriptor;
+import android.os.ResultReceiver;
 
 /**
  * System private API for talking with the Bluetooth service.
@@ -104,6 +105,15 @@ interface IBluetooth
     void getActivityEnergyInfoFromController();
     BluetoothActivityEnergyInfo reportActivityInfo();
 
+    /**
+     * Requests the controller activity info asynchronously.
+     * The implementor is expected to reply with the
+     * {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the
+     * key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}.
+     * The result code is ignored.
+     */
+    oneway void requestActivityInfo(in ResultReceiver result);
+
     void onLeServiceUp();
     void onBrEdrDown();
 }
index 831de4a..fb4dd84 100644 (file)
@@ -8905,7 +8905,7 @@ public class Intent implements Parcelable, Cloneable {
             } else if (ATTR_COMPONENT.equals(attrName)) {
                 intent.setComponent(ComponentName.unflattenFromString(attrValue));
             } else if (ATTR_FLAGS.equals(attrName)) {
-                intent.setFlags(Integer.valueOf(attrValue, 16));
+                intent.setFlags(Integer.parseInt(attrValue, 16));
             } else {
                 Log.e("Intent", "restoreFromXml: unknown attribute=" + attrName);
             }
index ade2248..787974d 100644 (file)
@@ -2181,7 +2181,7 @@ public abstract class PackageManager {
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
-     * The device implements {@link #FEATURE_VR_MODE} but additionally meets all CTS requirements
+     * The device implements {@link #FEATURE_VR_MODE} but additionally meets all CDD requirements
      * to be certified as a "VR Ready" device, which guarantees that the device is capable of
      * delivering consistent performance at a high framerate over an extended period of time for
      * typical VR application CPU/GPU workloads with a minimal number of frame drops, implements
index ffd9d89..3917bfa 100644 (file)
@@ -223,7 +223,7 @@ public final class CameraManager {
                         " currently connected camera device", cameraId));
             }
 
-            int id = Integer.valueOf(cameraId);
+            int id = Integer.parseInt(cameraId);
 
             /*
              * Get the camera characteristics from the camera service directly if it supports it,
index 5e832e3..801da88 100644 (file)
 
 package android.hardware.input;
 
+import java.util.Objects;
+
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 /**
  * Wrapper for passing identifying information for input devices.
@@ -65,6 +68,21 @@ public final class InputDeviceIdentifier implements Parcelable {
         return mProductId;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof InputDeviceIdentifier)) return false;
+
+        final InputDeviceIdentifier that = (InputDeviceIdentifier) o;
+        return ((mVendorId == that.mVendorId) && (mProductId == that.mProductId)
+                && TextUtils.equals(mDescriptor, that.mDescriptor));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDescriptor, mVendorId, mProductId);
+    }
+
     public static final Parcelable.Creator<InputDeviceIdentifier> CREATOR =
             new Parcelable.Creator<InputDeviceIdentifier>() {
 
index 910d94e..46fdd18 100644 (file)
@@ -93,8 +93,7 @@ public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcela
     }
 
     public static void logReceiveError(String ifName) {
-        IpConnectivityEvent.logEvent(IPCE_DHCP_RECV_ERROR,
-                new DhcpErrorEvent(ifName, RECEIVE_ERROR));
+        logEvent(IPCE_DHCP_RECV_ERROR, new DhcpErrorEvent(ifName, RECEIVE_ERROR));
     }
 
     public static int errorCodeWithOption(int errorCode, int option) {
index 96aa1ce..2eb8edb 100644 (file)
@@ -45,8 +45,7 @@ public abstract class IpConnectivityEvent {
 
     public static final int IPCE_DHCP_RECV_ERROR           = IPCE_DHCP_BASE + 0;
     public static final int IPCE_DHCP_PARSE_ERROR          = IPCE_DHCP_BASE + 1;
-    public static final int IPCE_DHCP_TIMEOUT              = IPCE_DHCP_BASE + 2;
-    public static final int IPCE_DHCP_STATE_CHANGE         = IPCE_DHCP_BASE + 3;
+    public static final int IPCE_DHCP_STATE_CHANGE         = IPCE_DHCP_BASE + 2;
 
     public static final int IPCE_NETMON_STATE_CHANGE       = IPCE_NETMON_BASE + 0;
     public static final int IPCE_NETMON_CHECK_RESULT       = IPCE_NETMON_BASE + 1;
index 42ccf20..a506504 100644 (file)
@@ -428,7 +428,7 @@ public final class NfcFCardEmulation {
             return false;
         }
         try {
-            Integer.valueOf(systemCode, 16);
+            Integer.parseInt(systemCode, 16);
         } catch (NumberFormatException e) {
             Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
             return false;
index 773e7dd..7f94d0e 100644 (file)
@@ -242,6 +242,8 @@ public abstract class BatteryStats implements Parcelable {
     private static final String VIDEO_DATA = "vid";
     private static final String AUDIO_DATA = "aud";
 
+    public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+
     private final StringBuilder mFormatBuilder = new StringBuilder(32);
     private final Formatter mFormatter = new Formatter(mFormatBuilder);
 
index dd73e53..7d74a12 100644 (file)
@@ -437,6 +437,14 @@ public class FileUtils {
         return filePath.startsWith(dirPath);
     }
 
+    public static boolean deleteContentsAndDir(File dir) {
+        if (deleteContents(dir)) {
+            return dir.delete();
+        } else {
+            return false;
+        }
+    }
+
     public static boolean deleteContents(File dir) {
         File[] files = dir.listFiles();
         boolean success = true;
index e4a76db..c36b488 100644 (file)
@@ -1163,7 +1163,7 @@ public final class StrictMode {
         }
         String policyString = message.substring(7, spaceIndex);
         try {
-            return Integer.valueOf(policyString).intValue();
+            return Integer.parseInt(policyString);
         } catch (NumberFormatException e) {
             return 0;
         }
@@ -1187,7 +1187,7 @@ public final class StrictMode {
         }
         String violationString = message.substring(numberStartIndex, numberEndIndex);
         try {
-            return Integer.valueOf(violationString).intValue();
+            return Integer.parseInt(violationString);
         } catch (NumberFormatException e) {
             return 0;
         }
diff --git a/core/java/android/os/SynchronousResultReceiver.java b/core/java/android/os/SynchronousResultReceiver.java
new file mode 100644 (file)
index 0000000..d1b6288
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Extends ResultReceiver to allow the server end of the ResultReceiver to synchronously wait
+ * on the response from the client. This enables an RPC like system but with the ability to
+ * timeout and discard late results.
+ *
+ * NOTE: Can only be used for one response. Subsequent responses on the same instance are ignored.
+ * {@hide}
+ */
+public class SynchronousResultReceiver extends ResultReceiver {
+    public static class Result {
+        public int resultCode;
+        @Nullable public Bundle bundle;
+
+        public Result(int resultCode, @Nullable Bundle bundle) {
+            this.resultCode = resultCode;
+            this.bundle = bundle;
+        }
+    }
+
+    private final CompletableFuture<Result> mFuture = new CompletableFuture<>();
+
+    public SynchronousResultReceiver() {
+        super((Handler) null);
+    }
+
+    @Override
+    final protected void onReceiveResult(int resultCode, Bundle resultData) {
+        super.onReceiveResult(resultCode, resultData);
+        mFuture.complete(new Result(resultCode, resultData));
+    }
+
+    /**
+     * Blocks waiting for the result from the remote client.
+     *
+     * @return the Result
+     * @throws TimeoutException if the timeout in milliseconds expired.
+     */
+    public @NonNull Result awaitResult(long timeoutMillis) throws TimeoutException {
+        final long deadline = System.currentTimeMillis() + timeoutMillis;
+        while (timeoutMillis >= 0) {
+            try {
+                return mFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (ExecutionException e) {
+                // This will NEVER happen.
+                throw new AssertionError("Error receiving response", e);
+            } catch (InterruptedException e) {
+                // The thread was interrupted, try and get the value again, this time
+                // with the remaining time until the deadline.
+                timeoutMillis -= deadline - System.currentTimeMillis();
+            }
+        }
+        throw new TimeoutException();
+    }
+
+}
index c21c65a..3915b02 100644 (file)
@@ -1325,6 +1325,24 @@ public interface IMountService extends IInterface {
             }
 
             @Override
+            public void destroyUserStorage(String volumeUuid, int userId, int flags)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(volumeUuid);
+                    _data.writeInt(userId);
+                    _data.writeInt(flags);
+                    mRemote.transact(Stub.TRANSACTION_destroyUserStorage, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
             public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1465,6 +1483,7 @@ public interface IMountService extends IInterface {
         static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
 
         static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
+        static final int TRANSACTION_destroyUserStorage = IBinder.FIRST_CALL_TRANSACTION + 67;
 
         static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
 
@@ -2096,6 +2115,15 @@ public interface IMountService extends IInterface {
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_destroyUserStorage: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String volumeUuid = data.readString();
+                    int userId = data.readInt();
+                    int _flags = data.readInt();
+                    destroyUserStorage(volumeUuid, userId, _flags);
+                    reply.writeNoException();
+                    return true;
+                }
                 case TRANSACTION_mountAppFuse: {
                     data.enforceInterface(DESCRIPTOR);
                     String name = data.readString();
@@ -2434,6 +2462,7 @@ public interface IMountService extends IInterface {
 
     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
             int flags) throws RemoteException;
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) throws RemoteException;
 
     public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
 }
index 0a8fdd9..f68e227 100644 (file)
@@ -1053,6 +1053,15 @@ public class StorageManager {
     }
 
     /** {@hide} */
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        try {
+            mMountService.destroyUserStorage(volumeUuid, userId, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
     public boolean isUserKeyUnlocked(int userId) {
         try {
             return mMountService.isUserKeyUnlocked(userId);
index 27315ee..3e25edb 100644 (file)
@@ -378,7 +378,7 @@ public class ZenModeConfig implements Parcelable {
     private static int tryParseInt(String value, int defValue) {
         if (TextUtils.isEmpty(value)) return defValue;
         try {
-            return Integer.valueOf(value);
+            return Integer.parseInt(value);
         } catch (NumberFormatException e) {
             return defValue;
         }
index 2481e04..415c291 100644 (file)
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
@@ -184,7 +185,7 @@ public class DisplayListCanvas extends Canvas {
      * any references to the functor, just that the reference from this specific
      * canvas's display list has been released.
      */
-    public void drawGLFunctor2(long drawGLFunctor, Runnable releasedCallback) {
+    public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
     }
 
index a0f5142..7f92777 100644 (file)
@@ -417,6 +417,21 @@ public final class MotionEvent extends InputEvent implements Parcelable {
     public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
 
     /**
+     * This flag indicates that the window that received this motion event is partly
+     * or wholly obscured by another visible window above it.  This flag is set to true
+     * even if the event did not directly pass through the obscured area.
+     * A security sensitive application can check this flag to identify situations in which
+     * a malicious application may have covered up part of its content for the purpose
+     * of misleading the user or hijacking touches.  An appropriate response might be
+     * to drop the suspect touches or to take additional precautions to confirm the user's
+     * actual intent.
+     *
+     * Unlike FLAG_WINDOW_IS_OBSCURED, this is actually true.
+     * @hide
+     */
+    public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
+
+    /**
      * Private flag that indicates when the system has detected that this motion event
      * may be inconsistent with respect to the sequence of previously delivered motion events,
      * such as when a pointer move event is sent but the pointer is not down.
index 187c8af..b0d204b 100644 (file)
@@ -727,6 +727,8 @@ public interface WindowManagerPolicy {
      * @param labelRes The resource ID the application would like to use as its name.
      * @param icon The resource ID the application would like to use as its icon.
      * @param windowFlags Window layout flags.
+     * @param overrideConfig override configuration to consider when generating
+     *        context to for resources.
      *
      * @return Optionally you can return the View that was used to create the
      *         window, for easy removal in removeStartingWindow.
@@ -735,7 +737,7 @@ public interface WindowManagerPolicy {
      */
     public View addStartingWindow(IBinder appToken, String packageName,
             int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
-            int labelRes, int icon, int logo, int windowFlags);
+            int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig);
 
     /**
      * Called when the first window of an application has been displayed, while
index 8e38c5a..a2a135b 100644 (file)
@@ -18,10 +18,13 @@ package com.android.internal.app;
 
 import com.android.internal.os.BatteryStatsImpl;
 
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.ParcelFileDescriptor;
 import android.os.WorkSource;
 import android.os.health.HealthStatsParceler;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
 import android.telephony.SignalStrength;
 
 interface IBatteryStats {
@@ -129,4 +132,8 @@ interface IBatteryStats {
 
     HealthStatsParceler takeUidSnapshot(int uid);
     HealthStatsParceler[] takeUidSnapshots(in int[] uid);
+
+    oneway void noteBluetoothControllerActivity(in BluetoothActivityEnergyInfo info);
+    oneway void noteModemControllerActivity(in ModemActivityInfo info);
+    oneway void noteWifiControllerActivity(in WifiActivityEnergyInfo info);
 }
index f3ae688..6dc0c60 100644 (file)
@@ -972,7 +972,7 @@ public class InputMethodUtils {
         private int getInt(final String key, final int defaultValue) {
             if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
                 final String result = mCopyOnWriteDataStore.get(key);
-                return result != null ? Integer.valueOf(result) : 0;
+                return result != null ? Integer.parseInt(result) : 0;
             }
             return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
         }
@@ -1273,7 +1273,7 @@ public class InputMethodUtils {
                             if (s.equals(subtypeHashCode)) {
                                 // If both imeId and subtypeId are enabled, return subtypeId.
                                 try {
-                                    final int hashCode = Integer.valueOf(subtypeHashCode);
+                                    final int hashCode = Integer.parseInt(subtypeHashCode);
                                     // Check whether the subtype id is valid or not
                                     if (isValidSubtypeId(imi, hashCode)) {
                                         return s;
index 01349bb..5c92f3c 100644 (file)
@@ -125,7 +125,7 @@ public class VpnProfile implements Cloneable, Parcelable {
 
             VpnProfile profile = new VpnProfile(key);
             profile.name = values[0];
-            profile.type = Integer.valueOf(values[1]);
+            profile.type = Integer.parseInt(values[1]);
             if (profile.type < 0 || profile.type > TYPE_MAX) {
                 return null;
             }
index 738aaca..b1598e7 100644 (file)
@@ -270,15 +270,12 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
             mLastXOffset = xOffset;
             mLastYOffset = yOffset;
 
-            // Only clip the content to the bounds if we are not fullscreen. In the other case, we
-            // actually need to draw outside these.
-            if (mResizeMode == RESIZE_MODE_FREEFORM) {
-                mRenderer.setContentDrawBounds(
-                        mLastXOffset,
-                        mLastYOffset,
-                        mLastXOffset + mLastContentWidth,
-                        mLastYOffset + mLastCaptionHeight + mLastContentHeight);
-            }
+            // Inform the renderer of the content's new bounds
+            mRenderer.setContentDrawBounds(
+                    mLastXOffset,
+                    mLastYOffset,
+                    mLastXOffset + mLastContentWidth,
+                    mLastYOffset + mLastCaptionHeight + mLastContentHeight);
 
             // If this was the first call and redrawLocked got already called prior
             // to us, we should re-issue a redrawLocked now.
index 4f17c39..eac9f64 100644 (file)
 package com.android.internal.policy;
 
 import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
 import android.view.ContextThemeWrapper;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
 /**
  * Context for decor views which can be seeded with pure application context and not depend on the
- * activity, but still provide some of the facilities that Activity has, e.g. themes.
+ * activity, but still provide some of the facilities that Activity has,
+ * e.g. themes, activity-based resources, etc.
  *
  * @hide
  */
 class DecorContext extends ContextThemeWrapper {
     private PhoneWindow mPhoneWindow;
     private WindowManager mWindowManager;
+    private Resources mActivityResources;
 
-    public DecorContext(Context context) {
+    public DecorContext(Context context, Resources activityResources) {
         super(context, null);
+        mActivityResources = activityResources;
     }
 
     void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -52,4 +57,14 @@ class DecorContext extends ContextThemeWrapper {
         }
         return super.getSystemService(name);
     }
+
+    @Override
+    public Resources getResources() {
+        return mActivityResources;
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        return mActivityResources.getAssets();
+    }
 }
index fe63267..151c530 100644 (file)
@@ -2297,7 +2297,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
             if (applicationContext == null) {
                 context = getContext();
             } else {
-                context = new DecorContext(applicationContext);
+                context = new DecorContext(applicationContext, getContext().getResources());
                 if (mTheme != -1) {
                     context.setTheme(mTheme);
                 }
index cadfd3d..d6f9db5 100644 (file)
@@ -105,8 +105,10 @@ static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobje
         jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    sp<GlFunctorReleasedCallbackBridge> bridge(new GlFunctorReleasedCallbackBridge(
-            env, releasedCallback));
+    sp<GlFunctorReleasedCallbackBridge> bridge;
+    if (releasedCallback) {
+        bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback);
+    }
     canvas->callDrawGLFunction(functor, bridge.get());
 }
 
index 4459f32..d3e18ed 100644 (file)
@@ -564,7 +564,8 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
 
             auto functor = std::bind(
                 std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this,
-                (jlong) info.frameNumber, (jint) bounds.left, (jint) bounds.top,
+                (jlong) info.canvasContext.getFrameNumber(),
+                (jint) bounds.left, (jint) bounds.top,
                 (jint) bounds.right, (jint) bounds.bottom);
 
             info.canvasContext.enqueueFrameWork(std::move(functor));
index 23f8810..96915f4 100644 (file)
@@ -38,7 +38,7 @@ public class SpannableStringBuilderBenchmark {
     @BeforeExperiment
     protected void setUp() throws Exception {
         clazz = Class.forName(paramType);
-        int strSize = Integer.valueOf(paramStringMult);
+        int strSize = Integer.parseInt(paramStringMult);
         StringBuilder strBuilder = new StringBuilder();
         for (int i = 0; i < strSize; i++) {
             strBuilder.append(TEST_STRING);
index dc5fed0..1286bcc 100644 (file)
@@ -30,7 +30,7 @@ public class SpannableStringInternalCopyBenchmark {
 
     @BeforeExperiment
     protected void setUp() throws Exception {
-        int strSize = Integer.valueOf(paramStringMult);
+        int strSize = Integer.parseInt(paramStringMult);
         StringBuilder strBuilder = new StringBuilder();
         for (int i = 0; i < strSize; i++) {
             strBuilder.append(SpannableStringBuilderBenchmark.TEST_STRING);
index a6e433f..24b20ca 100644 (file)
@@ -49,7 +49,7 @@ public class LinkifyBenchmark {
 
     @BeforeExperiment
     protected void setUp() throws Exception {
-        int copyAmount = Integer.valueOf(mParamCopyAmount);
+        int copyAmount = Integer.parseInt(mParamCopyAmount);
         StringBuilder strBuilder = new StringBuilder();
         for (int i = 0; i < copyAmount; i++) {
             strBuilder.append(mParamBasicText);
index 6913f43..715c875 100644 (file)
@@ -34,9 +34,7 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 #include <utils/Timers.h>
-#ifdef __ANDROID__
-#include <cutils/trace.h>
-#endif
+#include <utils/Trace.h>
 
 #include <assert.h>
 #include <dirent.h>
     _rc; })
 #endif
 
-#ifdef __ANDROID__
-#define MY_TRACE_BEGIN(x) ATRACE_BEGIN(x)
-#define MY_TRACE_END() ATRACE_END()
-#else
-#define MY_TRACE_BEGIN(x)
-#define MY_TRACE_END()
-#endif
-
 using namespace android;
 
 static const bool kIsDebug = false;
@@ -623,7 +613,7 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con
     ResTable* sharedRes = NULL;
     bool shared = true;
     bool onlyEmptyResources = true;
-    MY_TRACE_BEGIN(ap.path.string());
+    ATRACE_NAME(ap.path.string());
     Asset* idmap = openIdmapLocked(ap);
     size_t nextEntryIdx = mResources->getTableCount();
     ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
@@ -703,8 +693,6 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con
     if (idmap != NULL) {
         delete idmap;
     }
-    MY_TRACE_END();
-
     return onlyEmptyResources;
 }
 
@@ -752,6 +740,7 @@ const ResTable* AssetManager::getResTable(bool required) const
 
 void AssetManager::updateResourceParamsLocked() const
 {
+    ATRACE_CALL();
     ResTable* res = mResources;
     if (!res) {
         return;
index 1ccc59a..15cb684 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <algorithm>
 #include <limits>
 #include <memory>
 #include <type_traits>
@@ -5810,6 +5811,10 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con
     return NULL;
 }
 
+static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
+    return a.compare(b) < 0;
+}
+
 void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
         bool ignoreAndroidPackage, bool includeSystemConfigs) const {
     const size_t packageCount = mPackageGroups.size();
@@ -5840,17 +5845,11 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMi
                     ResTable_config cfg;
                     memset(&cfg, 0, sizeof(ResTable_config));
                     cfg.copyFromDtoH(config->config);
-                    // only insert unique
-                    const size_t N = configs->size();
-                    size_t n;
-                    for (n = 0; n < N; n++) {
-                        if (0 == (*configs)[n].compare(cfg)) {
-                            break;
-                        }
-                    }
-                    // if we didn't find it
-                    if (n == N) {
-                        configs->add(cfg);
+
+                    auto iter = std::lower_bound(configs->begin(), configs->end(), cfg,
+                                                 compareResTableConfig);
+                    if (iter == configs->end() || iter->compare(cfg) != 0) {
+                        configs->insertAt(cfg, std::distance(configs->begin(), iter));
                     }
                 }
             }
@@ -5858,6 +5857,10 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMi
     }
 }
 
+static bool compareString8AndCString(const String8& str, const char* cStr) {
+    return strcmp(str.string(), cStr) < 0;
+}
+
 void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
 {
     Vector<ResTable_config> configs;
@@ -5872,15 +5875,11 @@ void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) c
     char locale[RESTABLE_MAX_LOCALE_LEN];
     for (size_t i=0; i<I; i++) {
         configs[i].getBcp47Locale(locale);
-        const size_t J = locales->size();
-        size_t j;
-        for (j=0; j<J; j++) {
-            if (0 == strcmp(locale, (*locales)[j].string())) {
-                break;
-            }
-        }
-        if (j == J) {
-            locales->add(String8(locale));
+
+        auto iter = std::lower_bound(locales->begin(), locales->end(), locale,
+                                     compareString8AndCString);
+        if (iter == locales->end() || strcmp(iter->string(), locale) != 0) {
+            locales->insertAt(String8(locale), std::distance(locales->begin(), iter));
         }
     }
 }
index 7cd7fb5..b8b4625 100644 (file)
@@ -39,8 +39,20 @@ namespace {
  */
 #include "data/basic/basic_arsc.h"
 
+/**
+ * Include a binary library resource table.
+ *
+ * Package: com.android.test.basic
+ */
 #include "data/lib/lib_arsc.h"
 
+/**
+ * Include a system resource table.
+ *
+ * Package: android
+ */
+#include "data/system/system_arsc.h"
+
 TEST(ResTableTest, shouldLoadSuccessfully) {
     ResTable table;
     ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -324,4 +336,25 @@ TEST(ResTableTest, ShareButDontModifyResTable) {
     ASSERT_EQ(uint32_t(600), val.data);
 }
 
+TEST(ResTableTest, GetConfigurationsReturnsUniqueList) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable_config configSv;
+    memset(&configSv, 0, sizeof(configSv));
+    configSv.language[0] = 's';
+    configSv.language[1] = 'v';
+
+    Vector<ResTable_config> configs;
+    table.getConfigurations(&configs);
+
+    EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
+
+    Vector<String8> locales;
+    table.getLocales(&locales);
+
+    EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
+}
+
 } // namespace
index ac80d88..ff9be16 100644 (file)
@@ -21,7 +21,7 @@ namespace android {
 enum { MAY_NOT_BE_BAG = false };
 
 static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
-    return memcmp(&a, &b, sizeof(a)) == 0;
+    return a.compare(b) == 0;
 }
 
 static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
index 27f25fe..6a31fb8 100644 (file)
@@ -33,6 +33,12 @@ namespace style {
     };
 }
 
+namespace integer {
+    enum {
+        number = 0x01030000, // sv
+    };
+}
+
 } // namespace R
 } // namespace android
 
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
new file mode 100644 (file)
index 0000000..b97bdb6
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <public type="integer" name="number" id="0x01030000" />
+    <integer name="number">1</integer>
+</resources>
index 215ecae..b0dab6b 100644 (file)
@@ -1,8 +1,8 @@
 unsigned char system_arsc[] = {
-  0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x0c, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, 0x03, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
   0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -25,26 +25,33 @@ unsigned char system_arsc[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
-  0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
-  0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
-  0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x78, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x5e, 0x00,
+  0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x70, 0x00,
+  0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x84, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
   0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
   0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
   0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
   0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
   0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
-  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
-  0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+  0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -55,15 +62,27 @@ unsigned char system_arsc[] = {
   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
   0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
-  0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00,
+  0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
-  0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+  0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01,
+  0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x60, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
-unsigned int system_arsc_len = 792;
+unsigned int system_arsc_len = 1016;
index 6fc74a5..502f027 100644 (file)
 namespace android {
 namespace uirenderer {
 
-FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
-        const std::vector< sp<RenderNode> >& nodes,
-        const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
-        : mCanvasState(*this)
+        const LightGeometry& lightGeometry, Caches& caches)
+        : mStdAllocator(mAllocator)
+        , mLayerBuilders(mStdAllocator)
+        , mLayerStack(mStdAllocator)
+        , mCanvasState(*this)
         , mCaches(caches)
         , mLightRadius(lightGeometry.radius)
-        , mDrawFbo0(!nodes.empty()) {
-    ATRACE_NAME("prepare drawing commands");
-
-    mLayerBuilders.reserve(layers.entries().size());
-    mLayerStack.reserve(layers.entries().size());
+        , mDrawFbo0(true) {
 
     // Prepare to defer Fbo0
     auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
@@ -51,7 +49,31 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
             lightGeometry.center);
+}
+
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers,
+        const LightGeometry& lightGeometry, Caches& caches)
+        : mStdAllocator(mAllocator)
+        , mLayerBuilders(mStdAllocator)
+        , mLayerStack(mStdAllocator)
+        , mCanvasState(*this)
+        , mCaches(caches)
+        , mLightRadius(lightGeometry.radius)
+        , mDrawFbo0(false) {
+    // TODO: remove, with each layer on its own save stack
+
+    // Prepare to defer Fbo0 (which will be empty)
+    auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1));
+    mLayerBuilders.push_back(fbo0);
+    mLayerStack.push_back(0);
+    mCanvasState.initializeSaveStack(1, 1,
+            0, 0, 1, 1,
+            lightGeometry.center);
 
+    deferLayers(layers);
+}
+
+void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
     // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
     // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
     for (int i = layers.entries().size() - 1; i >= 0; i--) {
@@ -76,10 +98,45 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
             restoreForLayer();
         }
     }
+}
+
+void FrameBuilder::deferRenderNode(RenderNode& renderNode) {
+    renderNode.computeOrdering();
+
+    mCanvasState.save(SaveFlags::MatrixClip);
+    deferNodePropsAndOps(renderNode);
+    mCanvasState.restore();
+}
+
+void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) {
+    renderNode.computeOrdering();
 
+    mCanvasState.save(SaveFlags::MatrixClip);
+    mCanvasState.translate(tx, ty);
+    mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+            SkRegion::kIntersect_Op);
+    deferNodePropsAndOps(renderNode);
+    mCanvasState.restore();
+}
+
+static Rect nodeBounds(RenderNode& node) {
+    auto& props = node.properties();
+    return Rect(props.getLeft(), props.getTop(),
+            props.getRight(), props.getBottom());
+}
+
+void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
+        const Rect& contentDrawBounds) {
+    if (nodes.size() < 1) return;
+    if (nodes.size() == 1) {
+        if (!nodes[0]->nothingToDraw()) {
+            deferRenderNode(*nodes[0]);
+        }
+        return;
+    }
     // It there are multiple render nodes, they are laid out as follows:
     // #0 - backdrop (content + caption)
-    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+    // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
     // #2 - additional overlay nodes
     // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
     // resizing however it might become partially visible. The following render loop will crop the
@@ -88,45 +145,52 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
     //
     // Additional nodes will be drawn on top with no particular clipping semantics.
 
-    // The bounds of the backdrop against which the content should be clipped.
-    Rect backdropBounds = contentDrawBounds;
     // Usually the contents bounds should be mContentDrawBounds - however - we will
     // move it towards the fixed edge to give it a more stable appearance (for the moment).
     // If there is no content bounds we ignore the layering as stated above and start with 2.
-    int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
-
-    for (const sp<RenderNode>& node : nodes) {
-        if (node->nothingToDraw()) continue;
-        node->computeOrdering();
-        int count = mCanvasState.save(SaveFlags::MatrixClip);
-
-        if (layer == 0) {
-            const RenderProperties& properties = node->properties();
-            Rect targetBounds(properties.getLeft(), properties.getTop(),
-                              properties.getRight(), properties.getBottom());
-            // Move the content bounds towards the fixed corner of the backdrop.
-            const int x = targetBounds.left;
-            const int y = targetBounds.top;
-            // Remember the intersection of the target bounds and the intersection bounds against
-            // which we have to crop the content.
-            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
-            backdropBounds.doIntersect(targetBounds);
-        } else if (layer == 1) {
-            // We shift and clip the content to match its final location in the window.
-            const float left = contentDrawBounds.left;
-            const float top = contentDrawBounds.top;
-            const float dx = backdropBounds.left - left;
-            const float dy = backdropBounds.top - top;
-            const float width = backdropBounds.getWidth();
-            const float height = backdropBounds.getHeight();
-            mCanvasState.translate(dx, dy);
-            // It gets cropped against the bounds of the backdrop to stay inside.
-            mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op);
+
+    // Backdrop bounds in render target space
+    const Rect backdrop = nodeBounds(*nodes[0]);
+
+    // Bounds that content will fill in render target space (note content node bounds may be bigger)
+    Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
+    content.translate(backdrop.left, backdrop.top);
+    if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
+        // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
+
+        // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
+        // also fill left/top. Currently, both 2up and freeform position content at the top/left of
+        // the backdrop, so this isn't necessary.
+        if (content.right < backdrop.right) {
+            // draw backdrop to right side of content
+            deferRenderNode(0, 0, Rect(content.right, backdrop.top,
+                    backdrop.right, backdrop.bottom), *nodes[0]);
+        }
+        if (content.bottom < backdrop.bottom) {
+            // draw backdrop to bottom of content
+            // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
+            deferRenderNode(0, 0, Rect(content.left, content.bottom,
+                    content.right, backdrop.bottom), *nodes[0]);
         }
+    }
+
+    if (!backdrop.isEmpty()) {
+        // content node translation to catch up with backdrop
+        float dx = contentDrawBounds.left - backdrop.left;
+        float dy = contentDrawBounds.top - backdrop.top;
 
-        deferNodePropsAndOps(*node);
-        mCanvasState.restoreToCount(count);
-        layer++;
+        Rect contentLocalClip = backdrop;
+        contentLocalClip.translate(dx, dy);
+        deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
+    } else {
+        deferRenderNode(*nodes[1]);
+    }
+
+    // remaining overlay nodes, simply defer
+    for (size_t index = 2; index < nodes.size(); index++) {
+        if (!nodes[index]->nothingToDraw()) {
+            deferRenderNode(*nodes[index]);
+        }
     }
 }
 
index a6fd761..b915443 100644 (file)
@@ -37,8 +37,8 @@ class OffscreenBuffer;
 class Rect;
 
 /**
- * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
- * them to be rendered.
+ * Processes, optimizes, and stores rendering commands from RenderNodes and
+ * LayerUpdateQueue, building content needed to render a frame.
  *
  * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
  * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
@@ -60,21 +60,21 @@ public:
         float radius;
     };
 
-    // TODO: remove
-    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+    FrameBuilder(const SkRect& clip,
             uint32_t viewportWidth, uint32_t viewportHeight,
-            const std::vector< sp<RenderNode> >& nodes,
-            const LightGeometry& lightGeometry,
-            Caches& caches)
-            : FrameBuilder(layers, clip, viewportWidth, viewportHeight,
-                    nodes, lightGeometry, Rect(), caches) {}
+            const LightGeometry& lightGeometry, Caches& caches);
 
-    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
-            uint32_t viewportWidth, uint32_t viewportHeight,
-            const std::vector< sp<RenderNode> >& nodes,
-            const LightGeometry& lightGeometry,
-            const Rect &contentDrawBounds,
-            Caches& caches);
+    FrameBuilder(const LayerUpdateQueue& layerUpdateQueue,
+            const LightGeometry& lightGeometry, Caches& caches);
+
+    void deferLayers(const LayerUpdateQueue& layers);
+
+    void deferRenderNode(RenderNode& renderNode);
+
+    void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode);
+
+    void deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
+            const Rect& contentDrawBounds);
 
     virtual ~FrameBuilder() {}
 
@@ -223,8 +223,12 @@ private:
     MAP_DEFERRABLE_OPS(X)
 #undef X
 
+    // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
+    LinearAllocator mAllocator;
+    LinearStdAllocator<void*> mStdAllocator;
+
     // List of every deferred layer's render state. Replayed in reverse order to render a frame.
-    std::vector<LayerBuilder*> mLayerBuilders;
+    LsaVector<LayerBuilder*> mLayerBuilders;
 
     /*
      * Stack of indices within mLayerBuilders representing currently active layers. If drawing
@@ -238,7 +242,7 @@ private:
      * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
      * ops added to it.
     */
-    std::vector<size_t> mLayerStack;
+    LsaVector<size_t> mLayerStack;
 
     CanvasState mCanvasState;
 
@@ -246,9 +250,6 @@ private:
 
     float mLightRadius;
 
-    // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
-    LinearAllocator mAllocator;
-
     const bool mDrawFbo0;
 };
 
index eea11bf..3000777 100644 (file)
@@ -244,7 +244,8 @@ void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bak
 
         if (CC_UNLIKELY(activeUnclippedSaveLayers.empty()
                 && bakedState->computedState.opaqueOverClippedBounds
-                && bakedState->computedState.clippedBounds.contains(repaintRect))) {
+                && bakedState->computedState.clippedBounds.contains(repaintRect)
+                && !Properties::debugOverdraw)) {
             // discard all deferred drawing ops, since new one will occlude them
             clear();
         }
index a43e544..ac2bdcc 100644 (file)
@@ -102,8 +102,6 @@ public:
     // tree state changes
     TreeObserver* observer = nullptr;
 
-    // Frame number for use with synchronized surfaceview position updating
-    int64_t frameNumber = -1;
     int32_t windowInsetLeft = 0;
     int32_t windowInsetTop = 0;
     bool updateWindowPositions = false;
index 890d4a1..e6399d4 100644 (file)
@@ -108,6 +108,8 @@ void CanvasContext::setSurface(Surface* surface) {
         mEglSurface = mEglManager.createSurface(surface);
     }
 
+    mFrameNumber = -1;
+
     if (mEglSurface != EGL_NO_SURFACE) {
         const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
@@ -214,10 +216,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
     info.renderer = mCanvas;
 #endif
 
-    if (CC_LIKELY(mNativeSurface.get())) {
-        info.frameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber());
-    }
-
     mAnimationContext->startFrame(info.mode);
     for (const sp<RenderNode>& node : mRenderNodes) {
         // Only the primary target node will be drawn full - all other nodes would get drawn in
@@ -356,9 +354,13 @@ void CanvasContext::draw() {
 
 #if HWUI_NEW_OPS
     auto& caches = Caches::getInstance();
-    FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
-            mRenderNodes, mLightGeometry, mContentDrawBounds, caches);
+    FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches);
+
+    frameBuilder.deferLayers(mLayerUpdateQueue);
     mLayerUpdateQueue.clear();
+
+    frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds);
+
     BakedOpRenderer renderer(caches, mRenderThread.renderState(),
             mOpaque, mLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -511,6 +513,7 @@ void CanvasContext::draw() {
         swap.swapTime = systemTime(CLOCK_MONOTONIC);
         swap.vsyncTime = mRenderThread.timeLord().latestVsync();
         mHaveNewSurface = false;
+        mFrameNumber = -1;
     }
 
     // TODO: Use a fence for real completion?
@@ -623,8 +626,7 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
 #if HWUI_NEW_OPS
     static const std::vector< sp<RenderNode> > emptyNodeList;
     auto& caches = Caches::getInstance();
-    FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
-            emptyNodeList, mLightGeometry, mContentDrawBounds, caches);
+    FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches);
     mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(caches, mRenderThread.renderState(),
             mOpaque, mLightInfo);
@@ -779,6 +781,14 @@ void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
     mFrameWorkProcessor->add(task);
 }
 
+int64_t CanvasContext::getFrameNumber() {
+    // mFrameNumber is reset to -1 when the surface changes or we swap buffers
+    if (mFrameNumber == -1 && mNativeSurface.get()) {
+        mFrameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber());
+    }
+    return mFrameNumber;
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
index 52df3ab..e739b29 100644 (file)
@@ -166,6 +166,8 @@ public:
     // Used to queue up work that needs to be completed before this frame completes
     ANDROID_API void enqueueFrameWork(std::function<void()>&& func);
 
+    ANDROID_API int64_t getFrameNumber();
+
 private:
     friend class RegisterFrameCallbackTask;
     // TODO: Replace with something better for layer & other GL object
@@ -195,6 +197,7 @@ private:
     };
 
     RingBuffer<SwapHistory, 3> mSwapHistory;
+    int64_t mFrameNumber = -1;
 
     bool mOpaque;
 #if HWUI_NEW_OPS
index 5f4ebc0..dbaefa4 100644 (file)
@@ -171,11 +171,9 @@ public:
         syncHierarchyPropertiesAndDisplayListImpl(node.get());
     }
 
-    static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
-        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-        std::vector<sp<RenderNode>> vec;
-        vec.emplace_back(node);
-        return vec;
+    static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) {
+        syncHierarchyPropertiesAndDisplayList(node);
+        return node;
     }
 
     typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
index 0aef620..84ef9c2 100644 (file)
@@ -35,11 +35,10 @@ using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
 using namespace android::uirenderer::test;
 
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
 const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
-static std::vector<sp<RenderNode>> createTestNodeList() {
+static sp<RenderNode> createTestNode() {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
@@ -56,31 +55,33 @@ static std::vector<sp<RenderNode>> createTestNodeList() {
         canvas.restore();
     });
     TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-    std::vector<sp<RenderNode>> vec;
-    vec.emplace_back(node);
-    return vec;
+    return node;
 }
 
 void BM_FrameBuilder_defer(benchmark::State& state) {
-    auto nodes = createTestNodeList();
-    while (state.KeepRunning()) {
-        FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-                nodes, sLightGeometry, Caches::getInstance());
-        benchmark::DoNotOptimize(&frameBuilder);
-    }
+    TestUtils::runOnRenderThread([&state](RenderThread& thread) {
+        auto node = createTestNode();
+        while (state.KeepRunning()) {
+            FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+                    sLightGeometry, Caches::getInstance());
+            frameBuilder.deferRenderNode(*node);
+            benchmark::DoNotOptimize(&frameBuilder);
+        }
+    });
 }
 BENCHMARK(BM_FrameBuilder_defer);
 
 void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
-        auto nodes = createTestNodeList();
+        auto node = createTestNode();
 
         RenderState& renderState = thread.renderState();
         Caches& caches = Caches::getInstance();
 
         while (state.KeepRunning()) {
-            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-                    nodes, sLightGeometry, Caches::getInstance());
+            FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+                    sLightGeometry, caches);
+            frameBuilder.deferRenderNode(*node);
 
             BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
             frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -90,7 +91,7 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
 }
 BENCHMARK(BM_FrameBuilder_deferAndRender);
 
-static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) {
+static sp<RenderNode> getSyncedSceneNode(const char* sceneName) {
     gDisplay = getBuiltInDisplay(); // switch to real display if present
 
     TestContext testContext;
@@ -103,9 +104,7 @@ static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) {
     });
 
     TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
-    std::vector<sp<RenderNode>> nodes;
-    nodes.emplace_back(rootNode);
-    return nodes;
+    return rootNode;
 }
 
 static auto SCENES = {
@@ -116,11 +115,12 @@ void BM_FrameBuilder_defer_scene(benchmark::State& state) {
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
         const char* sceneName = *(SCENES.begin() + state.range_x());
         state.SetLabel(sceneName);
-        auto nodes = getSyncedSceneNodes(sceneName);
+        auto node = getSyncedSceneNode(sceneName);
         while (state.KeepRunning()) {
-            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
-                    SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
-                    nodes, sLightGeometry, Caches::getInstance());
+            FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h),
+                    gDisplay.w, gDisplay.h,
+                    sLightGeometry, Caches::getInstance());
+            frameBuilder.deferRenderNode(*node);
             benchmark::DoNotOptimize(&frameBuilder);
         }
     });
@@ -131,15 +131,16 @@ void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) {
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
         const char* sceneName = *(SCENES.begin() + state.range_x());
         state.SetLabel(sceneName);
-        auto nodes = getSyncedSceneNodes(sceneName);
+        auto node = getSyncedSceneNode(sceneName);
 
         RenderState& renderState = thread.renderState();
         Caches& caches = Caches::getInstance();
 
         while (state.KeepRunning()) {
-            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
-                    SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
-                    nodes, sLightGeometry, Caches::getInstance());
+            FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h),
+                    gDisplay.w, gDisplay.h,
+                    sLightGeometry, Caches::getInstance());
+            frameBuilder.deferRenderNode(*node);
 
             BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
             frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
index ebc1c80..0f16b15 100644 (file)
 namespace android {
 namespace uirenderer {
 
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
-const std::vector< sp<RenderNode> > sEmptyNodeList;
 const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
 
-
 /**
  * Virtual class implemented by each test to redirect static operation / state transitions to
  * virtual methods.
@@ -136,8 +133,10 @@ RENDERTHREAD_TEST(FrameBuilder, simple) {
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
@@ -162,8 +161,10 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
         strokedPaint.setStrokeWidth(10);
         canvas.drawPoint(50, 50, strokedPaint);
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SimpleStrokeTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
@@ -177,8 +178,9 @@ RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
@@ -211,15 +213,111 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
         }
         canvas.restore();
     });
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SimpleBatchingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
+RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
+    class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
+    public:
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+            EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
+            EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
+                    state.computedState.clipSideFlags);
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 100, 100,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 100, 100, SkPaint());
+    });
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
+            *TestUtils::getSyncedNode(node));
+
+    DeferRenderNodeTranslateClipTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(1, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
+    class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
+    public:
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            const Rect& clippedBounds = state.computedState.clippedBounds;
+            Matrix4 expected;
+            switch (mIndex++) {
+            case 0:
+                // background - left side
+                EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
+                expected.loadTranslate(100, 100, 0);
+                break;
+            case 1:
+                // background - top side
+                EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
+                expected.loadTranslate(100, 100, 0);
+                break;
+            case 2:
+                // content
+                EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
+                expected.loadTranslate(-50, -50, 0);
+                break;
+            case 3:
+                // overlay
+                EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
+                break;
+            default:
+                ADD_FAILURE() << "Too many rects observed";
+            }
+            EXPECT_EQ(expected, state.computedState.transform);
+        }
+    };
+
+    std::vector<sp<RenderNode>> nodes;
+    SkPaint transparentPaint;
+    transparentPaint.setAlpha(128);
+
+    // backdrop
+    nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400
+            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 600, 400, transparentPaint);
+    }));
+
+    // content
+    Rect contentDrawBounds(150, 150, 650, 450); // 500x300
+    nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 800, 600, transparentPaint);
+    }));
+
+    // overlay
+    nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 800, 200, transparentPaint);
+    }));
+
+    for (auto& node : nodes) {
+        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    }
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
+
+    DeferRenderNodeSceneTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
+}
+
 RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
     class EmptyNoFbo0TestRenderer : public TestRendererBase {
     public:
@@ -231,9 +329,9 @@ RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
         }
     };
 
-    // Pass empty node list, so no work is enqueued for Fbo0
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            sEmptyNodeList, sLightGeometry, Caches::getInstance());
+    // Use layer update constructor, so no work is enqueued for Fbo0
+    LayerUpdateQueue emptyLayerUpdateQueue;
+    FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
     EmptyNoFbo0TestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -252,11 +350,12 @@ RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // no drawn content
     });
-    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
 
-    // Draw, but pass empty node list, so no work is done for primary frame
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedNodeList, sLightGeometry, Caches::getInstance());
+    // Draw, but pass node without draw content, so no work is done for primary frame
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     EmptyWithFbo0TestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
@@ -281,9 +380,9 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
 
     // Damage (and therefore clip) is same as last draw, subset of renderable area.
     // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
-    SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
             << "Recording must not have rejected ops, in order for this test to be valid";
@@ -324,9 +423,9 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
         canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
         canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
     });
-
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
             << "Recording must not have rejected ops, in order for this test to be valid";
@@ -369,8 +468,10 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
         canvas.drawBitmap(bitmap, 40, 70, nullptr);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     ClippedMergingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -397,8 +498,10 @@ RENDERTHREAD_TEST(FrameBuilder, textMerging) {
         TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
         TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextMergingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
@@ -428,8 +531,11 @@ RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
             TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
         }
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextStrikethroughTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
@@ -485,8 +591,9 @@ RENDERTHREAD_TEST(FrameBuilder, textStyle) {
             TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
         }
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
     TextStyleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
@@ -516,8 +623,11 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextureLayerClipLocalMatrixTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
@@ -546,8 +656,10 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextureLayerCombineMatricesTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
@@ -562,8 +674,11 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
             [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.drawLayer(layerUpdater.get());
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -584,9 +699,10 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
         canvas.callDrawGLFunction(&noopFunctor, nullptr);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(scrolledFunctorView),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
+
     FunctorTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
@@ -608,9 +724,10 @@ RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
         canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(unclippedColorView),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
+
     ColorTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
@@ -654,8 +771,10 @@ TEST(FrameBuilder, renderNode) {
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     RenderNodeTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -678,9 +797,11 @@ RENDERTHREAD_TEST(FrameBuilder, clipped) {
         canvas.drawBitmap(bitmap, 0, 0, nullptr);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
-            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
-            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    // clip to small area, should see in receiver
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     ClippedTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -725,8 +846,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
         canvas.drawRect(10, 10, 190, 190, SkPaint());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
@@ -806,8 +930,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerNestedTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(12, renderer.getIndex());
@@ -826,8 +952,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
         canvas.restore();
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     FailRenderer renderer;
     // should see no ops, even within the layer, since the layer should be rejected
@@ -869,8 +997,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -923,8 +1054,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restoreToCount(restoreTo);
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedMergedClearsTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex())
@@ -964,8 +1098,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
     });
 
     // draw with partial screen dirty, and assert we see that rect later
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedClearClipTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -981,8 +1117,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
     });
 
     // draw with partial screen dirty that doesn't intersect with savelayer
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -1046,8 +1184,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
         canvas.restore();
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedComplexTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(13, renderer.getIndex());
@@ -1098,14 +1239,17 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
     *layerHandle = &layer;
 
-    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+    auto syncedNode = TestUtils::getSyncedNode(node);
 
     // only enqueue partial damage
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedNodeList, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     HwLayerSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1202,14 +1346,17 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
     OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
     *(parent->getLayerHandle()) = &parentLayer;
 
-    auto syncedList = TestUtils::createSyncedNodeList(parent);
+    auto syncedNode = TestUtils::getSyncedNode(parent);
 
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
 
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedList, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     HwLayerComplexTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(14, renderer.getIndex());
@@ -1260,15 +1407,14 @@ RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
     *layerHandle = &layer;
 
-    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
 
     // only enqueue partial damage
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
     // Draw, but pass empty node list, so no work is done for primary frame
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
-            sEmptyNodeList, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
     BuildLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
@@ -1315,8 +1461,10 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) {
         drawOrderedRect(&canvas, 8);
         drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ZReorderTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
@@ -1406,8 +1554,10 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ProjectionReorderTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
@@ -1486,11 +1636,16 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
     layer.setWindowTransform(windowTransform);
     *layerHandle = &layer;
 
-    auto syncedList = TestUtils::createSyncedNodeList(parent);
+    auto syncedNode = TestUtils::getSyncedNode(parent);
+
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            syncedList, sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     ProjectionHwLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1545,8 +1700,10 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
         canvas.drawRenderNode(child.get());
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ProjectionChildScrollTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1588,8 +1745,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadow) {
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1632,9 +1791,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
         canvas.restoreToCount(count);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowSaveLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1681,12 +1841,15 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
     layer.setWindowTransform(windowTransform);
     *layerHandle = &layer;
 
-    auto syncedList = TestUtils::createSyncedNodeList(parent);
+    auto syncedNode = TestUtils::getSyncedNode(parent);
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedList,
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     ShadowHwLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
@@ -1713,10 +1876,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
     });
-
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowLayeringTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -1743,9 +1906,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent),
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowClippingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1772,8 +1936,10 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac
         canvas.drawRect(0, 0, 100, 100, paint);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     PropertyTestRenderer renderer(opValidateCallback);
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
@@ -1915,10 +2081,12 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
         paint.setColor(SK_ColorWHITE);
         canvas.drawRect(0, 0, 10000, 10000, paint);
     });
-    auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
+    auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+                sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*syncedNode);
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            nodes, sLightGeometry, Caches::getInstance());
     SaveLayerAlphaClipTestRenderer renderer(outObservedData);
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 
@@ -1991,8 +2159,10 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
         canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     ClipReplaceTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
index e2fc376..6148b33 100644 (file)
@@ -26,7 +26,6 @@
 using namespace android;
 using namespace android::uirenderer;
 
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
 const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
@@ -43,8 +42,9 @@ RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
     RenderState& renderState = renderThread.renderState();
     Caches& caches = Caches::getInstance();
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometery, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
     BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }
@@ -59,8 +59,9 @@ RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
     RenderState& renderState = renderThread.renderState();
     Caches& caches = Caches::getInstance();
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometery, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
     BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }
index 2cd9872..baa6b0f 100644 (file)
@@ -135,7 +135,7 @@ public final class MediaBrowser {
         mContext = context;
         mServiceComponent = serviceComponent;
         mCallback = callback;
-        mRootHints = rootHints;
+        mRootHints = rootHints == null ? null : new Bundle(rootHints);
     }
 
     /**
@@ -444,7 +444,7 @@ public final class MediaBrowser {
             }
         };
         try {
-            mServiceBinder.getMediaItem(mediaId, receiver);
+            mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
         } catch (RemoteException e) {
             Log.i(TAG, "Remote error getting media item.");
             mHandler.post(new Runnable() {
index eef5a7c..6b6a561 100644 (file)
@@ -20,5 +20,5 @@ oneway interface IMediaBrowserService {
     void addSubscription(String uri, in IBinder token, in Bundle options,
             IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
-    void getMediaItem(String uri, in ResultReceiver cb);
+    void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
 }
index ddc0e88..4b88926 100644 (file)
@@ -97,6 +97,7 @@ public abstract class MediaBrowserService extends Service {
     private @interface ResultFlags { }
 
     private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
+    private ConnectionRecord mCurConnection;
     private final Handler mHandler = new Handler();
     private ServiceBinder mBinder;
     MediaSession.Token mSession;
@@ -291,7 +292,8 @@ public abstract class MediaBrowserService extends Service {
         }
 
         @Override
-        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
+        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
+                final IMediaBrowserServiceCallbacks callbacks) {
             if (TextUtils.isEmpty(mediaId) || receiver == null) {
                 return;
             }
@@ -299,7 +301,13 @@ public abstract class MediaBrowserService extends Service {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    performLoadItem(mediaId, receiver);
+                    final IBinder b = callbacks.asBinder();
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
+                        return;
+                    }
+                    performLoadItem(mediaId, connection, receiver);
                 }
             });
         }
@@ -470,6 +478,20 @@ public abstract class MediaBrowserService extends Service {
     }
 
     /**
+     * Gets the root hints sent from the currently connected {@link MediaBrowser}.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren}
+     *             or {@link #onLoadItem}
+     */
+    public final Bundle getBrowserRootHints() {
+        if (mCurConnection == null) {
+            throw new IllegalStateException("This should be called inside of onLoadChildren or"
+                    + " onLoadItem methods");
+        }
+        return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
+    }
+
+    /**
      * Notifies all connected media browsers that the children of
      * the specified parent id have changed in some way.
      * This will cause browsers to fetch subscribed content again.
@@ -619,11 +641,13 @@ public abstract class MediaBrowserService extends Service {
             }
         };
 
+        mCurConnection = connection;
         if (options == null) {
             onLoadChildren(parentId, result);
         } else {
             onLoadChildren(parentId, result, options);
         }
+        mCurConnection = null;
 
         if (!result.isDone()) {
             throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
@@ -652,7 +676,8 @@ public abstract class MediaBrowserService extends Service {
         return list.subList(fromIndex, toIndex);
     }
 
-    private void performLoadItem(String itemId, final ResultReceiver receiver) {
+    private void performLoadItem(String itemId, final ConnectionRecord connection,
+            final ResultReceiver receiver) {
         final Result<MediaBrowser.MediaItem> result =
                 new Result<MediaBrowser.MediaItem>(itemId) {
             @Override
@@ -663,7 +688,9 @@ public abstract class MediaBrowserService extends Service {
             }
         };
 
-        MediaBrowserService.this.onLoadItem(itemId, result);
+        mCurConnection = connection;
+        onLoadItem(itemId, result);
+        mCurConnection = null;
 
         if (!result.isDone()) {
             throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
index a9b6bfd..5566b4e 100644 (file)
@@ -378,7 +378,7 @@ public class Camera2RecordingTest extends Camera2SurfaceViewTestCase {
         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
         List<Range<Integer> > fpsRanges = Arrays.asList(
                 mStaticInfo.getAeAvailableTargetFpsRangesChecked());
-        int cameraId = Integer.valueOf(mCamera.getId());
+        int cameraId = Integer.parseInt(mCamera.getId());
         int maxVideoFrameRate = -1;
         for (int profileId : camcorderProfileList) {
             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
@@ -539,7 +539,7 @@ public class Camera2RecordingTest extends Camera2SurfaceViewTestCase {
         int kFrameDrop_Tolerence = FRAMEDROP_TOLERANCE;
 
         for (int profileId : mCamcorderProfileList) {
-            int cameraId = Integer.valueOf(mCamera.getId());
+            int cameraId = Integer.parseInt(mCamera.getId());
             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
                     allowedUnsupported(cameraId, profileId)) {
                 continue;
index 854be0b..b0e5e4e 100644 (file)
@@ -212,6 +212,12 @@ public class OpenExternalDirectoryActivity extends Activity {
                 break;
             }
         }
+        if (internalRoot == null) {
+            // Should not happen on normal circumstances, unless app crafted an invalid volume
+            // using reflection or the list of mounted volumes changed.
+            Log.e(TAG, "Didn't find right volume for '" + storageVolume.dump() + "' on " + volumes);
+            return false;
+        }
 
         // Checks if the user has granted the permission already.
         final Intent intent = getIntentForExistingPermission(activity, isRoot, internalRoot, file);
index 985fe3c..699f827 100644 (file)
     <!-- [CHAR LIMIT=50] Title of the settings section that displays general preferences
          that are applicable to all engines, such as the speech rate -->
     <string name="tts_general_section_title">General</string>
+    <!-- On main TTS Settings screen, in default settings section,
+         reset speech pitch of synthesized voice to 1x speech pitch. [CHAR LIMIT=50] -->
+    <string name="tts_reset_speech_pitch_title">Reset speech pitch</string>
+    <!--On main TTS Settings screen, summary for reset speech pitch of synthesized voice [CHAR LIMIT=150] -->
+    <string name="tts_reset_speech_pitch_summary">Reset the pitch at which the text is spoken to default.</string>
 
     <!-- Default speech rate choices -->
     <string-array name="tts_rate_entries">
index 502eed1..346ae20 100644 (file)
@@ -336,7 +336,7 @@ public class BugreportProgressService extends Service {
                     launchBugreportInfoDialog(id);
                     break;
                 case INTENT_BUGREPORT_SCREENSHOT:
-                    takeScreenshot(id, true);
+                    takeScreenshot(id);
                     break;
                 case INTENT_BUGREPORT_SHARE:
                     shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
@@ -417,8 +417,6 @@ public class BugreportProgressService extends Service {
             return true;
         }
         mProcesses.put(info.id, info);
-        // Take initial screenshot.
-        takeScreenshot(id, false);
         updateProgress(info);
         return true;
     }
@@ -635,19 +633,11 @@ public class BugreportProgressService extends Service {
     /**
      * Starting point for taking a screenshot.
      * <p>
-     * If {@code delayed} is set, it first display a toast message and waits
-     * {@link #SCREENSHOT_DELAY_SECONDS} seconds before taking it, otherwise it takes the screenshot
-     * right away.
-     * <p>
-     * Typical usage is delaying when taken from the notification action, and taking it right away
-     * upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
+     * It first display a toast message and waits {@link #SCREENSHOT_DELAY_SECONDS} seconds before
+     * taking the screenshot.
      */
-    private void takeScreenshot(int id, boolean delayed) {
-        if (delayed) {
-            // Only logs screenshots requested from the notification action.
-            MetricsLogger.action(this,
-                    MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
-        }
+    private void takeScreenshot(int id) {
+        MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
         if (getInfo(id) == null) {
             // Most likely am killed Shell before user tapped the notification. Since system might
             // be too busy anwyays, it's better to ignore the notification and switch back to the
@@ -659,19 +649,15 @@ public class BugreportProgressService extends Service {
             return;
         }
         setTakingScreenshot(true);
-        if (delayed) {
-            collapseNotificationBar();
-            final String msg = mContext.getResources()
-                    .getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
-                            SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
-            Log.i(TAG, msg);
-            // Show a toast just once, otherwise it might be captured in the screenshot.
-            Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
-
-            takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
-        } else {
-            takeScreenshot(id, 0);
-        }
+        collapseNotificationBar();
+        final String msg = mContext.getResources()
+                .getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
+                        SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
+        Log.i(TAG, msg);
+        // Show a toast just once, otherwise it might be captured in the screenshot.
+        Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
+
+        takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
     }
 
     /**
index 537e4c5..f76fb26 100644 (file)
@@ -216,7 +216,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
         Bundle extras =
                 sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
-                NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
+                NAME, NO_TITLE, NO_DESCRIPTION, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -266,7 +266,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
 
         Bundle extras = acceptBugreportAndGetSharedIntent(ID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
-                NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
+                NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -283,6 +283,8 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
         resetProperties();
 
         sendBugreportStarted(1000);
+        waitForScreenshotButtonEnabled(true);
+        takeScreenshot();
         sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
         waitShareNotification(ID);
 
@@ -340,7 +342,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
                 mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
-                NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+                NEW_NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -377,7 +379,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
                 plainText? mPlainTextPath : mZipPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
-                NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+                NEW_NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -404,7 +406,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
 
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
-                NO_NAME, NO_TITLE, mDescription, 1, DIDNT_RENAME_SCREENSHOTS);
+                NO_NAME, NO_TITLE, mDescription, 0, DIDNT_RENAME_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -449,7 +451,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
         // title.txt and description.txt entries.
         extras = sendBugreportFinishedAndGetSharedIntent(ID2, mZipPath2, NO_SCREENSHOT);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID2, PID2, TITLE2,
-                NEW_NAME2, TITLE2, DESCRIPTION2, 1, RENAMED_SCREENSHOTS);
+                NEW_NAME2, TITLE2, DESCRIPTION2, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -500,7 +502,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase {
         // Finally, share bugreport.
         Bundle extras = acceptBugreportAndGetSharedIntent(ID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
-                NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+                NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
diff --git a/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml b/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
new file mode 100644 (file)
index 0000000..dc8e629
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shape="rectangle">
+
+    <solid
+            android:color="@color/recents_tv_card_background_color"/>
+    <corners
+            android:radius="@dimen/recents_tv_card_corner_radius" />
+</shape>
\ No newline at end of file
index d2ec52d..201f47d 100644 (file)
@@ -30,7 +30,7 @@
             android:layout_height="@dimen/recents_tv_screenshot_height"
             android:gravity="center"
             android:orientation="vertical"
-            android:background="@color/recents_tv_card_background_color"
+            android:background="@drawable/recents_tv_card_thumbnail_background"
             android:layout_centerHorizontal="true" >
 
         <ImageView
diff --git a/packages/SystemUI/res/layout/brightness_mirror.xml b/packages/SystemUI/res/layout/brightness_mirror.xml
new file mode 100644 (file)
index 0000000..bbaff6a
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/brightness_mirror"
+    android:layout_width="@dimen/notification_panel_width"
+    android:layout_height="wrap_content"
+    android:layout_gravity="@integer/notification_panel_layout_gravity"
+    android:visibility="invisible">
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/brightness_mirror_background"
+        android:elevation="2dp">
+        <include layout="@layout/quick_settings_brightness_dialog"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+    </FrameLayout>
+</FrameLayout>
index 63b759b..52cab72 100644 (file)
@@ -26,7 +26,7 @@
             android:id="@+id/keyboard_shortcuts_icon"
             android:layout_width="24dp"
             android:layout_height="24dp"
-            android:layout_marginEnd="32dp"
+            android:layout_marginEnd="24dp"
             android:layout_gravity="center_vertical"
             android:visibility="gone"
             android:layout_alignParentStart="true"
index 778ef8f..879505e 100644 (file)
@@ -21,5 +21,5 @@
     android:layout_marginStart="24dp"
     android:layout_marginTop="8dp"
     android:layout_marginEnd="0dp"
-    android:layout_marginBottom="20dp"
+    android:layout_marginBottom="0dp"
     android:background="?android:attr/dividerHorizontal" />
index 381fb16..8414223 100644 (file)
@@ -19,7 +19,8 @@
           android:layout_width="wrap_content"
           android:layout_height="match_parent"
           android:textSize="14sp"
+          android:fontFamily="sans-serif-medium"
           android:paddingStart="24dp"
           android:paddingTop="20dp"
           android:paddingEnd="24dp"
-          android:paddingBottom="13dp"/>
+          android:paddingBottom="10dp"/>
index 625e9c1..43b3de1 100644 (file)
@@ -32,4 +32,5 @@
     android:shadowRadius="5"
     android:fontFamily="sans-serif-medium"
     android:background="?android:selectableItemBackground"
-    android:visibility="invisible" />
+    android:visibility="invisible"
+    android:forceHasOverlappingRendering="false" />
index 9df5dbf..d17601c 100644 (file)
@@ -19,6 +19,7 @@
 <!-- extends LinearLayout -->
 <com.android.systemui.statusbar.SignalClusterView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/signal_cluster"
     android:layout_height="match_parent"
     android:layout_width="wrap_content"
     android:gravity="center_vertical"
index 7c4ce15..285a8ec 100644 (file)
         android:layout_width="match_parent"
         android:layout_height="@dimen/status_bar_height" />
 
-    <FrameLayout android:id="@+id/brightness_mirror"
-                 android:layout_width="@dimen/notification_panel_width"
-                 android:layout_height="wrap_content"
-                 android:layout_gravity="@integer/notification_panel_layout_gravity"
-                 android:visibility="invisible">
-        <FrameLayout
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:elevation="2dp"
-                android:background="@drawable/brightness_mirror_background">
-            <include layout="@layout/quick_settings_brightness_dialog"
-                     android:layout_width="match_parent"
-                     android:layout_height="wrap_content" />
-        </FrameLayout>
-    </FrameLayout>
+    <include layout="@layout/brightness_mirror" />
 
     <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
               android:layout="@layout/car_fullscreen_user_switcher"
index e9448db..3a33992 100644 (file)
@@ -27,7 +27,6 @@
         android:orientation="horizontal"/>
 
     <include layout="@layout/signal_cluster_view"
-        android:id="@+id/signal_cluster"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
index 03b9837..5043610 100644 (file)
     <!-- The amount of overscroll allowed when flinging to the end of the stack. -->
     <dimen name="recents_fling_overscroll_distance">24dp</dimen>
 
+    <!-- The size of the drag hint text. -->
+    <dimen name="recents_drag_hint_text_size">14sp</dimen>
+
     <!-- The min alpha to apply to a task affiliation group color. -->
     <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
 
index c2de150..5fa802b 100644 (file)
@@ -29,6 +29,7 @@
     <dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
     <dimen name="recents_tv_text_padding_start">12dip</dimen>
     <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
+    <dimen name="recents_tv_card_corner_radius">2dip</dimen>
 
     <!-- Padding for grid view in recents view on tv -->
     <dimen name="recents_tv_gird_row_top_margin">215dip</dimen>
index 37b00bb..a03aa28 100644 (file)
     <string name="recents_stack_action_button_label">Clear all</string>
     <!-- Recents: Incompatible task message. [CHAR LIMIT=NONE] -->
     <string name="recents_incompatible_app_message">App doesn\'t support split screen</string>
+    <!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
+    <string name="recents_drag_hint_message">Drag here to use split screen</string>
 
     <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
     <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
index e838191..9a6fa9c 100644 (file)
@@ -51,11 +51,9 @@ public class SwipeHelper implements Gefingerpoken {
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
     private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
-    private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
+    private int MAX_DISMISS_VELOCITY = 4000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
 
-    public static float SWIPE_PROGRESS_FADE_START = 0f; // fraction of thumbnail width
-                                                 // where fade starts
     static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
                                               // beyond which swipe progress->0
     private float mMinSwipeProgress = 0f;
@@ -102,8 +100,7 @@ public class SwipeHelper implements Gefingerpoken {
         mFalsingThreshold = context.getResources().getDimensionPixelSize(
                 R.dimen.swipe_helper_falsing_threshold);
         mFalsingManager = FalsingManager.getInstance(context);
-        mFlingAnimationUtils = new FlingAnimationUtils(context,
-                MAX_ESCAPE_ANIMATION_DURATION / 1000f /* maxLengthSeconds */);
+        mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
     }
 
     public void setLongPressListener(LongPressListener listener) {
@@ -183,21 +180,23 @@ public class SwipeHelper implements Gefingerpoken {
         mMaxSwipeProgress = maxSwipeProgress;
     }
 
-    private float getSwipeProgressForOffset(View view) {
+    private float getSwipeProgressForOffset(View view, float translation) {
         float viewSize = getSize(view);
-        final float fadeSize = SWIPE_PROGRESS_FADE_END * viewSize;
-        float result = 1.0f;
-        float pos = getTranslation(view);
-        if (pos >= viewSize * SWIPE_PROGRESS_FADE_START) {
-            result = 1.0f - (pos - viewSize * SWIPE_PROGRESS_FADE_START) / fadeSize;
-        } else if (pos < viewSize * (1.0f - SWIPE_PROGRESS_FADE_START)) {
-            result = 1.0f + (viewSize * SWIPE_PROGRESS_FADE_START + pos) / fadeSize;
-        }
+        float result = Math.abs(translation / viewSize);
         return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
     }
 
+    private float getSwipeAlpha(float progress) {
+        return Math.min(0, Math.max(1, progress / SWIPE_PROGRESS_FADE_END));
+    }
+
     private void updateSwipeProgressFromOffset(View animView, boolean dismissable) {
-        float swipeProgress = getSwipeProgressForOffset(animView);
+        updateSwipeProgressFromOffset(animView, dismissable, getTranslation(animView));
+    }
+
+    private void updateSwipeProgressFromOffset(View animView, boolean dismissable,
+            float translation) {
+        float swipeProgress = getSwipeProgressForOffset(animView, translation);
         if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
             if (FADE_OUT_DURING_SWIPE && dismissable) {
                 float alpha = swipeProgress;
@@ -208,7 +207,7 @@ public class SwipeHelper implements Gefingerpoken {
                         animView.setLayerType(View.LAYER_TYPE_NONE, null);
                     }
                 }
-                animView.setAlpha(getSwipeProgressForOffset(animView));
+                animView.setAlpha(getSwipeAlpha(swipeProgress));
             }
         }
         invalidateGlobalRegion(animView);
@@ -485,7 +484,7 @@ public class SwipeHelper implements Gefingerpoken {
      * view is being animated to dismiss or snap.
      */
     public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
-        updateSwipeProgressFromOffset(animView, canBeDismissed);
+        updateSwipeProgressFromOffset(animView, canBeDismissed, value);
     }
 
     private void snapChildInstantly(final View view) {
@@ -600,7 +599,15 @@ public class SwipeHelper implements Gefingerpoken {
     }
 
     protected float getEscapeVelocity() {
-        return SWIPE_ESCAPE_VELOCITY * mDensityScale;
+        return getUnscaledEscapeVelocity() * mDensityScale;
+    }
+
+    protected float getUnscaledEscapeVelocity() {
+        return SWIPE_ESCAPE_VELOCITY;
+    }
+
+    protected long getMaxEscapeAnimDuration() {
+        return MAX_ESCAPE_ANIMATION_DURATION;
     }
 
     protected boolean swipedFarEnough() {
index e050b0d..9239ded 100644 (file)
@@ -120,9 +120,8 @@ public class PageIndicator extends ViewGroup {
         }
         ImageView first = (ImageView) getChildAt(firstIndex);
         ImageView second = (ImageView) getChildAt(secondIndex);
-        if (second == null) {
-            // Weird state where number of pages must not have propagated yet.
-            return;
+        if (first == null || second == null) {
+            // may happen during reInflation or other weird cases
         }
         // Lay the two views on top of each other.
         second.setTranslationX(first.getX() - second.getX());
index 4d69280..2040833 100644 (file)
@@ -99,6 +99,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
     private RecentsPackageMonitor mPackageMonitor;
     private long mLastTabKeyEventTime;
     private int mLastDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
+    private int mLastDisplayDensity;
     private boolean mFinishedOnStartup;
     private boolean mIgnoreAltTabRelease;
     private boolean mIsVisible;
@@ -276,7 +277,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
 
-        mLastDeviceOrientation = Utilities.getAppConfiguration(this).orientation;
+        Configuration appConfiguration = Utilities.getAppConfiguration(this);
+        mLastDeviceOrientation = appConfiguration.orientation;
+        mLastDisplayDensity = appConfiguration.densityDpi;
         mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
         mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
             @Override
@@ -349,7 +352,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
         loader.loadTasks(this, loadPlan, loadOpts);
         TaskStack stack = loadPlan.getTaskStack();
         mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
-        mRecentsView.updateStack(stack);
+        mRecentsView.updateStack(stack, true /* setStackViewTasks */);
 
         // Update the nav bar scrim, but defer the animation until the enter-window event
         boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
@@ -427,11 +430,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
         super.onConfigurationChanged(newConfig);
 
         // Notify of the config change
-        int newDeviceOrientation = Utilities.getAppConfiguration(this).orientation;
+        Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
         int numStackTasks = mRecentsView.getStack().getStackTaskCount();
         EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
-                (mLastDeviceOrientation != newDeviceOrientation), numStackTasks > 0));
-        mLastDeviceOrientation = newDeviceOrientation;
+                mLastDeviceOrientation != newDeviceConfiguration.orientation,
+                mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0));
+        mLastDeviceOrientation = newDeviceConfiguration.orientation;
+        mLastDisplayDensity = newDeviceConfiguration.densityDpi;
     }
 
     @Override
@@ -454,14 +459,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
         int numStackTasks = stack.getStackTaskCount();
 
         EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
-                false /* fromDeviceOrientationChange */, numStackTasks > 0));
-
-        if (mRecentsView != null) {
-            mRecentsView.updateStack(stack);
-        }
-
-        EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
+                false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */,
                 numStackTasks > 0));
+        EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, stack));
     }
 
     @Override
index 0413bc9..3ecada9 100644 (file)
@@ -560,7 +560,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
                 com.android.internal.R.dimen.navigation_bar_height);
         mNavBarWidth = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_width);
-        mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+        mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height,
@@ -771,8 +771,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
                     if (icon != null) {
                         icon.setCallback(null);
                     }
-                    mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
+                    mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
                             disabledInSafeMode);
+                    mHeaderBar.onTaskDataLoaded();
                     mHeaderBar.setDimAlpha(toTransform.dimAlpha);
                     mHeaderBar.draw(c);
                     c.setBitmap(null);
index 53b67cf..294c1e7 100644 (file)
@@ -25,12 +25,14 @@ public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
 
     public final boolean fromMultiWindow;
     public final boolean fromDeviceOrientationChange;
+    public final boolean fromDisplayDensityChange;
     public final boolean hasStackTasks;
 
     public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromDeviceOrientationChange,
-            boolean hasStackTasks) {
+            boolean fromDisplayDensityChange, boolean hasStackTasks) {
         this.fromMultiWindow = fromMultiWindow;
         this.fromDeviceOrientationChange = fromDeviceOrientationChange;
+        this.fromDisplayDensityChange = fromDisplayDensityChange;
         this.hasStackTasks = hasStackTasks;
     }
 }
index cf2a68e..11649fb 100644 (file)
 package com.android.systemui.recents.events.activity;
 
 import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.TaskStack;
 
 /**
  * This is sent by the activity whenever the multi-window state has changed.
  */
-public class MultiWindowStateChangedEvent extends EventBus.Event {
+public class MultiWindowStateChangedEvent extends EventBus.AnimatedEvent {
 
     public final boolean inMultiWindow;
-    public final boolean hasStackTasks;
+    public final TaskStack stack;
 
-    public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean hasStackTasks) {
+    public MultiWindowStateChangedEvent(boolean inMultiWindow, TaskStack stack) {
         this.inMultiWindow = inMultiWindow;
-        this.hasStackTasks = hasStackTasks;
+        this.stack = stack;
     }
 }
index 75d5ec5..6f1a69c 100644 (file)
@@ -951,18 +951,6 @@ public class SystemServicesProxy {
     }
 
     /**
-     * Returns the current display orientation.
-     */
-    public int getDisplayOrientation() {
-        // Because of multi-window, the configuration orientation does not necessarily reflect the
-        // orientation of the display, instead we just use the display's real-size.
-        Rect displayRect = getDisplayRect();
-        return displayRect.width() > displayRect.height()
-                ? Configuration.ORIENTATION_LANDSCAPE
-                : Configuration.ORIENTATION_PORTRAIT;
-    }
-
-    /**
      * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
      */
     public Rect getWindowRect() {
index 82c81ae..fb92971 100644 (file)
@@ -353,23 +353,15 @@ public class RecentsTaskLoader {
 
     /**
      * Acquires the task resource data directly from the cache, loading if necessary.
-     *
-     * @param fetchAndInvalidateThumbnails If set, will try loading thumbnails, invalidating them
-     *                                     in the cache and loading if necessary. Otherwise, do not
-     *                                     load the thumbnail unless the icon also has to be loaded.
      */
-    public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
+    public void loadTaskData(Task t) {
         Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
         Bitmap thumbnail = null;
         ActivityManager.TaskThumbnailInfo thumbnailInfo = null;
-        if (fetchAndInvalidateThumbnails) {
-            ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
-            if (thumbnailData != null) {
-                thumbnail = thumbnailData.thumbnail;
-                thumbnailInfo = thumbnailData.thumbnailInfo;
-            }
-        } else {
-            thumbnail = mDefaultThumbnail;
+        ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
+        if (thumbnailData != null) {
+            thumbnail = thumbnailData.thumbnail;
+            thumbnailInfo = thumbnailData.thumbnailInfo;
         }
 
         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
index 95e276f..47995c4 100644 (file)
@@ -30,17 +30,21 @@ import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
+import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntProperty;
 import android.util.SparseArray;
 import android.view.animation.Interpolator;
 
@@ -56,6 +60,8 @@ import com.android.systemui.recents.views.DropTarget;
 import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -240,22 +246,30 @@ public class TaskStack {
      */
     public static class DockState implements DropTarget {
 
+        // The rotation to apply to the hint text
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({HORIZONTAL, VERTICAL})
+        public @interface TextOrientation {}
+        private static final int HORIZONTAL = 0;
+        private static final int VERTICAL = 1;
+
         private static final int DOCK_AREA_ALPHA = 192;
-        public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, null, null, null);
+        public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
+                null, null, null);
         public static final DockState LEFT = new DockState(DOCKED_LEFT,
-                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
+                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
                 new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
                 new RectF(0, 0, 0.5f, 1));
         public static final DockState TOP = new DockState(DOCKED_TOP,
-                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
+                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
                 new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
                 new RectF(0, 0, 1, 0.5f));
         public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
-                DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
+                DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
                 new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
                 new RectF(0.5f, 0, 1, 1));
         public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
-                DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
+                DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
                 new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
                 new RectF(0, 0.5f, 1, 1));
 
@@ -267,33 +281,109 @@ public class TaskStack {
         }
 
         // Represents the view state of this dock state
-        public class ViewState {
+        public static class ViewState {
+            private static final IntProperty<ViewState> HINT_ALPHA =
+                    new IntProperty<ViewState>("drawableAlpha") {
+                        @Override
+                        public void setValue(ViewState object, int alpha) {
+                            object.mHintTextAlpha = alpha;
+                            object.dockAreaOverlay.invalidateSelf();
+                        }
+
+                        @Override
+                        public Integer get(ViewState object) {
+                            return object.mHintTextAlpha;
+                        }
+                    };
+
             public final int dockAreaAlpha;
             public final ColorDrawable dockAreaOverlay;
-            private AnimatorSet dockAreaOverlayAnimator;
-
-            private ViewState(int alpha) {
-                dockAreaAlpha = alpha;
+            public final int hintTextAlpha;
+            public final int hintTextOrientation;
+
+            private final int mHintTextResId;
+            private String mHintText;
+            private Paint mHintTextPaint;
+            private Point mHintTextBounds = new Point();
+            private int mHintTextAlpha = 255;
+            private AnimatorSet mDockAreaOverlayAnimator;
+            private Rect mTmpRect = new Rect();
+
+            private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
+                    int hintTextResId) {
+                dockAreaAlpha = areaAlpha;
                 dockAreaOverlay = new ColorDrawable(0xFFffffff);
                 dockAreaOverlay.setAlpha(0);
+                hintTextAlpha = hintAlpha;
+                hintTextOrientation = hintOrientation;
+                mHintTextResId = hintTextResId;
+                mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+                mHintTextPaint.setColor(Color.WHITE);
+            }
+
+            /**
+             * Updates the view state with the given context.
+             */
+            public void update(Context context) {
+                Resources res = context.getResources();
+                mHintText = context.getString(mHintTextResId);
+                mHintTextPaint.setTextSize(res.getDimensionPixelSize(
+                        R.dimen.recents_drag_hint_text_size));
+                mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
+                mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
+            }
+
+            /**
+             * Draws the current view state.
+             */
+            public void draw(Canvas canvas) {
+                // Draw the overlay background
+                if (dockAreaOverlay.getAlpha() > 0) {
+                    dockAreaOverlay.draw(canvas);
+                }
+
+                // Draw the hint text
+                if (mHintTextAlpha > 0) {
+                    Rect bounds = dockAreaOverlay.getBounds();
+                    int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
+                    int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
+                    mHintTextPaint.setAlpha(mHintTextAlpha);
+                    if (hintTextOrientation == VERTICAL) {
+                        canvas.save();
+                        canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
+                    }
+                    canvas.drawText(mHintText, x, y, mHintTextPaint);
+                    if (hintTextOrientation == VERTICAL) {
+                        canvas.restore();
+                    }
+                }
             }
 
             /**
              * Creates a new bounds and alpha animation.
              */
-            public void startAnimation(Rect bounds, int alpha, int duration,
+            public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
                     Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
-                if (dockAreaOverlayAnimator != null) {
-                    dockAreaOverlayAnimator.cancel();
+                if (mDockAreaOverlayAnimator != null) {
+                    mDockAreaOverlayAnimator.cancel();
                 }
 
                 ArrayList<Animator> animators = new ArrayList<>();
-                if (dockAreaOverlay.getAlpha() != alpha) {
+                if (dockAreaOverlay.getAlpha() != areaAlpha) {
                     if (animateAlpha) {
                         animators.add(ObjectAnimator.ofInt(dockAreaOverlay,
-                                Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), alpha));
+                                Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha));
                     } else {
-                        dockAreaOverlay.setAlpha(alpha);
+                        dockAreaOverlay.setAlpha(areaAlpha);
+                    }
+                }
+                if (mHintTextAlpha != hintAlpha) {
+                    if (animateAlpha) {
+                        animators.add(ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
+                                hintAlpha));
+                    } else {
+                        mHintTextAlpha = hintAlpha;
+                        dockAreaOverlay.invalidateSelf();
                     }
                 }
                 if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
@@ -307,11 +397,11 @@ public class TaskStack {
                     }
                 }
                 if (!animators.isEmpty()) {
-                    dockAreaOverlayAnimator = new AnimatorSet();
-                    dockAreaOverlayAnimator.playTogether(animators);
-                    dockAreaOverlayAnimator.setDuration(duration);
-                    dockAreaOverlayAnimator.setInterpolator(interpolator);
-                    dockAreaOverlayAnimator.start();
+                    mDockAreaOverlayAnimator = new AnimatorSet();
+                    mDockAreaOverlayAnimator.playTogether(animators);
+                    mDockAreaOverlayAnimator.setDuration(duration);
+                    mDockAreaOverlayAnimator.setInterpolator(interpolator);
+                    mDockAreaOverlayAnimator.start();
                 }
             }
         }
@@ -331,17 +421,26 @@ public class TaskStack {
          *                              the initial touch area.  This is also the new dock area to
          *                              draw.
          */
-        DockState(int dockSide, int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
+        DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
+                  @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
                   RectF expandedTouchDockArea) {
             this.dockSide = dockSide;
             this.createMode = createMode;
-            this.viewState = new ViewState(dockAreaAlpha);
+            this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
+                    R.string.recents_drag_hint_message);
             this.dockArea = dockArea;
             this.touchArea = touchArea;
             this.expandedTouchDockArea = expandedTouchDockArea;
         }
 
         /**
+         * Updates the dock state with the given context.
+         */
+        public void update(Context context) {
+            viewState.update(context);
+        }
+
+        /**
          * Returns whether {@param x} and {@param y} are contained in the area scaled to the
          * given {@param width} and {@param height}.
          */
index a72a7e6..235b782 100644 (file)
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Outline;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@ import android.util.TypedValue;
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewOutlineProvider;
 import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -49,6 +51,7 @@ public class TaskCardView extends LinearLayout {
     private ImageView mBadgeView;
     private Task mTask;
     private boolean mDismissState;
+    private int mCornerRadius;
 
     private ViewFocusAnimator mViewFocusAnimator;
     private DismissAnimationsHolder mDismissAnimationsHolder;
@@ -77,6 +80,8 @@ public class TaskCardView extends LinearLayout {
         mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
         mDismissAnimationsHolder = new DismissAnimationsHolder(this);
         View title = findViewById(R.id.card_info_field);
+        mCornerRadius = getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
         mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
         mViewFocusAnimator = new ViewFocusAnimator(this);
     }
@@ -272,13 +277,18 @@ public class TaskCardView extends LinearLayout {
 
     private void setAsScreenShotView(Bitmap screenshot, ImageView screenshotView) {
         LayoutParams lp = (LayoutParams) screenshotView.getLayoutParams();
-        lp.width = getResources()
-                .getDimensionPixelSize(R.dimen.recents_tv_card_width);
-        lp.height = getResources()
-                .getDimensionPixelSize(R.dimen.recents_tv_screenshot_height);
+        lp.width = LayoutParams.MATCH_PARENT;
+        lp.height = LayoutParams.MATCH_PARENT;
 
         screenshotView.setLayoutParams(lp);
         screenshotView.setImageBitmap(screenshot);
+        screenshotView.setClipToOutline(true);
+        screenshotView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+            }
+        });
     }
 
     private void setAsBannerView(Drawable banner, ImageView bannerView) {
index db5413f..04f10ef 100644 (file)
@@ -286,10 +286,9 @@ public class RecentsTransitionHelper {
         // Calculate the offscreen task rect (for tasks that are not backed by views)
         float stackScroll = stackView.getScroller().getStackScroll();
         TaskView taskView = stackView.getChildViewForTask(task);
-        TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm();
-        Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect);
-        offscreenTaskRect.offsetTo(offscreenTaskRect.left,
-                layoutAlgorithm.mStackRect.bottom);
+        TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
+        Rect offscreenTaskRect = new Rect();
+        stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
 
         // If this is a full screen stack, the transition will be towards the single, full screen
         // task. We only need the transition spec for this task.
@@ -302,8 +301,8 @@ public class RecentsTransitionHelper {
             if (taskView == null) {
                 specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
             } else {
-                layoutAlgorithm.getStackTransformScreenCoordinates(task, stackScroll, mTmpTransform,
-                        null);
+                mTmpTransform.fillIn(taskView);
+                stackLayout.transformToScreenCoordinates(mTmpTransform);
                 specs.add(composeAnimationSpec(stackView, taskView, mTmpTransform,
                         true /* addHeaderBitmap */));
             }
@@ -324,8 +323,8 @@ public class RecentsTransitionHelper {
                     //       never happen)
                     specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
                 } else {
-                    layoutAlgorithm.getStackTransformScreenCoordinates(t, stackScroll,
-                            mTmpTransform, null);
+                    mTmpTransform.fillIn(taskView);
+                    stackLayout.transformToScreenCoordinates(mTmpTransform);
                     specs.add(composeAnimationSpec(stackView, tv, mTmpTransform,
                             true /* addHeaderBitmap */));
                 }
index d55c7d8..6ecd52d 100644 (file)
@@ -57,6 +57,7 @@ import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEve
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
 import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
@@ -91,7 +92,7 @@ public class RecentsView extends FrameLayout {
     private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
     private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
 
-    private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 150;
+    private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
     private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
 
     private TaskStack mStack;
@@ -143,7 +144,6 @@ public class RecentsView extends FrameLayout {
                     R.dimen.recents_task_view_rounded_corners_radius);
             mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
                     this, false);
-            mStackActionButton.forceHasOverlappingRendering(false);
             mStackActionButton.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -203,9 +203,11 @@ public class RecentsView extends FrameLayout {
     /**
      * Called from RecentsActivity when the task stack is updated.
      */
-    public void updateStack(TaskStack stack) {
+    public void updateStack(TaskStack stack, boolean setStackViewTasks) {
         mStack = stack;
-        mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
+        if (setStackViewTasks) {
+            mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
+        }
 
         // Update the top level view's visibilities
         if (stack.getTaskCount() > 0) {
@@ -424,10 +426,7 @@ public class RecentsView extends FrameLayout {
 
         ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
         for (int i = visDockStates.size() - 1; i >= 0; i--) {
-            Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
-            if (d.getAlpha() > 0) {
-                d.draw(canvas);
-            }
+            visDockStates.get(i).viewState.draw(canvas);
         }
     }
 
@@ -463,18 +462,29 @@ public class RecentsView extends FrameLayout {
     public final void onBusEvent(DragStartEvent event) {
         updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
                 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+                TaskStack.DockState.NONE.viewState.hintTextAlpha,
                 true /* animateAlpha */, false /* animateBounds */);
+
+        // Temporarily hide the stack action button without changing visibility
+        if (mStackActionButton != null) {
+            mStackActionButton.animate()
+                    .alpha(0f)
+                    .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .start();
+        }
     }
 
     public final void onBusEvent(DragDropTargetChangedEvent event) {
         if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
             updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
                     true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+                    TaskStack.DockState.NONE.viewState.hintTextAlpha,
                     true /* animateAlpha */, true /* animateBounds */);
         } else {
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
             updateVisibleDockRegions(new TaskStack.DockState[] {dockState},
-                    false /* isDefaultDockState */, -1, true /* animateAlpha */,
+                    false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
                     true /* animateBounds */);
         }
         if (mStackActionButton != null) {
@@ -496,13 +506,9 @@ public class RecentsView extends FrameLayout {
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
 
             // Hide the dock region
-            updateVisibleDockRegions(null, false /* isDefaultDockState */, -1,
+            updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
                     false /* animateAlpha */, false /* animateBounds */);
 
-            TaskStackLayoutAlgorithm stackLayout = mTaskStackView.getStackAlgorithm();
-            TaskStackViewScroller stackScroller = mTaskStackView.getScroller();
-            TaskViewTransform tmpTransform = new TaskViewTransform();
-
             // We translated the view but we need to animate it back from the current layout-space
             // rect to its final layout-space rect
             int x = (int) event.taskView.getTranslationX();
@@ -546,9 +552,18 @@ public class RecentsView extends FrameLayout {
                     event.task.getTopComponent().flattenToShortString());
         } else {
             // Animate the overlay alpha back to 0
-            updateVisibleDockRegions(null, true /* isDefaultDockState */, -1,
+            updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
                     true /* animateAlpha */, false /* animateBounds */);
         }
+
+        // Show the stack action button again without changing visibility
+        if (mStackActionButton != null) {
+            mStackActionButton.animate()
+                    .alpha(1f)
+                    .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .start();
+        }
     }
 
     private Rect getTaskRect(TaskView taskView) {
@@ -622,6 +637,10 @@ public class RecentsView extends FrameLayout {
         hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
     }
 
+    public final void onBusEvent(MultiWindowStateChangedEvent event) {
+        updateStack(event.stack, false /* setStackViewTasks */);
+    }
+
     /**
      * Shows the stack action button.
      */
@@ -704,8 +723,8 @@ public class RecentsView extends FrameLayout {
      * Updates the dock region to match the specified dock state.
      */
     private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
-            boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha,
-            boolean animateBounds) {
+            boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
+            boolean animateAlpha, boolean animateBounds) {
         ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
                 new ArraySet<TaskStack.DockState>());
         ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
@@ -714,11 +733,16 @@ public class RecentsView extends FrameLayout {
             TaskStack.DockState.ViewState viewState = dockState.viewState;
             if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
                 // This is no longer visible, so hide it
-                viewState.startAnimation(null, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
+                viewState.startAnimation(null, 0, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
                         Interpolators.ALPHA_OUT, animateAlpha, animateBounds);
             } else {
                 // This state is now visible, update the bounds and show it
-                int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha);
+                int areaAlpha = overrideAreaAlpha != -1
+                        ? overrideAreaAlpha
+                        : viewState.dockAreaAlpha;
+                int hintAlpha = overrideHintAlpha != -1
+                        ? overrideHintAlpha
+                        : viewState.hintTextAlpha;
                 Rect bounds = isDefaultDockState
                         ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight())
                         : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
@@ -727,8 +751,9 @@ public class RecentsView extends FrameLayout {
                     viewState.dockAreaOverlay.setCallback(this);
                     viewState.dockAreaOverlay.setBounds(bounds);
                 }
-                viewState.startAnimation(bounds, alpha, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
-                        Interpolators.ALPHA_IN, animateAlpha, animateBounds);
+                viewState.startAnimation(bounds, areaAlpha, hintAlpha,
+                        DOCK_AREA_OVERLAY_TRANSITION_DURATION, Interpolators.ALPHA_IN,
+                        animateAlpha, animateBounds);
             }
         }
     }
index 70c4dbd..6115aa8 100644 (file)
@@ -171,6 +171,7 @@ public class RecentsViewTouchHandler {
                 TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
                 for (TaskStack.DockState dockState : dockStates) {
                     registerDropTargetForCurrentDrag(dockState);
+                    dockState.update(mRv.getContext());
                     mVisibleDockStates.add(dockState);
                 }
             }
@@ -192,7 +193,9 @@ public class RecentsViewTouchHandler {
     }
 
     public final void onBusEvent(ConfigurationChangedEvent event) {
-        updateSnapAlgorithm();
+        if (event.fromDisplayDensityChange) {
+            updateSnapAlgorithm();
+        }
     }
 
     /**
index dce2353..2f3019c 100644 (file)
@@ -41,7 +41,8 @@ public class SystemBarScrimViews {
 
     private boolean mHasNavBarScrim;
     private boolean mShouldAnimateNavBarScrim;
-
+    private boolean mHasTransposedNavBar;
+    private boolean mHasDockedTasks;
     private int mNavBarScrimEnterDuration;
 
     public SystemBarScrimViews(RecentsActivity activity) {
@@ -50,6 +51,8 @@ public class SystemBarScrimViews {
         mNavBarScrimView.forceHasOverlappingRendering(false);
         mNavBarScrimEnterDuration = activity.getResources().getInteger(
                 R.integer.recents_nav_bar_scrim_enter_duration);
+        mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavBar();
+        mHasDockedTasks = Recents.getSystemServices().hasDockedTask();
     }
 
     /**
@@ -101,8 +104,7 @@ public class SystemBarScrimViews {
      * @return Whether to show the nav bar scrim.
      */
     private boolean isNavBarScrimRequired(boolean hasStackTasks) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        return hasStackTasks && !ssp.hasTransposedNavBar() && !ssp.hasDockedTask();
+        return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
     }
 
     /**** EventBus events ****/
@@ -142,11 +144,15 @@ public class SystemBarScrimViews {
     }
 
     public final void onBusEvent(ConfigurationChangedEvent event) {
+        if (event.fromDeviceOrientationChange) {
+            mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavBar();
+        }
         animateScrimToCurrentNavBarState(event.hasStackTasks);
     }
 
     public final void onBusEvent(MultiWindowStateChangedEvent event) {
-        animateScrimToCurrentNavBarState(event.hasStackTasks);
+        mHasDockedTasks = event.inMultiWindow;
+        animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
     }
 
     /**
index 665d9ad..e79306f 100644 (file)
@@ -485,7 +485,7 @@ public class TaskStackAnimationHelper {
 
         // Get the final set of task transforms
         mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
-                mTmpFinalTaskTransforms);
+                true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
 
         // Focus the task view
         TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
@@ -529,7 +529,7 @@ public class TaskStackAnimationHelper {
             int duration;
             Interpolator interpolator;
             if (willScrollToFront) {
-                duration = Math.max(100, 100 + ((i - 1) * 50));
+                duration = calculateStaggeredAnimDuration(i);
                 interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
             } else {
                 if (i < newFocusTaskViewIndex) {
@@ -553,4 +553,100 @@ public class TaskStackAnimationHelper {
         }
         return willScroll;
     }
+
+    /**
+     * Starts the animation to go to the initial stack layout with a task focused.  In addition, the
+     * previous task will be animated in after the scroll completes.
+     */
+    public void startNewStackScrollAnimation(TaskStack newStack,
+            ReferenceCountedTrigger animationTrigger) {
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+        // Get the current set of task transforms
+        ArrayList<Task> stackTasks = newStack.getStackTasks();
+        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
+
+        // Update the stack
+        mStackView.setTasks(newStack, false /* allowNotifyStackChanges */);
+        mStackView.updateLayoutAlgorithm(false /* boundScroll */);
+
+        // Pick up the newly visible views after the scroll
+        final float newScroll = stackLayout.mInitialScrollP;
+        mStackView.bindVisibleTaskViews(newScroll);
+
+        // Update the internal state
+        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+        stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */);
+        stackScroller.setStackScroll(newScroll);
+        mStackView.cancelDeferredTaskViewLayoutAnimation();
+
+        // Get the final set of task transforms
+        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
+                false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
+
+        // Hide the front most task view until the scroll is complete
+        Task frontMostTask = newStack.getStackFrontMostTask(false /* includeFreeform */);
+        final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
+        final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
+                stackTasks.indexOf(frontMostTask));
+        if (frontMostTaskView != null) {
+            mStackView.updateTaskViewToTransform(frontMostTaskView,
+                    stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE);
+        }
+
+        // Setup the end listener to return all the hidden views to the view pool after the
+        // focus animation
+        animationTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                mStackView.bindVisibleTaskViews(newScroll);
+
+                // Now, animate in the front-most task
+                if (frontMostTaskView != null) {
+                    mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform,
+                            new AnimationProps(75, 200, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR));
+                }
+            }
+        });
+
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (mStackView.isIgnoredTask(task)) {
+                continue;
+            }
+            if (task == frontMostTask && frontMostTaskView != null) {
+                continue;
+            }
+
+            int taskIndex = stackTasks.indexOf(task);
+            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
+            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
+
+            // Update the task to the initial state (for the newly picked up tasks)
+            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
+
+            int duration = calculateStaggeredAnimDuration(i);
+            Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+
+            AnimationProps anim = new AnimationProps()
+                    .setDuration(AnimationProps.BOUNDS, duration)
+                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
+                    .setListener(animationTrigger.decrementOnAnimationEnd());
+            animationTrigger.increment();
+            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
+        }
+    }
+
+    /**
+     * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and
+     * {@link #startNewStackScrollAnimation}.
+     */
+    private int calculateStaggeredAnimDuration(int i) {
+        return Math.max(100, 100 + ((i - 1) * 50));
+    }
 }
index 34d6bce..774e4e9 100644 (file)
@@ -345,11 +345,11 @@ public class TaskStackLayoutAlgorithm {
         mCb = cb;
         mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
         mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
-        mBaseTopMargin = getDimensionForDevice(res,
+        mBaseTopMargin = getDimensionForDevice(context,
                 R.dimen.recents_layout_top_margin_phone,
                 R.dimen.recents_layout_top_margin_tablet,
                 R.dimen.recents_layout_top_margin_tablet_xlarge);
-        mBaseSideMargin = getDimensionForDevice(res,
+        mBaseSideMargin = getDimensionForDevice(context,
                 R.dimen.recents_layout_side_margin_phone,
                 R.dimen.recents_layout_side_margin_tablet,
                 R.dimen.recents_layout_side_margin_tablet_xlarge);
@@ -375,14 +375,14 @@ public class TaskStackLayoutAlgorithm {
                 res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
         mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
         mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
-        mBaseInitialTopOffset = getDimensionForDevice(res,
+        mBaseInitialTopOffset = getDimensionForDevice(context,
                 R.dimen.recents_layout_initial_top_offset_phone_port,
                 R.dimen.recents_layout_initial_top_offset_phone_land,
                 R.dimen.recents_layout_initial_top_offset_tablet,
                 R.dimen.recents_layout_initial_top_offset_tablet,
                 R.dimen.recents_layout_initial_top_offset_tablet,
                 R.dimen.recents_layout_initial_top_offset_tablet);
-        mBaseInitialBottomOffset = getDimensionForDevice(res,
+        mBaseInitialBottomOffset = getDimensionForDevice(context,
                 R.dimen.recents_layout_initial_bottom_offset_phone_port,
                 R.dimen.recents_layout_initial_bottom_offset_phone_land,
                 R.dimen.recents_layout_initial_bottom_offset_tablet,
@@ -454,11 +454,8 @@ public class TaskStackLayoutAlgorithm {
         mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
                 mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
 
-        // Anchor the task rect top aligned to the non-freeform stack rect
-        float aspect = (float) (windowRect.width() - (mSystemInsets.left + mSystemInsets.right)) /
-                (windowRect.height() - (mSystemInsets.top + mSystemInsets.bottom));
-        int minHeight = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
-        int height = (int) Math.min(mStackRect.width() / aspect, minHeight);
+        // Anchor the task rect top aligned to the stack rect
+        int height = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
         mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
 
         // Short circuit here if the stack rects haven't changed so we don't do all the work below
@@ -577,7 +574,7 @@ public class TaskStackLayoutAlgorithm {
     /**
      * Creates task overrides to ensure the initial stack layout if necessary.
      */
-    public void setTaskOverridesForInitialState(TaskStack stack) {
+    public void setTaskOverridesForInitialState(TaskStack stack, boolean ignoreScrollToFront) {
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
 
         mTaskIndexOverrideMap.clear();
@@ -585,7 +582,7 @@ public class TaskStackLayoutAlgorithm {
         boolean scrollToFront = launchState.launchedFromHome ||
                 launchState.launchedViaDockGesture;
         if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
-            if (!launchState.launchedWithAltTab && !scrollToFront) {
+            if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
                 // Set the initial scroll to the predefined state (which differs from the stack)
                 float [] initialNormX = new float[] {
                         getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
@@ -834,12 +831,19 @@ public class TaskStackLayoutAlgorithm {
      */
     public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
             TaskViewTransform transformOut, TaskViewTransform frontTransform) {
-        Rect windowRect = Recents.getSystemServices().getWindowRect();
         TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
                 transformOut, frontTransform, true /* forceUpdate */,
                 false /* ignoreTaskOverrides */);
-        transform.rect.offset(windowRect.left, windowRect.top);
-        return transform;
+        return transformToScreenCoordinates(transform);
+    }
+
+    /**
+     * Transforms the given {@param transformOut} to the screen coordinates.
+     */
+    public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut) {
+        Rect windowRect = Recents.getSystemServices().getWindowRect();
+        transformOut.rect.offset(windowRect.left, windowRect.top);
+        return transformOut;
     }
 
     /**
@@ -938,7 +942,11 @@ public class TaskStackLayoutAlgorithm {
      * stack.
      */
     float getStackScrollForTask(Task t) {
-        return mTaskIndexOverrideMap.get(t.key.id, (float) mTaskIndexMap.get(t.key.id, 0));
+        Float overrideP = mTaskIndexOverrideMap.get(t.key.id, null);
+        if (overrideP == null) {
+            return (float) mTaskIndexMap.get(t.key.id, 0);
+        }
+        return overrideP;
     }
 
     /**
@@ -997,7 +1005,8 @@ public class TaskStackLayoutAlgorithm {
         int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
                 WIDTH);
         int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
-        if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+        if (Utilities.getAppConfiguration(mContext).orientation
+                == Configuration.ORIENTATION_LANDSCAPE) {
             // If we are in landscape, calculate the width of the stack in portrait and ensure that
             // we are not larger than that size
             Rect portraitDisplayRect = new Rect(0, 0,
@@ -1014,20 +1023,21 @@ public class TaskStackLayoutAlgorithm {
     /**
      * Retrieves resources that are constant regardless of the current configuration of the device.
      */
-    public static int getDimensionForDevice(Resources res, int phoneResId,
+    public static int getDimensionForDevice(Context ctx, int phoneResId,
             int tabletResId, int xlargeTabletResId) {
-        return getDimensionForDevice(res, phoneResId, phoneResId, tabletResId, tabletResId,
+        return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
                 xlargeTabletResId, xlargeTabletResId);
     }
 
     /**
      * Retrieves resources that are constant regardless of the current configuration of the device.
      */
-    public static int getDimensionForDevice(Resources res, int phonePortResId, int phoneLandResId,
+    public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
             int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
             int xlargeTabletLandResId) {
         RecentsConfiguration config = Recents.getConfiguration();
-        boolean isLandscape = Recents.getSystemServices().getDisplayOrientation() ==
+        Resources res = ctx.getResources();
+        boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
                 Configuration.ORIENTATION_LANDSCAPE;
         if (config.isXLargeScreen) {
             return res.getDimensionPixelSize(isLandscape
index a75d1e1..6176d99 100644 (file)
@@ -328,6 +328,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
      */
     public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
         boolean isInitialized = mLayoutAlgorithm.isInitialized();
+
         // Only notify if we are already initialized, otherwise, everything will pick up all the
         // new and old tasks when we next layout
         mStack.setTasks(getContext(), stack.computeAllTasksList(),
@@ -344,7 +345,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
      */
     public void updateToInitialState() {
         mStackScroller.setStackScrollToInitialState();
-        mLayoutAlgorithm.setTaskOverridesForInitialState(mStack);
+        mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
     }
 
     /** Updates the list of task views */
@@ -508,11 +509,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
      * Binds the visible {@link TaskView}s at the given target scroll.
      */
     void bindVisibleTaskViews(float targetStackScroll) {
-        bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */);
-    }
-
-    void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
-        bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides);
+        bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
     }
 
     /**
@@ -525,17 +522,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
      * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
      *                          includes those visible at the current stack scroll, and all at the
      *                          target stack scroll.
-     * @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible
-     *                       {@link TaskView}s
      * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
      *                            tasks at their non-overridden task progress
      */
-    void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet,
-            boolean ignoreTaskOverrides) {
+    void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
         // Get all the task transforms
         ArrayList<Task> tasks = mStack.getStackTasks();
         int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
-                mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet,
+                mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
                 ignoreTaskOverrides);
 
         // Return all the invisible children to the pool
@@ -548,7 +542,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
             Task task = tv.getTask();
 
             // Skip ignored tasks
-            if (ignoreTasksSet.contains(task.key)) {
+            if (mIgnoreTasks.contains(task.key)) {
                 continue;
             }
 
@@ -578,7 +572,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
             TaskViewTransform transform = mCurrentTaskTransforms.get(i);
 
             // Skip ignored tasks
-            if (ignoreTasksSet.contains(task.key)) {
+            if (mIgnoreTasks.contains(task.key)) {
                 continue;
             }
 
@@ -626,10 +620,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
     }
 
     /**
-     * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
+     * @see #relayoutTaskViews(AnimationProps, boolean)
      */
     public void relayoutTaskViews(AnimationProps animation) {
-        relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */);
+        relayoutTaskViews(animation, false /* ignoreTaskOverrides */);
     }
 
     /**
@@ -637,16 +631,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
      * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
      * animations that are current running on those task views, and will ensure that the children
      * {@link TaskView}s will match the set of visible tasks in the stack.
-     *
-     * @param ignoreTasksSet the set of tasks to ignore in the relayout
      */
-    private void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
-            boolean ignoreTaskOverrides) {
+    private void relayoutTaskViews(AnimationProps animation, boolean ignoreTaskOverrides) {
         // If we had a deferred animation, cancel that
         mDeferredTaskViewLayoutAnimation = null;
 
         // Synchronize the current set of TaskViews
-        bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
+        bindVisibleTaskViews(mStackScroller.getStackScroll(),
                 ignoreTaskOverrides /* ignoreTaskOverrides */);
 
         // Animate them to their final transforms with the given animation
@@ -657,7 +648,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
             int taskIndex = mStack.indexOfStackTask(tv.getTask());
             TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
 
-            if (ignoreTasksSet.contains(tv.getTask().key)) {
+            if (mIgnoreTasks.contains(tv.getTask().key)) {
                 continue;
             }
 
@@ -715,13 +706,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
      * {@param stackScroll} and {@param focusState}.
      */
     public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
-            ArrayList<TaskViewTransform> transformsOut) {
+            boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
         Utilities.matchTaskListSize(tasks, transformsOut);
         for (int i = tasks.size() - 1; i >= 0; i--) {
             Task task = tasks.get(i);
             TaskViewTransform transform = transformsOut.get(i);
             mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
-                    true /* forceUpdate */, true /* ignoreTaskOverrides */);
+                    true /* forceUpdate */, ignoreTaskOverrides);
             transform.visible = true;
         }
     }
@@ -812,22 +803,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
 
     /**
      * Updates the layout algorithm min and max virtual scroll bounds.
-     *
-     * @see #updateLayoutAlgorithm(boolean, ArraySet<Task.TaskKey>)
      */
    public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
-        updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks);
-    }
-
-    /**
-     * Updates the min and max virtual scroll bounds.
-     *
-     * @param ignoreTasksSet the set of tasks to ignore in the relayout
-     */
-    private void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
-            ArraySet<Task.TaskKey> ignoreTasksSet) {
         // Compute the min and max scroll values
-        mLayoutAlgorithm.update(mStack, ignoreTasksSet);
+        mLayoutAlgorithm.update(mStack, mIgnoreTasks);
 
         // Update the freeform workspace background
         SystemServicesProxy ssp = Recents.getSystemServices();
@@ -1195,8 +1174,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
         }
 
         // Rebind all the views, including the ignore ones
-        bindVisibleTaskViews(mStackScroller.getStackScroll(), mIgnoreTasks,
-                false /* ignoreTaskOverrides */);
+        bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
 
         // Measure each of the TaskViews
         mTmpTaskViews.clear();
@@ -1553,7 +1531,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
         tv.onTaskBound(task);
 
         // Load the task data
-        Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
+        Recents.getTaskLoader().loadTaskData(task);
     }
 
     private void unbindTaskView(TaskView tv, Task task) {
@@ -1640,7 +1618,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
         }
         if (launchTaskIndex != -1) {
             // Stop all animations
-            mUIDozeTrigger.stopDozing();
             cancelAllTaskViewAnimations();
 
             final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
@@ -1834,7 +1811,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
             updateLayoutAlgorithm(true /* boundScroll */);
             addIgnoreTask(event.task);
         }
-        relayoutTaskViews(animation, mIgnoreTasks, ignoreTaskOverrides);
+        relayoutTaskViews(animation, ignoreTaskOverrides);
     }
 
     public final void onBusEvent(final DragEndEvent event) {
@@ -1948,26 +1925,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
         }
     }
 
-    public final void onBusEvent(MultiWindowStateChangedEvent event) {
-        if (!event.inMultiWindow) {
+    public final void onBusEvent(final MultiWindowStateChangedEvent event) {
+        if (event.inMultiWindow) {
+            setTasks(event.stack, true /* allowNotifyStackChanges */);
+        } else {
+            // Reset the launch state before handling the multiwindow change
+            RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+            launchState.reset();
+
             // Defer until the next frame to ensure that we have received all the system insets, and
             // initial layout updates
+            event.getAnimationTrigger().increment();
             post(new Runnable() {
                 @Override
                 public void run() {
                     // Scroll the stack to the front to see the undocked task
-                    mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
-                        @Override
-                        public void run() {
-                            List<TaskView> taskViews = getTaskViews();
-                            int taskViewCount = taskViews.size();
-                            for (int i = 0; i < taskViewCount; i++) {
-                                TaskView tv = taskViews.get(i);
-                                tv.getHeaderView().rebindToTask(tv.getTask(),
-                                        tv.mTouchExplorationEnabled, tv.mIsDisabledInSafeMode);
-                            }
-                        }
-                    });
+                    mAnimationHelper.startNewStackScrollAnimation(event.stack,
+                            event.getAnimationTrigger());
+                    event.getAnimationTrigger().decrement();
                 }
             });
         }
index 3cdb1fb..9edf9d6 100644 (file)
@@ -60,8 +60,7 @@ import java.util.List;
 class TaskStackViewTouchHandler implements SwipeHelper.Callback {
 
     private static final int INACTIVE_POINTER_ID = -1;
-    private static final Interpolator STACK_TRANSFORM_INTERPOLATOR =
-            new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f);
+    private static final float CHALLENGING_SWIPE_ESCAPE_VELOCITY = 800f; // dp/sec
     // The min overscroll is the amount of task progress overscroll we want / the max overscroll
     // curve value below
     private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
@@ -125,7 +124,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
             @Override
             protected float getSize(View v) {
-                return mSv.getWidth();
+                return getScaledDismissSize();
             }
 
             @Override
@@ -138,6 +137,16 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
                 anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
                 mSwipeHelperAnimations.put(v, anim);
             }
+
+            @Override
+            protected float getUnscaledEscapeVelocity() {
+                return CHALLENGING_SWIPE_ESCAPE_VELOCITY;
+            }
+
+            @Override
+            protected long getMaxEscapeAnimDuration() {
+                return 700;
+            }
         };
         mSwipeHelper.setDisableHardwareLayers(true);
     }
@@ -483,7 +492,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
 
             // Get the final set of task transforms (with task removed)
             mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
-                    mCurrentTasks, mFinalTaskTransforms);
+                    mCurrentTasks, true /* ignoreTaskOverrides */, mFinalTaskTransforms);
 
             // Set the target to scroll towards upon dismissal
             mTargetStackScroll = newStackScroll;
@@ -500,7 +509,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
 
     @Override
     public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
-        updateTaskViewTransforms(getDismissFraction(v));
+        updateTaskViewTransforms(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(swipeProgress));
         return true;
     }
 
@@ -616,13 +625,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
     }
 
     /**
-     * Returns the fraction which we should interpolate the other task views based on the dismissal
-     * of this given task.
-     *
-     * TODO: We can interpolate this to adjust when the other tasks should respond to the dismissal
+     * Returns the scaled size used to calculate the dismiss fraction.
      */
-    private float getDismissFraction(View v) {
-        float fraction = Math.min(1f, Math.abs(v.getTranslationX() / mSv.getWidth()));
-        return STACK_TRANSFORM_INTERPOLATOR.getInterpolation(fraction);
+    private float getScaledDismissSize() {
+        return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight());
     }
 }
index 37f5a9f..7ea70b5 100644 (file)
@@ -24,13 +24,9 @@ import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Outline;
-import android.graphics.Paint;
 import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -607,6 +603,8 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
         mTask = t;
         mTask.addCallback(this);
         mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
+        mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode);
+        mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
 
         if (!t.isDockable && ssp.hasDockedTask()) {
             if (mIncompatibleAppToastView == null) {
@@ -623,15 +621,15 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
 
     @Override
     public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
-        // Bind each of the views to the new task data
-        mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode);
-        mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+        // Update each of the views to the new task data
+        mThumbnailView.onTaskDataLoaded(thumbnailInfo);
+        mHeaderView.onTaskDataLoaded();
         mTaskDataLoaded = true;
     }
 
     @Override
     public void onTaskDataUnloaded() {
-        // Unbind each of the views from the task data and remove the task callback
+        // Unbind each of the views from the task and remove the task callback
         mTask.removeCallback(this);
         mThumbnailView.unbindFromTask();
         mHeaderView.unbindFromTask(mTouchExplorationEnabled);
@@ -640,7 +638,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
 
     @Override
     public void onTaskStackIdChanged() {
-        mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+        // Force rebind the header, the thumbnail does not change due to stack changes
+        mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+        mHeaderView.onTaskDataLoaded();
     }
 
     /**** View.OnClickListener Implementation ****/
index fb0fc30..e2f2198 100644 (file)
@@ -284,14 +284,14 @@ public class TaskViewHeader extends FrameLayout
         // Update the dimensions of everything in the header. We do this because we need to use
         // resources for the display, and not the current configuration.
         Resources res = getResources();
-        int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+        int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land);
-        int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+        int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding,
@@ -438,21 +438,18 @@ public class TaskViewHeader extends FrameLayout
         }
     }
 
-    /** Binds the bar view to the task */
-    public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
+    /**
+     * Binds the bar view to the task.
+     */
+    public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
         mTask = t;
 
-        // If an activity icon is defined, then we use that as the primary icon to show in the bar,
-        // otherwise, we fall back to the application icon
         int primaryColor = disabledInSafeMode
                 ? mDisabledTaskBarBackgroundColor
                 : t.colorPrimary;
         if (mBackground.getColor() != primaryColor) {
             updateBackgroundColor(primaryColor, mDimAlpha);
         }
-        if (t.icon != null) {
-            mIconView.setImageDrawable(t.icon);
-        }
         if (!mTitleView.getText().toString().equals(t.title)) {
             mTitleView.setText(t.title);
         }
@@ -497,6 +494,16 @@ public class TaskViewHeader extends FrameLayout
         }
     }
 
+    /**
+     * Called when the bound task's data has loaded and this view should update to reflect the
+     * changes.
+     */
+    public void onTaskDataLoaded() {
+        if (mTask.icon != null) {
+            mIconView.setImageDrawable(mTask.icon);
+        }
+    }
+
     /** Unbinds the bar view from the task */
     void unbindFromTask(boolean touchExplorationEnabled) {
         mTask = null;
index 4de7713..109dc20 100644 (file)
@@ -38,6 +38,7 @@ import android.view.ViewDebug;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 
 
@@ -132,7 +133,7 @@ public class TaskViewThumbnail extends View {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
         SystemServicesProxy ssp = Recents.getSystemServices();
-        mOrientation = ssp.getDisplayOrientation();
+        mOrientation = Utilities.getAppConfiguration(mContext).orientation;
         mDisplayRect = ssp.getDisplayRect();
     }
 
@@ -302,21 +303,29 @@ public class TaskViewThumbnail extends View {
         updateThumbnailPaintFilter();
     }
 
-    /** Binds the thumbnail view to the task */
-    void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo,
-            boolean disabledInSafeMode) {
+    /**
+     * Binds the thumbnail view to the task.
+     */
+    void bindToTask(Task t, boolean disabledInSafeMode) {
         mTask = t;
         mDisabledInSafeMode = disabledInSafeMode;
-        if (t.thumbnail != null) {
-            setThumbnail(t.thumbnail, thumbnailInfo);
-        } else {
-            setThumbnail(null, null);
-        }
         if (t.colorBackground != 0) {
             mBgFillPaint.setColor(t.colorBackground);
         }
     }
 
+    /**
+     * Called when the bound task's data has loaded and this view should update to reflect the
+     * changes.
+     */
+    void onTaskDataLoaded(ActivityManager.TaskThumbnailInfo thumbnailInfo) {
+        if (mTask.thumbnail != null) {
+            setThumbnail(mTask.thumbnail, thumbnailInfo);
+        } else {
+            setThumbnail(null, null);
+        }
+    }
+
     /** Unbinds the thumbnail view from the task */
     void unbindFromTask() {
         mTask = null;
index 7c6c641..cb1128b 100644 (file)
@@ -910,7 +910,7 @@ public abstract class BaseStatusBar extends SystemUI implements
         final float fontScale = newConfig.fontScale;
         final int density = newConfig.densityDpi;
         if (density != mDensity || mFontScale != fontScale) {
-            reInflateViews();
+            onDensityOrFontScaleChanged();
             mDensity = density;
             mFontScale = fontScale;
         }
@@ -926,7 +926,7 @@ public abstract class BaseStatusBar extends SystemUI implements
         }
     }
 
-    protected void reInflateViews() {
+    protected void onDensityOrFontScaleChanged() {
         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
         for (int i = 0; i < activeNotifications.size(); i++) {
             Entry entry = activeNotifications.get(i);
index e25f9de..6e9de23 100644 (file)
@@ -41,6 +41,7 @@ import android.widget.Chronometer;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
+import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
@@ -103,6 +104,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     private int mMaxExpandHeight;
     private int mHeadsUpHeight;
     private View mVetoButton;
+    private int mNotificationColor;
     private boolean mClearable;
     private ExpansionLogger mLogger;
     private String mLoggingKey;
@@ -226,6 +228,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         mPrivateLayout.onNotificationUpdated(entry);
         mPublicLayout.onNotificationUpdated(entry);
         mShowingPublicInitialized = false;
+        updateNotificationColor();
         updateClearability();
         if (mIsSummaryWithChildren) {
             recreateNotificationHeader();
@@ -603,11 +606,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     }
 
     public int getNotificationColor() {
-        int color = getStatusBarNotification().getNotification().color;
-        if (color == Notification.COLOR_DEFAULT) {
-            return mContext.getColor(com.android.internal.R.color.notification_icon_default_color);
-        }
-        return color;
+        return mNotificationColor;
+    }
+
+    private void updateNotificationColor() {
+        mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
+                getStatusBarNotification().getNotification().color);
     }
 
     public HybridNotificationView getSingleLineView() {
index e4accf5..870447a 100644 (file)
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
 
 import android.app.Notification;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -39,6 +40,7 @@ import java.text.NumberFormat;
 
 public class StatusBarIconView extends AnimatedImageView {
     private static final String TAG = "StatusBarIconView";
+    private boolean mAlwaysScaleIcon;
 
     private StatusBarIcon mIcon;
     @ViewDebug.ExportedProperty private String mSlot;
@@ -49,6 +51,7 @@ public class StatusBarIconView extends AnimatedImageView {
     private String mNumberText;
     private Notification mNotification;
     private final boolean mBlocked;
+    private int mDensity;
 
     public StatusBarIconView(Context context, String slot, Notification notification) {
         this(context, slot, notification, false);
@@ -57,7 +60,6 @@ public class StatusBarIconView extends AnimatedImageView {
     public StatusBarIconView(Context context, String slot, Notification notification,
             boolean blocked) {
         super(context);
-        final Resources res = context.getResources();
         mBlocked = blocked;
         mSlot = slot;
         mNumberPain = new Paint();
@@ -65,18 +67,37 @@ public class StatusBarIconView extends AnimatedImageView {
         mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
         mNumberPain.setAntiAlias(true);
         setNotification(notification);
+        maybeUpdateIconScale();
+        setScaleType(ScaleType.CENTER);
+        mDensity = context.getResources().getDisplayMetrics().densityDpi;
+    }
 
+    private void maybeUpdateIconScale() {
         // We do not resize and scale system icons (on the right), only notification icons (on the
         // left).
-        if (notification != null) {
-            final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
-            final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
-            final float scale = (float)imageBounds / (float)outerBounds;
-            setScaleX(scale);
-            setScaleY(scale);
+        if (mNotification != null || mAlwaysScaleIcon) {
+            updateIconScale();
         }
+    }
 
-        setScaleType(ScaleType.CENTER);
+    private void updateIconScale() {
+        Resources res = mContext.getResources();
+        final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
+        final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
+        final float scale = (float)imageBounds / (float)outerBounds;
+        setScaleX(scale);
+        setScaleY(scale);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        int density = newConfig.densityDpi;
+        if (density != mDensity) {
+            mDensity = density;
+            maybeUpdateIconScale();
+            updateDrawable();
+        }
     }
 
     public void setNotification(Notification notification) {
@@ -87,12 +108,9 @@ public class StatusBarIconView extends AnimatedImageView {
     public StatusBarIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mBlocked = false;
-        final Resources res = context.getResources();
-        final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
-        final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
-        final float scale = (float)imageBounds / (float)outerBounds;
-        setScaleX(scale);
-        setScaleY(scale);
+        mAlwaysScaleIcon = true;
+        updateIconScale();
+        mDensity = context.getResources().getDisplayMetrics().densityDpi;
     }
 
     private static boolean streq(String a, String b) {
index 225751a..f86badb 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.view.View;
@@ -28,21 +29,29 @@ public class IconMerger extends LinearLayout {
     private static final String TAG = "IconMerger";
     private static final boolean DEBUG = false;
 
-    private final int mIconSize;
-    private final int mIconHPadding;
+    private int mIconSize;
+    private int mIconHPadding;
 
     private View mMoreView;
 
     public IconMerger(Context context, AttributeSet attrs) {
         super(context, attrs);
+        reloadDimens();
+        if (DEBUG) {
+            setBackgroundColor(0x800099FF);
+        }
+    }
 
-        Resources res = context.getResources();
+    private void reloadDimens() {
+        Resources res = mContext.getResources();
         mIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
         mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+    }
 
-        if (DEBUG) {
-            setBackgroundColor(0x800099FF);
-        }
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        reloadDimens();
     }
 
     public void setOverflowIndicator(View v) {
index c4917a1..cbaab14 100644 (file)
@@ -5,6 +5,7 @@ import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.support.annotation.NonNull;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
@@ -17,7 +18,6 @@ import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A controller for the space in the status bar to the left of the system icons. This area is
@@ -51,9 +51,7 @@ public class NotificationIconAreaController {
      * Initializes the views that will represent the notification area.
      */
     protected void initializeNotificationAreaViews(Context context) {
-        Resources res = context.getResources();
-        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
-        mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+        reloadDimens(context);
 
         LayoutInflater layoutInflater = LayoutInflater.from(context);
         mNotificationIconArea = inflateIconArea(layoutInflater);
@@ -68,6 +66,27 @@ public class NotificationIconAreaController {
         }
     }
 
+    public void onDensityOrFontScaleChanged(Context context) {
+        reloadDimens(context);
+        final LinearLayout.LayoutParams params = generateIconLayoutParams();
+        for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
+            View child = mNotificationIcons.getChildAt(i);
+            child.setLayoutParams(params);
+        }
+    }
+
+    @NonNull
+    private LinearLayout.LayoutParams generateIconLayoutParams() {
+        return new LinearLayout.LayoutParams(
+                mIconSize + 2 * mIconHPadding, getHeight());
+    }
+
+    private void reloadDimens(Context context) {
+        Resources res = context.getResources();
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+        mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+    }
+
     /**
      * Returns the view that represents the notification area.
      */
@@ -125,8 +144,7 @@ public class NotificationIconAreaController {
      * Updates the notifications with the given list of notifications to display.
      */
     public void updateNotificationIcons(NotificationData notificationData) {
-        final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
-                mIconSize + 2 * mIconHPadding, getHeight());
+        final LinearLayout.LayoutParams params = generateIconLayoutParams();
 
         ArrayList<NotificationData.Entry> activeNotifications =
                 notificationData.getActiveNotifications();
index 82806fd..b75c291 100644 (file)
@@ -86,6 +86,7 @@ import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
 import android.view.ViewStub;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -762,14 +763,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         mStackScroller.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setOnGroupChangeListener(mStackScroller);
 
-        mKeyguardIconOverflowContainer =
-                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
-                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
-        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
-        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
-        mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
-
-
+        inflateOverflowContainer();
         inflateEmptyShadeView();
         inflateDismissView();
         mExpandedContents = mStackScroller;
@@ -950,13 +944,62 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         return new BatteryControllerImpl(mContext);
     }
 
+    private void inflateOverflowContainer() {
+        mKeyguardIconOverflowContainer =
+                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
+                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
+        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
+        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
+        mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
+    }
+
     @Override
-    protected void reInflateViews() {
-        super.reInflateViews();
+    protected void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        mScrimController.onDensityOrFontScaleChanged();
+        mStatusBarView.onDensityOrFontScaleChanged();
+        mBrightnessMirrorController.onDensityOrFontScaleChanged();
+        inflateSignalClusters();
+        mIconController.onDensityOrFontScaleChanged();
         inflateDismissView();
         updateClearAll();
         inflateEmptyShadeView();
         updateEmptyShadeView();
+        inflateOverflowContainer();
+    }
+
+    private void inflateSignalClusters() {
+        SignalClusterView signalClusterView = reinflateSignalCluster(mStatusBarView);
+        mIconController.setSignalCluster(signalClusterView);
+        reinflateSignalCluster(mKeyguardStatusView);
+    }
+
+    private SignalClusterView reinflateSignalCluster(View view) {
+        SignalClusterView signalCluster =
+                (SignalClusterView) view.findViewById(R.id.signal_cluster);
+        if (signalCluster != null) {
+            ViewParent parent = signalCluster.getParent();
+            if (parent instanceof ViewGroup) {
+                ViewGroup viewParent = (ViewGroup) parent;
+                int index = viewParent.indexOfChild(signalCluster);
+                viewParent.removeView(signalCluster);
+                SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(mContext)
+                        .inflate(R.layout.signal_cluster_view, viewParent, false);
+                ViewGroup.MarginLayoutParams layoutParams =
+                        (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams();
+                layoutParams.setMarginsRelative(
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.signal_cluster_margin_start),
+                        0, 0, 0);
+                newCluster.setLayoutParams(layoutParams);
+                newCluster.setSecurityController(mSecurityController);
+                newCluster.setNetworkController(mNetworkController);
+                viewParent.addView(newCluster, index);
+                return newCluster;
+            }
+            return signalCluster;
+        }
+        return null;
     }
 
     private void inflateEmptyShadeView() {
@@ -3250,17 +3293,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
     // SystemUIService notifies SystemBars of configuration changes, which then calls down here
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
+        updateResources();
+        updateDisplaySize(); // populates mDisplayMetrics
         super.onConfigurationChanged(newConfig); // calls refreshLayout
 
         if (DEBUG) {
             Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
         }
-        updateDisplaySize(); // populates mDisplayMetrics
 
-        updateResources();
         repositionNavigationBar();
         updateRowStates();
-        mIconController.updateResources();
+        mIconController.defineSlots();
         mScreenPinningRequest.onConfigurationChanged();
         mNetworkController.onConfigurationChanged();
     }
@@ -3331,7 +3374,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         mMaxAllowedKeyguardNotifications = res.getInteger(
                 R.integer.keyguard_max_notification_count);
 
-        if (DEBUG) Log.v(TAG, "updateResources");
+        if (DEBUG) Log.v(TAG, "defineSlots");
     }
 
     // Visibility reporting
index cc3b4bd..c80b3ad 100644 (file)
@@ -21,10 +21,12 @@ import android.util.AttributeSet;
 import android.util.EventLog;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.systemui.DejankUtils;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.R;
 
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
@@ -185,4 +187,11 @@ public class PhoneStatusBarView extends PanelBar {
         float scrimFraction = Math.max(mPanelFraction, mMinFraction);
         mScrimController.setPanelExpansion(scrimFraction);
     }
+
+    public void onDensityOrFontScaleChanged() {
+        ViewGroup.LayoutParams layoutParams = getLayoutParams();
+        layoutParams.height = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_height);
+        setLayoutParams(layoutParams);
+    }
 }
index 3eda320..cf5277f 100644 (file)
@@ -24,6 +24,7 @@ import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -519,4 +520,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
         mScrimBehind.setChangeRunnable(changeRunnable);
     }
+
+    public void onDensityOrFontScaleChanged() {
+        ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
+        layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
+                R.dimen.heads_up_scrim_height);
+        mHeadsUpScrim.setLayoutParams(layoutParams);
+    }
 }
index a40aa83..f415ae5 100644 (file)
@@ -76,6 +76,7 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
     private View mNotificationIconAreaInner;
 
     private BatteryMeterView mBatteryMeterView;
+    private BatteryMeterView mBatteryMeterViewKeyguard;
     private TextView mClock;
 
     private int mIconSize;
@@ -129,39 +130,43 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
         mStatusIconsKeyguard = (LinearLayout) keyguardStatusBar.findViewById(R.id.statusIcons);
 
         mBatteryMeterView = (BatteryMeterView) statusBar.findViewById(R.id.battery);
-        maybeScaleBatteryMeterView(context);
+        mBatteryMeterViewKeyguard = (BatteryMeterView) keyguardStatusBar.findViewById(R.id.battery);
+        scaleBatteryMeterViews(context);
 
         mClock = (TextView) statusBar.findViewById(R.id.clock);
         mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
         mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
         mHandler = new Handler();
-        updateResources();
+        defineSlots();
+        loadDimens();
 
         TunerService.get(mContext).addTunable(this, ICON_BLACKLIST);
     }
 
+    public void setSignalCluster(SignalClusterView signalCluster) {
+        mSignalCluster = signalCluster;
+    }
+
     /**
-     * Looks up the scale factor for status bar icons and scales the battery view by that amount
-     * if appropriate.
+     * Looks up the scale factor for status bar icons and scales the battery view by that amount.
      */
-    private void maybeScaleBatteryMeterView(Context context) {
+    private void scaleBatteryMeterViews(Context context) {
         Resources res = context.getResources();
         TypedValue typedValue = new TypedValue();
 
         res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
         float iconScaleFactor = typedValue.getFloat();
 
-        if (iconScaleFactor == 1.f) {
-            return;
-        }
-
-        float batteryHeight = res.getDimension(R.dimen.status_bar_battery_icon_height);
-        float batteryWidth = res.getDimension(R.dimen.status_bar_battery_icon_width);
+        int batteryHeight = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_height);
+        int batteryWidth = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_width);
+        int marginBottom = res.getDimensionPixelSize(R.dimen.battery_margin_bottom);
 
         LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams(
                 (int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor));
+        scaledLayoutParams.setMarginsRelative(0, 0, 0, marginBottom);
 
         mBatteryMeterView.setLayoutParams(scaledLayoutParams);
+        mBatteryMeterViewKeyguard.setLayoutParams(scaledLayoutParams);
     }
 
     @Override
@@ -185,15 +190,16 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
             setIcon(views.get(i).getSlot(), views.get(i).getStatusBarIcon());
         }
     }
-
-    public void updateResources() {
+    private void loadDimens() {
         mIconSize = mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_icon_size);
         mIconHPadding = mContext.getResources().getDimensionPixelSize(
                 R.dimen.status_bar_icon_padding);
+    }
+
+    public void defineSlots() {
         defineSlots(mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_statusBarIcons));
-        FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size);
     }
 
     private void addSystemIcon(int index, StatusBarIcon icon) {
@@ -571,4 +577,35 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
         }
         return ret;
     }
+
+    public void onDensityOrFontScaleChanged() {
+        loadDimens();
+        mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+        updateClock();
+        for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
+            View child = mStatusIcons.getChildAt(i);
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+            lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
+            child.setLayoutParams(lp);
+        }
+        for (int i = 0; i < mStatusIconsKeyguard.getChildCount(); i++) {
+            View child = mStatusIconsKeyguard.getChildAt(i);
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+            child.setLayoutParams(lp);
+        }
+        scaleBatteryMeterViews(mContext);
+    }
+
+    private void updateClock() {
+        FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size);
+        mClock.setPaddingRelative(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.status_bar_clock_starting_padding),
+                0,
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.status_bar_clock_end_padding),
+                0);
+    }
 }
index 62d6b76..97b31f2 100644 (file)
@@ -26,10 +26,13 @@ public class StatusBarIconList {
     private ArrayList<StatusBarIcon> mIcons = new ArrayList<>();
 
     public void defineSlots(String[] slots) {
+        mSlots.clear();
         final int N = slots.length;
         for (int i=0; i < N; i++) {
             mSlots.add(slots[i]);
-            mIcons.add(null);
+            if (mIcons.size() < mSlots.size()) {
+                mIcons.add(null);
+            }
         }
     }
 
index ab34768..7a6d080 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.widget.FrameLayout;
@@ -33,12 +34,14 @@ public class BrightnessMirrorController {
     public long TRANSITION_DURATION_OUT = 150;
     public long TRANSITION_DURATION_IN = 200;
 
+    private final StatusBarWindowView mStatusBarWindow;
     private final ScrimView mScrimBehind;
-    private final View mBrightnessMirror;
     private final View mNotificationPanel;
     private final int[] mInt2Cache = new int[2];
+    private View mBrightnessMirror;
 
     public BrightnessMirrorController(StatusBarWindowView statusBarWindow) {
+        mStatusBarWindow = statusBarWindow;
         mScrimBehind = (ScrimView) statusBarWindow.findViewById(R.id.scrim_behind);
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
         mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
@@ -56,11 +59,11 @@ public class BrightnessMirrorController {
         inAnimation(mNotificationPanel.animate())
                 .withLayer()
                 .withEndAction(new Runnable() {
-            @Override
-            public void run() {
-                mBrightnessMirror.setVisibility(View.INVISIBLE);
-            }
-        });
+                    @Override
+                    public void run() {
+                        mBrightnessMirror.setVisibility(View.INVISIBLE);
+                    }
+                });
     }
 
     private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) {
@@ -104,4 +107,12 @@ public class BrightnessMirrorController {
                 R.integer.notification_panel_layout_gravity);
         mBrightnessMirror.setLayoutParams(lp);
     }
+
+    public void onDensityOrFontScaleChanged() {
+        int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
+        mStatusBarWindow.removeView(mBrightnessMirror);
+        mBrightnessMirror = LayoutInflater.from(mBrightnessMirror.getContext()).inflate(
+                R.layout.brightness_mirror, mStatusBarWindow, false);
+        mStatusBarWindow.addView(mBrightnessMirror, index);
+    }
 }
index ee483e5..be98d7a 100644 (file)
@@ -663,6 +663,11 @@ public class NotificationChildrenContainer extends ViewGroup {
             addView(divider, index);
             mDividers.set(i, divider);
         }
+        removeView(mOverflowNumber);
+        mOverflowNumber = null;
+        mOverflowInvertHelper = null;
+        mGroupOverFlowState = null;
+        updateGroupOverflow();
     }
 
     public void setUserLocked(boolean userLocked) {
index 8b52bf6..c962606 100644 (file)
@@ -747,7 +747,7 @@ public class NotificationStackScrollLayout extends ViewGroup
     public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
         if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
             mScrimController.setTopHeadsUpDragAmount(animView,
-                    Math.min(Math.abs(swipeProgress - 1.0f), 1.0f));
+                    Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
         }
         return true; // Don't fade out the notification
     }
@@ -3116,8 +3116,13 @@ public class NotificationStackScrollLayout extends ViewGroup
     }
 
     public void setOverflowContainer(NotificationOverflowContainer overFlowContainer) {
+        int index = -1;
+        if (mOverflowContainer != null) {
+            index = indexOfChild(mOverflowContainer);
+            removeView(mOverflowContainer);
+        }
         mOverflowContainer = overFlowContainer;
-        addView(mOverflowContainer);
+        addView(mOverflowContainer, index);
     }
 
     public void updateOverflowContainerVisibility(boolean visible) {
index 35ae8b4..0d10c6d 100644 (file)
@@ -1074,7 +1074,11 @@ public final class ScriptGroup extends BaseObj {
     protected void finalize() throws Throwable {
         // Clear out the list mClosures to avoid double-destroying the closures,
         // in case their finalizers race ahead.
-        mClosures.clear();
+        if (mClosures != null) {
+            // ScriptGroup created using the old Builder class does not
+            // initialize the field mClosures
+            mClosures.clear();
+        }
         super.finalize();
     }
 }
index e5fe03a..40d44b4 100644 (file)
@@ -1747,7 +1747,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
         if(restoreDefaultNetworkDelayStr != null &&
                 restoreDefaultNetworkDelayStr.length() != 0) {
             try {
-                return Integer.valueOf(restoreDefaultNetworkDelayStr);
+                return Integer.parseInt(restoreDefaultNetworkDelayStr);
             } catch (NumberFormatException e) {
             }
         }
index 5388f10..07aa565 100644 (file)
@@ -106,7 +106,7 @@ final class DockObserver extends SystemService {
                 FileReader file = new FileReader(DOCK_STATE_PATH);
                 try {
                     int len = file.read(buffer, 0, 1024);
-                    setActualDockStateLocked(Integer.valueOf((new String(buffer, 0, len)).trim()));
+                    setActualDockStateLocked(Integer.parseInt((new String(buffer, 0, len)).trim()));
                     mPreviousDockState = mActualDockState;
                 } finally {
                     file.close();
index 544e645..79d16da 100644 (file)
@@ -2473,7 +2473,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
             int subtypeId = NOT_A_SUBTYPE_ID;
             if (lastIme != null && lastImi != null) {
                 final boolean imiIdIsSame = lastImi.getId().equals(mCurMethodId);
-                final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+                final int lastSubtypeHash = Integer.parseInt(lastIme.second);
                 final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
                         : mCurrentSubtype.hashCode();
                 // If the last IME is the same as the current IME and the last subtype is not
@@ -2587,7 +2587,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
             final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
             if (lastImi == null) return null;
             try {
-                final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+                final int lastSubtypeHash = Integer.parseInt(lastIme.second);
                 final int lastSubtypeId =
                         InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
                 if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
@@ -3437,7 +3437,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
             if (subtypeHashCode != null) {
                 try {
                     lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
-                            imi, Integer.valueOf(subtypeHashCode));
+                            imi, Integer.parseInt(subtypeHashCode));
                 } catch (NumberFormatException e) {
                     Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
                 }
@@ -3798,9 +3798,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                             Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
                             continue;
                         }
-                        final int icon = Integer.valueOf(
+                        final int icon = Integer.parseInt(
                                 parser.getAttributeValue(null, ATTR_ICON));
-                        final int label = Integer.valueOf(
+                        final int label = Integer.parseInt(
                                 parser.getAttributeValue(null, ATTR_LABEL));
                         final String imeSubtypeLocale =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
@@ -3826,7 +3826,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                         final String subtypeIdString =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
                         if (subtypeIdString != null) {
-                            builder.setSubtypeId(Integer.valueOf(subtypeIdString));
+                            builder.setSubtypeId(Integer.parseInt(subtypeIdString));
                         }
                         tempSubtypesArray.add(builder.build());
                     }
index 229a3f4..ec05dae 100644 (file)
@@ -2875,6 +2875,19 @@ class MountService extends IMountService.Stub
     }
 
     @Override
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+        waitForReady();
+
+        try {
+            mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
+                    userId, flags);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
     public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
         try {
             final int uid = Binder.getCallingUid();
index 820551d..9602bf6 100644 (file)
@@ -714,20 +714,24 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
             }
             handleRemoveListLocked();
         }
+
+        // Called only by Telecomm to communicate call state across different phone accounts. So
+        // there is no need to add a valid subId or slotId.
         broadcastCallStateChanged(state, incomingNumber,
+                SubscriptionManager.INVALID_PHONE_INDEX,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
     }
 
-    public void notifyCallStateForSubscriber(int subId, int state, String incomingNumber) {
+    public void notifyCallStateForPhoneId(int phoneId, int subId, int state,
+                String incomingNumber) {
         if (!checkNotifyPermission("notifyCallState()")) {
             return;
         }
         if (VDBG) {
-            log("notifyCallStateForSubscriber: subId=" + subId
+            log("notifyCallStateForPhoneId: subId=" + subId
                 + " state=" + state + " incomingNumber=" + incomingNumber);
         }
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCallState[phoneId] = state;
                 mCallIncomingNumber[phoneId] = incomingNumber;
@@ -746,7 +750,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
             }
             handleRemoveListLocked();
         }
-        broadcastCallStateChanged(state, incomingNumber, subId);
+        broadcastCallStateChanged(state, incomingNumber, phoneId, subId);
     }
 
     public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) {
@@ -788,31 +792,27 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
             }
             handleRemoveListLocked();
         }
-        broadcastServiceStateChanged(state, subId);
-    }
-
-    public void notifySignalStrength(SignalStrength signalStrength) {
-        notifySignalStrengthForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                signalStrength);
+        broadcastServiceStateChanged(state, phoneId, subId);
     }
 
-    public void notifySignalStrengthForSubscriber(int subId, SignalStrength signalStrength) {
+    public void notifySignalStrengthForPhoneId(int phoneId, int subId,
+                SignalStrength signalStrength) {
         if (!checkNotifyPermission("notifySignalStrength()")) {
             return;
         }
         if (VDBG) {
-            log("notifySignalStrengthForSubscriber: subId=" + subId
-                + " signalStrength=" + signalStrength);
-            toStringLogSSC("notifySignalStrengthForSubscriber");
+            log("notifySignalStrengthForPhoneId: subId=" + subId
+                +" phoneId=" + phoneId + " signalStrength=" + signalStrength);
+            toStringLogSSC("notifySignalStrengthForPhoneId");
         }
+
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
-                if (VDBG) log("notifySignalStrengthForSubscriber: valid phoneId=" + phoneId);
+                if (VDBG) log("notifySignalStrengthForPhoneId: valid phoneId=" + phoneId);
                 mSignalStrength[phoneId] = signalStrength;
                 for (Record r : mRecords) {
                     if (VDBG) {
-                        log("notifySignalStrengthForSubscriber: r=" + r + " subId=" + subId
+                        log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " ss=" + signalStrength);
                     }
                     if (r.matchPhoneStateListenerEvent(
@@ -820,7 +820,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
                             idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG) {
-                                log("notifySignalStrengthForSubscriber: callback.onSsS r=" + r
+                                log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " ss=" + signalStrength);
                             }
@@ -835,7 +835,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
                             int gsmSignalStrength = signalStrength.getGsmSignalStrength();
                             int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
                             if (DBG) {
-                                log("notifySignalStrengthForSubscriber: callback.onSS r=" + r
+                                log("notifySignalStrengthForPhoneId: callback.onSS r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " gsmSS=" + gsmSignalStrength + " ss=" + ss);
                             }
@@ -846,11 +846,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
                     }
                 }
             } else {
-                log("notifySignalStrengthForSubscriber: invalid phoneId=" + phoneId);
+                log("notifySignalStrengthForPhoneId: invalid phoneId=" + phoneId);
             }
             handleRemoveListLocked();
         }
-        broadcastSignalStrengthChanged(signalStrength, subId);
+        broadcastSignalStrengthChanged(signalStrength, phoneId, subId);
     }
 
     @Override
@@ -1347,7 +1347,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
     // the legacy intent broadcasting
     //
 
-    private void broadcastServiceStateChanged(ServiceState state, int subId) {
+    private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneState(state.getState());
@@ -1363,10 +1363,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
         intent.putExtras(data);
         // Pass the subscription along with the intent.
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int subId) {
+    private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
+            int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneSignalStrength(signalStrength);
@@ -1382,6 +1384,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
         signalStrength.fillInNotifierBundle(data);
         intent.putExtras(data);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
@@ -1391,7 +1394,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
      * can be {@code SubscriptionManager.INVALID_SUBSCRIPTION_ID}, in which case we send
      * a global state change broadcast ({@code TelephonyManager.ACTION_PHONE_STATE_CHANGED}).
      */
-    private void broadcastCallStateChanged(int state, String incomingNumber, int subId) {
+    private void broadcastCallStateChanged(int state, String incomingNumber, int phoneId,
+                int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
             if (state == TelephonyManager.CALL_STATE_IDLE) {
@@ -1418,6 +1422,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
             intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
             intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         }
+        // If the phoneId is invalid, the broadcast is for overall call state.
+        if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
+            intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+        }
 
         // Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
         // that have the runtime one
index 801d6e0..a628747 100644 (file)
@@ -1110,7 +1110,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
         private int getInt(final String key, final int defaultValue) {
             if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
                 final String result = mCopyOnWriteDataStore.get(key);
-                return result != null ? Integer.valueOf(result) : 0;
+                return result != null ? Integer.parseInt(result) : 0;
             }
             return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
         }
index e0e6070..fcda83d 100644 (file)
@@ -330,7 +330,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
                         FileReader file = new FileReader(uei.getSwitchStatePath());
                         int len = file.read(buffer, 0, 1024);
                         file.close();
-                        curState = Integer.valueOf((new String(buffer, 0, len)).trim());
+                        curState = Integer.parseInt((new String(buffer, 0, len)).trim());
 
                         if (curState > 0) {
                             updateStateLocked(uei.getDevPath(), uei.getDevName(), curState);
index 72d7521..a60690c 100644 (file)
@@ -97,6 +97,7 @@ import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
 import android.app.IUserSwitchObserver;
 import android.app.Instrumentation;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -341,6 +342,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_B
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -9742,7 +9744,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                 } else {
                     mStackSupervisor.resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
                             null /* tempTaskInsetBounds */, preserveWindows,
-                            allowResizeInDockedMode);
+                            allowResizeInDockedMode, !DEFER_RESUME);
                 }
             }
         } finally {
@@ -11478,20 +11480,13 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
 
         synchronized (this) {
-            if (mStackSupervisor.isFocusedUserLockedProfile()) {
+            if (mStackSupervisor.isUserLockedProfile(userId)) {
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     final int currentUserId = mUserController.getCurrentUserIdLocked();
-                    // Get the focused task before launching launcher.
-
                     if (mUserController.isLockScreenDisabled(currentUserId)) {
-
                         // If there is no device lock, we will show the profile's credential page.
-                        // startActivityFromRecentsInner is intercepted and will forward user to it.
-                        if (mFocusedActivity != null) {
-                            mStackSupervisor.startActivityFromRecentsInner(
-                                    mFocusedActivity.task.taskId, null);
-                        }
+                        mActivityStarter.showConfirmDeviceCredential(userId);
                     } else {
                         // Showing launcher to avoid user entering credential twice.
                         startHomeActivityLocked(currentUserId, "notifyLockedProfile");
@@ -17973,46 +17968,11 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
         synchronized (this) {
             final long origId = Binder.clearCallingIdentity();
-            final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
-            if (stack != null) {
-                mWindowManager.deferSurfaceLayout();
-                try {
-                    if (fromStackId == DOCKED_STACK_ID) {
-
-                        // We are moving all tasks from the docked stack to the fullscreen stack,
-                        // which is dismissing the docked stack, so resize all other stacks to
-                        // fullscreen here already so we don't end up with resize trashing.
-                        for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                            if (StackId.isResizeableByDockedStack(i)) {
-                                ActivityStack otherStack = mStackSupervisor.getStack(i);
-                                if (otherStack != null) {
-                                    mStackSupervisor.resizeStackLocked(i,
-                                            null, null, null, PRESERVE_WINDOWS,
-                                            true /* allowResizeInDockedMode */);
-                                }
-                            }
-                        }
-                    }
-                    final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                    final int size = tasks.size();
-                    if (onTop) {
-                        for (int i = 0; i < size; i++) {
-                            mStackSupervisor.moveTaskToStackLocked(tasks.get(i).taskId,
-                                    FULLSCREEN_WORKSPACE_STACK_ID, onTop, !FORCE_FOCUS,
-                                    "moveTasksToFullscreenStack", ANIMATE);
-                        }
-                    } else {
-                        for (int i = size - 1; i >= 0; i--) {
-                            mStackSupervisor.positionTaskInStackLocked(tasks.get(i).taskId,
-                                    FULLSCREEN_WORKSPACE_STACK_ID, 0);
-                        }
-                    }
-                } finally {
-                    mWindowManager.continueSurfaceLayout();
-                }
-
+            try {
+                mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
-            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -18234,7 +18194,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                     for (int stackId : resizedStacks) {
                         final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
                         mStackSupervisor.resizeStackLocked(
-                                stackId, newBounds, null, null, false, false);
+                                stackId, newBounds, null, null, false, false, !DEFER_RESUME);
                     }
                 }
             }
@@ -21013,6 +20973,13 @@ public final class ActivityManagerService extends ActivityManagerNative
                 return mStackSupervisor.getTopVisibleActivities();
             }
         }
+
+        @Override
+        public void notifyDockedStackMinimizedChanged(boolean minimized) {
+            synchronized (ActivityManagerService.this) {
+                mStackSupervisor.setDockedStackMinimized(minimized);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
index 48f87b6..aaf8301 100755 (executable)
@@ -1396,7 +1396,7 @@ final class ActivityRecord {
             if (ATTR_ID.equals(attrName)) {
                 createTime = Long.valueOf(attrValue);
             } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
-                launchedFromUid = Integer.valueOf(attrValue);
+                launchedFromUid = Integer.parseInt(attrValue);
             } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
                 launchedFromPackage = attrValue;
             } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
@@ -1404,7 +1404,7 @@ final class ActivityRecord {
             } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
                 componentSpecified = Boolean.valueOf(attrValue);
             } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.valueOf(attrValue);
+                userId = Integer.parseInt(attrValue);
             } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
                 taskDescription.restoreFromXml(attrName, attrValue);
             } else {
index 6e2cebb..97ee744 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.Manifest;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -29,6 +30,8 @@ import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IActivityManager;
 import android.app.IActivityManager.WaitResult;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
@@ -37,6 +40,7 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -166,6 +170,7 @@ import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -238,6 +243,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
     // Restore task from the saved recents if it can't be found in any live stack.
     static final boolean RESTORE_FROM_RECENTS = true;
 
+    // Don't execute any calls to resume.
+    static final boolean DEFER_RESUME = true;
+
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -342,9 +350,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
     /** Used on user changes */
     final ArrayList<UserState> mStartingUsers = new ArrayList<>();
 
-    /** Used to queue up any background users being started */
-    final ArrayList<UserState> mStartingBackgroundUsers = new ArrayList<>();
-
     /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
      * is being brought in front of us. */
     boolean mUserLeaving = false;
@@ -432,6 +437,20 @@ public final class ActivityStackSupervisor implements DisplayListener {
      */
     private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
 
+
+    /**
+     * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
+     * will be ignored. Useful for the case where the caller is handling resizing of other stack and
+     * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
+     * like the docked stack going empty.
+     */
+    private boolean mAllowDockedStackResize = true;
+
+    /**
+     * Is dock currently minimized.
+     */
+    boolean mIsDockMinimized;
+
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -716,10 +735,44 @@ public final class ActivityStackSupervisor implements DisplayListener {
         return null;
     }
 
-    boolean isFocusedUserLockedProfile() {
-        final int userId = mFocusedStack.topRunningActivityLocked().userId;
-        return userId != UserHandle.myUserId()
-                && mService.mUserController.shouldConfirmCredentials(userId);
+    /**
+     * TODO: Handle freefom mode.
+     * @return true when credential confirmation is needed for the user and there is any
+     *         activity started by the user in any visible stack.
+     */
+    boolean isUserLockedProfile(@UserIdInt int userId) {
+        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+            return false;
+        }
+        final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+        final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+        final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack};
+        for (final ActivityStack activityStack : activityStacks) {
+            if (activityStack == null) {
+                continue;
+            }
+            if (activityStack.topRunningActivityLocked() == null) {
+                continue;
+            }
+            if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) {
+                continue;
+            }
+            if (activityStack.isDockedStack() && mIsDockMinimized) {
+                continue;
+            }
+            final TaskRecord topTask = activityStack.topTask();
+            if (topTask == null) {
+                continue;
+            }
+            // To handle the case that work app is in the task but just is not the top one.
+            for (int i = topTask.mActivities.size() - 1; i >= 0; i--) {
+                final ActivityRecord activityRecord = topTask.mActivities.get(i);
+                if (activityRecord.userId == userId) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     void setNextTaskIdForUserLocked(int taskId, int userId) {
@@ -1794,7 +1847,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 if (StackId.resizeStackWithLaunchBounds(stackId)) {
                     resizeStackLocked(stackId, bounds,
                             null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                            !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */);
+                            !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
                 } else {
                     // WM resizeTask must be done after the task is moved to the correct stack,
                     // because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1921,7 +1974,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
     }
 
     void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
-            boolean preserveWindows, boolean allowResizeInDockedMode) {
+            boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
         if (stackId == DOCKED_STACK_ID) {
             resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
                     preserveWindows);
@@ -1943,7 +1996,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
         mWindowManager.deferSurfaceLayout();
         try {
             resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
-            ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows);
+            if (!deferResume) {
+                ensureConfigurationAndResume(
+                        stack, stack.topRunningActivityLocked(), preserveWindows);
+            }
         } finally {
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -2030,9 +2086,64 @@ public final class ActivityStackSupervisor implements DisplayListener {
         }
     }
 
+    void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
+        final ActivityStack stack = getStack(fromStackId);
+        if (stack == null) {
+            return;
+        }
+
+        mWindowManager.deferSurfaceLayout();
+        try {
+            if (fromStackId == DOCKED_STACK_ID) {
+
+                // We are moving all tasks from the docked stack to the fullscreen stack,
+                // which is dismissing the docked stack, so resize all other stacks to
+                // fullscreen here already so we don't end up with resize trashing.
+                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                    if (StackId.isResizeableByDockedStack(i)) {
+                        ActivityStack otherStack = getStack(i);
+                        if (otherStack != null) {
+                            resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
+                                    true /* allowResizeInDockedMode */, DEFER_RESUME);
+                        }
+                    }
+                }
+
+                // Also disable docked stack resizing since we have manually adjusted the
+                // size of other stacks above and we don't want to trigger a docked stack
+                // resize when we remove task from it below and it is detached from the
+                // display because it no longer contains any tasks.
+                mAllowDockedStackResize = false;
+            }
+            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            final int size = tasks.size();
+            if (onTop) {
+                for (int i = 0; i < size; i++) {
+                    moveTaskToStackLocked(tasks.get(i).taskId,
+                            FULLSCREEN_WORKSPACE_STACK_ID, onTop, onTop /*forceFocus*/,
+                            "moveTasksToFullscreenStack", ANIMATE);
+                }
+            } else {
+                for (int i = size - 1; i >= 0; i--) {
+                    positionTaskInStackLocked(tasks.get(i).taskId,
+                            FULLSCREEN_WORKSPACE_STACK_ID, 0);
+                }
+            }
+        } finally {
+            mAllowDockedStackResize = true;
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
     void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds,
             Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
+
+        if (!mAllowDockedStackResize) {
+            // Docked stack resize currently disabled.
+            return;
+        }
+
         final ActivityStack stack = getStack(DOCKED_STACK_ID);
         if (stack == null) {
             Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
@@ -2042,6 +2153,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
         mWindowManager.deferSurfaceLayout();
         try {
+            // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
+            mAllowDockedStackResize = false;
             ActivityRecord r = stack.topRunningActivityLocked();
             resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
                     tempDockedTaskInsetBounds);
@@ -2052,20 +2165,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                 // In this case we make all other static stacks fullscreen and move all
                 // docked stack tasks to the fullscreen stack.
-                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                    if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
-                        resizeStackLocked(i, null, null, null, preserveWindows,
-                                true /* allowResizeInDockedMode */);
-                    }
-                }
-
-                ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                final int count = tasks.size();
-                for (int i = 0; i < count; i++) {
-                    moveTaskToStackLocked(tasks.get(i).taskId,
-                            FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
-                            false /* animate */);
-                }
+                moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
 
                 // stack shouldn't contain anymore activities, so nothing to resume.
                 r = null;
@@ -2080,12 +2180,13 @@ public final class ActivityStackSupervisor implements DisplayListener {
                     if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
                         resizeStackLocked(i, tempRect, tempOtherTaskBounds,
                                 tempOtherTaskInsetBounds, preserveWindows,
-                                true /* allowResizeInDockedMode */);
+                                true /* allowResizeInDockedMode */, !DEFER_RESUME);
                     }
                 }
             }
             ensureConfigurationAndResume(stack, r, preserveWindows);
         } finally {
+            mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
         }
@@ -2444,7 +2545,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
             // animation bounds for the pinned stack to the desired bounds the caller wants.
             resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
                     null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */);
+                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
 
             if (task.mActivities.size() == 1) {
                 // There is only one activity in the task. So, we can just move the task over to
@@ -2487,8 +2588,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
         stack.positionTask(task, position);
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
-        stack.ensureActivityConfigurationLocked(task.topRunningActivityLocked(), 0,
-                !PRESERVE_WINDOWS);
         stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
     }
@@ -3590,6 +3689,22 @@ public final class ActivityStackSupervisor implements DisplayListener {
         }
     }
 
+    void setDockedStackMinimized(boolean minimized) {
+        mIsDockMinimized = minimized;
+        if (minimized) {
+            // Docked stack is not visible, no need to confirm credentials for its top activity.
+            return;
+        }
+        final ActivityStack dockedStack = getStack(StackId.DOCKED_STACK_ID);
+        if (dockedStack == null) {
+            return;
+        }
+        final ActivityRecord top = dockedStack.topRunningActivityLocked();
+        if (top != null && mService.mUserController.shouldConfirmCredentials(top.userId)) {
+            mService.mActivityStarter.showConfirmDeviceCredential(top.userId);
+        }
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
 
         public ActivityStackSupervisorHandler(Looper looper) {
@@ -4241,28 +4356,31 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 ? new ActivityOptions(bOptions) : null;
         final int launchStackId = (activityOptions != null)
                 ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
-
         if (launchStackId == HOME_STACK_ID) {
             throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
                     + taskId + " can't be launch in the home stack.");
         }
+
+        if (launchStackId == DOCKED_STACK_ID) {
+            mWindowManager.setDockedStackCreateState(
+                    activityOptions.getDockCreateMode(), null /* initialBounds */);
+
+            // Defer updating the stack in which recents is until the app transition is done, to
+            // not run into issues where we still need to draw the task in recents but the
+            // docked stack is already created.
+            deferUpdateBounds(HOME_STACK_ID);
+            mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+        }
+
         task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
         if (task == null) {
+            continueUpdateBounds(HOME_STACK_ID);
+            mWindowManager.executeAppTransition();
             throw new IllegalArgumentException(
                     "startActivityFromRecentsInner: Task " + taskId + " not found.");
         }
 
         if (launchStackId != INVALID_STACK_ID) {
-            if (launchStackId == DOCKED_STACK_ID) {
-                mWindowManager.setDockedStackCreateState(
-                        activityOptions.getDockCreateMode(), null /* initialBounds */);
-
-                // Defer updating the stack in which recents is until the app transition is done, to
-                // not run into issues where we still need to draw the task in recents but the
-                // docked stack is already created.
-                deferUpdateBounds(HOME_STACK_ID);
-                mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
-            }
             if (task.stack.mStackId != launchStackId) {
                 moveTaskToStackLocked(
                         taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
@@ -4290,12 +4408,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
         intent = task.intent;
         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
         userId = task.userId;
-            int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
-                    null, null, 0, 0, bOptions, userId, null, task);
-            if (launchStackId == DOCKED_STACK_ID) {
-                setResizingDuringAnimation(task.taskId);
-            }
-            return result;
+        int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
+                null, null, 0, 0, bOptions, userId, null, task);
+        if (launchStackId == DOCKED_STACK_ID) {
+            setResizingDuringAnimation(task.taskId);
+        }
+        return result;
     }
 
     /**
index 785dd47..566d8d9 100644 (file)
@@ -29,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
+import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.IIntentSender;
@@ -76,6 +77,7 @@ class ActivityStartInterceptor {
     ActivityInfo mAInfo;
     String mResolvedType;
     TaskRecord mInTask;
+    ActivityOptions mActivityOptions;
 
     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
         mService = service;
@@ -92,7 +94,7 @@ class ActivityStartInterceptor {
     }
 
     void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
-            TaskRecord inTask, int callingPid, int callingUid) {
+            TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
         mUserManager = UserManager.get(mService.mContext);
         mIntent = intent;
         mCallingPid = callingPid;
@@ -101,6 +103,7 @@ class ActivityStartInterceptor {
         mAInfo = aInfo;
         mResolvedType = resolvedType;
         mInTask = inTask;
+        mActivityOptions = activityOptions;
         if (interceptSuspendPackageIfNeed()) {
             // Skip the rest of interceptions as the package is suspended by device admin so
             // no user action can undo this.
@@ -177,6 +180,12 @@ class ActivityStartInterceptor {
             mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
             mInTask = null;
         }
+        if (mActivityOptions == null) {
+            mActivityOptions = ActivityOptions.makeBasic();
+        }
+        // Showing credential confirmation activity in home task to avoid stopping multi-windowed
+        // mode after showing the full-screen credential confirmation activity.
+        mActivityOptions.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
index 4075230..9c93f2c 100644 (file)
@@ -82,9 +82,11 @@ import android.app.AppGlobals;
 import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -380,7 +382,8 @@ class ActivityStarter {
         }
 
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
-        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid);
+        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid,
+                options);
         intent = mInterceptor.mIntent;
         rInfo = mInterceptor.mRInfo;
         aInfo = mInterceptor.mAInfo;
@@ -388,7 +391,7 @@ class ActivityStarter {
         inTask = mInterceptor.mInTask;
         callingPid = mInterceptor.mCallingPid;
         callingUid = mInterceptor.mCallingUid;
-
+        options = mInterceptor.mActivityOptions;
         if (abort) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
@@ -589,6 +592,53 @@ class ActivityStarter {
         }
     }
 
+    void showConfirmDeviceCredential(int userId) {
+        // First, retrieve the stack that we want to resume after credential is confirmed.
+        ActivityStack targetStack;
+        ActivityStack fullscreenStack =
+                mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+        if (fullscreenStack != null &&
+                fullscreenStack.getStackVisibilityLocked(null) != ActivityStack.STACK_INVISIBLE) {
+            // Single window case and the case that the docked stack is shown with fullscreen stack.
+            targetStack = fullscreenStack;
+        } else {
+            // The case that the docked stack is shown with recent.
+            targetStack = mSupervisor.getStack(HOME_STACK_ID);
+        }
+        if (targetStack == null) {
+            return;
+        }
+        final KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        final Intent credential =
+                km.createConfirmDeviceCredentialIntent(null, null, userId);
+        credential.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
+        final ActivityRecord activityRecord = targetStack.topRunningActivityLocked();
+        if (activityRecord != null) {
+            final IIntentSender target = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY,
+                    activityRecord.launchedFromPackage,
+                    activityRecord.launchedFromUid,
+                    activityRecord.userId,
+                    null, null, 0,
+                    new Intent[] { activityRecord.intent },
+                    new String[] { activityRecord.resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT |
+                            PendingIntent.FLAG_ONE_SHOT |
+                            PendingIntent.FLAG_IMMUTABLE,
+                    null);
+            credential.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+            // Show confirm credentials activity.
+            mService.mContext.startActivityAsUser(credential, options.toBundle(),
+                    UserHandle.CURRENT);
+        }
+    }
+
+
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
index 3d42047..6cd6ff4 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
@@ -32,11 +33,12 @@ import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFormatException;
-import android.os.PowerManager;
+import android.os.Parcelable;
 import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SynchronousResultReceiver;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
@@ -56,7 +58,6 @@ import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.PowerProfile;
-import com.android.internal.telephony.ITelephony;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 
@@ -71,7 +72,7 @@ import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
+import java.util.concurrent.TimeoutException;
 
 /**
  * All information we are collecting about things that can happen that impact
@@ -81,15 +82,32 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         implements PowerManagerInternal.LowPowerModeListener {
     static final String TAG = "BatteryStatsService";
 
-    static IBatteryStats sService;
+    /**
+     * How long to wait on an individual subsystem to return its stats.
+     */
+    private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+
+    private static IBatteryStats sService;
+
     final BatteryStatsImpl mStats;
-    final BatteryStatsHandler mHandler;
-    Context mContext;
-    PowerManagerInternal mPowerManagerInternal;
+    private final BatteryStatsHandler mHandler;
+    private Context mContext;
+    private IWifiManager mWifiManager;
+    private TelephonyManager mTelephony;
+
+    // Lock acquired when extracting data from external sources.
+    private final Object mExternalStatsLock = new Object();
+
+    // WiFi keeps an accumulated total of stats, unlike Bluetooth.
+    // Keep the last WiFi stats so we can compute a delta.
+    @GuardedBy("mExternalStatsLock")
+    private WifiActivityEnergyInfo mLastInfo =
+            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
 
     class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
         public static final int MSG_SYNC_EXTERNAL_STATS = 1;
         public static final int MSG_WRITE_TO_DISK = 2;
+
         private int mUpdateFlags = 0;
         private IntArray mUidsToRemove = new IntArray();
 
@@ -107,7 +125,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                         updateFlags = mUpdateFlags;
                         mUpdateFlags = 0;
                     }
-                    updateExternalStats((String)msg.obj, updateFlags);
+                    updateExternalStatsSync((String)msg.obj, updateFlags);
 
                     // other parts of the system could be calling into us
                     // from mStats in order to report of changes. We must grab the mStats
@@ -124,7 +142,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                     break;
 
                 case MSG_WRITE_TO_DISK:
-                    updateExternalStats("write", UPDATE_ALL);
+                    updateExternalStatsSync("write", UPDATE_ALL);
                     synchronized (mStats) {
                         mStats.writeAsyncLocked();
                     }
@@ -178,16 +196,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
      * initialized.  So we initialize the low power observer later.
      */
     public void initPowerManagement() {
-        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mPowerManagerInternal.registerLowPowerModeObserver(this);
-        mStats.notePowerSaveMode(mPowerManagerInternal.getLowPowerModeEnabled());
+        final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
+        powerMgr.registerLowPowerModeObserver(this);
+        mStats.notePowerSaveMode(powerMgr.getLowPowerModeEnabled());
         (new WakeupReasonThread()).start();
     }
 
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
 
-        updateExternalStats("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+        updateExternalStatsSync("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
         synchronized (mStats) {
             mStats.shutdownLocked();
         }
@@ -287,7 +305,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+        updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
         }
@@ -302,7 +320,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+        updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
         }
@@ -875,6 +893,47 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         }
     }
 
+    @Override
+    public void noteWifiControllerActivity(WifiActivityEnergyInfo info) {
+        enforceCallingPermission();
+
+        if (info == null || !info.isValid()) {
+            Slog.e(TAG, "invalid wifi data given: " + info);
+            return;
+        }
+
+        synchronized (mStats) {
+            mStats.updateWifiStateLocked(info);
+        }
+    }
+
+    @Override
+    public void noteBluetoothControllerActivity(BluetoothActivityEnergyInfo info) {
+        enforceCallingPermission();
+        if (info == null || !info.isValid()) {
+            Slog.e(TAG, "invalid bluetooth data given: " + info);
+            return;
+        }
+
+        synchronized (mStats) {
+            mStats.updateBluetoothStateLocked(info);
+        }
+    }
+
+    @Override
+    public void noteModemControllerActivity(ModemActivityInfo info) {
+        enforceCallingPermission();
+
+        if (info == null || !info.isValid()) {
+            Slog.e(TAG, "invalid modem data given: " + info);
+            return;
+        }
+
+        synchronized (mStats) {
+            mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(), info);
+        }
+    }
+
     public boolean isOnBattery() {
         return mStats.isOnBattery();
     }
@@ -901,7 +960,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
 
                 // Sync external stats first as the battery has changed states. If we don't sync
                 // immediately here, we may not collect the relevant data later.
-                updateExternalStats("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+                updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
                 synchronized (mStats) {
                     mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
                 }
@@ -1088,9 +1147,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                         pw.println("Battery stats reset.");
                         noOutput = true;
                     }
-                    updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+                    updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
                 } else if ("--write".equals(arg)) {
-                    updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+                    updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
                     synchronized (mStats) {
                         mStats.writeSyncLocked();
                         pw.println("Battery stats written.");
@@ -1154,7 +1213,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                 flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
             }
             // Fetch data from external sources and update the BatteryStatsImpl object with them.
-            updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+            updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -1215,145 +1274,96 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         }
     }
 
-    // Objects for extracting data from external sources.
-    private final Object mExternalStatsLock = new Object();
-
-    @GuardedBy("mExternalStatsLock")
-    private IWifiManager mWifiManager;
-
-    // WiFi keeps an accumulated total of stats, unlike Bluetooth.
-    // Keep the last WiFi stats so we can compute a delta.
-    @GuardedBy("mExternalStatsLock")
-    private WifiActivityEnergyInfo mLastInfo =
-            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
-
-    @GuardedBy("mExternalStatsLock")
-    private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() {
-        if (mWifiManager == null) {
-            mWifiManager = IWifiManager.Stub.asInterface(
-                    ServiceManager.getService(Context.WIFI_SERVICE));
-            if (mWifiManager == null) {
-                return null;
-            }
-        }
-
-        try {
-            // We read the data even if we are not on battery. This is so that we keep the
-            // correct delta from when we should start reading (aka when we are on battery).
-            WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo();
-            if (info != null && info.isValid()) {
-                if (info.mControllerEnergyUsed < 0 || info.mControllerIdleTimeMs < 0 ||
-                        info.mControllerRxTimeMs < 0 || info.mControllerTxTimeMs < 0) {
-                    Slog.wtf(TAG, "Reported WiFi energy data is invalid: " + info);
-                    return null;
-                }
-
-                final long timePeriodMs = info.mTimestamp - mLastInfo.mTimestamp;
-                final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
-                final long lastTxMs = mLastInfo.mControllerTxTimeMs;
-                final long lastRxMs = mLastInfo.mControllerRxTimeMs;
-                final long lastEnergy = mLastInfo.mControllerEnergyUsed;
-
-                // We will modify the last info object to be the delta, and store the new
-                // WifiActivityEnergyInfo object as our last one.
-                final WifiActivityEnergyInfo result = mLastInfo;
-                result.mTimestamp = info.getTimeStamp();
-                result.mStackState = info.getStackState();
-
-                // These times seem to be the most reliable.
-                result.mControllerTxTimeMs = info.mControllerTxTimeMs - lastTxMs;
-                result.mControllerRxTimeMs = info.mControllerRxTimeMs - lastRxMs;
-
-                // WiFi calculates the idle time as a difference from the on time and the various
-                // Rx + Tx times. There seems to be some missing time there because this sometimes
-                // becomes negative. Just cap it at 0 and move on.
-                // b/21613534
-                result.mControllerIdleTimeMs = Math.max(0, info.mControllerIdleTimeMs - lastIdleMs);
-                result.mControllerEnergyUsed =
-                        Math.max(0, info.mControllerEnergyUsed - lastEnergy);
-
-                if (result.mControllerTxTimeMs < 0 ||
-                        result.mControllerRxTimeMs < 0) {
-                    // The stats were reset by the WiFi system (which is why our delta is negative).
-                    // Returns the unaltered stats.
-                    result.mControllerEnergyUsed = info.mControllerEnergyUsed;
-                    result.mControllerRxTimeMs = info.mControllerRxTimeMs;
-                    result.mControllerTxTimeMs = info.mControllerTxTimeMs;
-                    result.mControllerIdleTimeMs = info.mControllerIdleTimeMs;
-
-                    Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
-                }
-
-                // There is some accuracy error in reports so allow some slop in the results.
-                final long SAMPLE_ERROR_MILLIS = 750;
-                final long totalTimeMs = result.mControllerIdleTimeMs + result.mControllerRxTimeMs +
-                        result.mControllerTxTimeMs;
-                if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
-                    StringBuilder sb = new StringBuilder();
-                    sb.append("Total time ");
-                    TimeUtils.formatDuration(totalTimeMs, sb);
-                    sb.append(" is longer than sample period ");
-                    TimeUtils.formatDuration(timePeriodMs, sb);
-                    sb.append(".\n");
-                    sb.append("Previous WiFi snapshot: ").append("idle=");
-                    TimeUtils.formatDuration(lastIdleMs, sb);
-                    sb.append(" rx=");
-                    TimeUtils.formatDuration(lastRxMs, sb);
-                    sb.append(" tx=");
-                    TimeUtils.formatDuration(lastTxMs, sb);
-                    sb.append(" e=").append(lastEnergy);
-                    sb.append("\n");
-                    sb.append("Current WiFi snapshot: ").append("idle=");
-                    TimeUtils.formatDuration(info.mControllerIdleTimeMs, sb);
-                    sb.append(" rx=");
-                    TimeUtils.formatDuration(info.mControllerRxTimeMs, sb);
-                    sb.append(" tx=");
-                    TimeUtils.formatDuration(info.mControllerTxTimeMs, sb);
-                    sb.append(" e=").append(info.mControllerEnergyUsed);
-                    Slog.wtf(TAG, sb.toString());
-                }
-
-                mLastInfo = info;
-                return result;
-            }
-        } catch (RemoteException e) {
-            // Nothing to report, WiFi is dead.
-        }
-        return null;
+    private WifiActivityEnergyInfo extractDelta(WifiActivityEnergyInfo latest) {
+        final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
+        final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
+        final long lastTxMs = mLastInfo.mControllerTxTimeMs;
+        final long lastRxMs = mLastInfo.mControllerRxTimeMs;
+        final long lastEnergy = mLastInfo.mControllerEnergyUsed;
+
+        // We will modify the last info object to be the delta, and store the new
+        // WifiActivityEnergyInfo object as our last one.
+        final WifiActivityEnergyInfo delta = mLastInfo;
+        delta.mTimestamp = latest.getTimeStamp();
+        delta.mStackState = latest.getStackState();
+
+        // These times seem to be the most reliable.
+        delta.mControllerTxTimeMs = latest.mControllerTxTimeMs - lastTxMs;
+        delta.mControllerRxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
+
+        // WiFi calculates the idle time as a difference from the on time and the various
+        // Rx + Tx times. There seems to be some missing time there because this sometimes
+        // becomes negative. Just cap it at 0 and move on.
+        // b/21613534
+        delta.mControllerIdleTimeMs = Math.max(0, latest.mControllerIdleTimeMs - lastIdleMs);
+        delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
+
+        if (delta.mControllerTxTimeMs < 0 || delta.mControllerRxTimeMs < 0) {
+            // The stats were reset by the WiFi system (which is why our delta is negative).
+            // Returns the unaltered stats.
+            delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
+            delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
+            delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
+            delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+
+            Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
+        }
+
+        // There is some accuracy error in reports so allow some slop in the results.
+        final long SAMPLE_ERROR_MILLIS = 750;
+        final long totalTimeMs = delta.mControllerIdleTimeMs + delta.mControllerRxTimeMs +
+                delta.mControllerTxTimeMs;
+        if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Total time ");
+            TimeUtils.formatDuration(totalTimeMs, sb);
+            sb.append(" is longer than sample period ");
+            TimeUtils.formatDuration(timePeriodMs, sb);
+            sb.append(".\n");
+            sb.append("Previous WiFi snapshot: ").append("idle=");
+            TimeUtils.formatDuration(lastIdleMs, sb);
+            sb.append(" rx=");
+            TimeUtils.formatDuration(lastRxMs, sb);
+            sb.append(" tx=");
+            TimeUtils.formatDuration(lastTxMs, sb);
+            sb.append(" e=").append(lastEnergy);
+            sb.append("\n");
+            sb.append("Current WiFi snapshot: ").append("idle=");
+            TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
+            sb.append(" rx=");
+            TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
+            sb.append(" tx=");
+            TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
+            sb.append(" e=").append(latest.mControllerEnergyUsed);
+            Slog.wtf(TAG, sb.toString());
+        }
+
+        mLastInfo = latest;
+        return delta;
     }
 
-    @GuardedBy("mExternalStatsLock")
-    private BluetoothActivityEnergyInfo pullBluetoothEnergyInfoLocked() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null) {
-            BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
-                    BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
-            if (info != null && info.isValid()) {
-                if (info.getControllerEnergyUsed() < 0 || info.getControllerIdleTimeMillis() < 0 ||
-                        info.getControllerRxTimeMillis() < 0 || info.getControllerTxTimeMillis() < 0) {
-                    Slog.wtf(TAG, "Bluetooth energy data is invalid: " + info);
-                }
-                return info;
-            }
+    /**
+     * Helper method to extract the Parcelable controller info from a
+     * SynchronousResultReceiver.
+     */
+    private static <T extends Parcelable> T awaitControllerInfo(
+            @Nullable SynchronousResultReceiver receiver) throws TimeoutException {
+        if (receiver == null) {
+            return null;
         }
-        return null;
-    }
 
-    @GuardedBy("mExternalStatsLock")
-    private ModemActivityInfo pullModemActivityInfoLocked() {
-        ITelephony tm = ITelephony.Stub.asInterface(ServiceManager.getService(
-                Context.TELEPHONY_SERVICE));
-        try {
-            if (tm != null) {
-                ModemActivityInfo info = tm.getModemActivityInfo();
-                if (info == null || info.isValid()) {
-                    return info;
-                }
-                Slog.wtf(TAG, "Modem activity info is invalid: " + info);
+        final SynchronousResultReceiver.Result result =
+                receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
+        if (result.bundle != null) {
+            // This is the final destination for the Bundle.
+            result.bundle.setDefusable(true);
+
+            final T data = result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
+            if (data != null) {
+                return data;
             }
-        } catch (RemoteException e) {
-            // Nothing to do.
         }
+        Slog.e(TAG, "no controller energy info supplied");
         return null;
     }
 
@@ -1371,58 +1381,108 @@ public final class BatteryStatsService extends IBatteryStats.Stub
      *                    {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_WIFI},
      *                    and {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_BT}.
      */
-    void updateExternalStats(final String reason, final int updateFlags) {
+    void updateExternalStatsSync(final String reason, int updateFlags) {
+        SynchronousResultReceiver wifiReceiver = null;
+        SynchronousResultReceiver bluetoothReceiver = null;
+        SynchronousResultReceiver modemReceiver = null;
+
         synchronized (mExternalStatsLock) {
             if (mContext == null) {
-                // We haven't started yet (which means the BatteryStatsImpl object has
-                // no power profile. Don't consume data we can't compute yet.
+                // Don't do any work yet.
                 return;
             }
 
-            if (BatteryStatsImpl.DEBUG_ENERGY) {
-                Slog.d(TAG, "Updating external stats: reason=" + reason);
-            }
-
-            WifiActivityEnergyInfo wifiEnergyInfo = null;
             if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
-                wifiEnergyInfo = pullWifiEnergyInfoLocked();
-            }
+                if (mWifiManager == null) {
+                    mWifiManager = IWifiManager.Stub.asInterface(
+                            ServiceManager.getService(Context.WIFI_SERVICE));
+                }
 
-            ModemActivityInfo modemActivityInfo = null;
-            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
-                modemActivityInfo = pullModemActivityInfoLocked();
+                if (mWifiManager != null) {
+                    try {
+                        wifiReceiver = new SynchronousResultReceiver();
+                        mWifiManager.requestActivityInfo(wifiReceiver);
+                    } catch (RemoteException e) {
+                        // Oh well.
+                    }
+                }
             }
 
-            BluetoothActivityEnergyInfo bluetoothEnergyInfo = null;
             if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
-                // We only pull bluetooth stats when we have to, as we are not distributing its
-                // use amongst apps and the sampling frequency does not matter.
-                bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
+                final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+                if (adapter != null) {
+                    bluetoothReceiver = new SynchronousResultReceiver();
+                    adapter.requestControllerActivityEnergyInfo(
+                            BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED,
+                            bluetoothReceiver);
+                }
             }
 
-            synchronized (mStats) {
-                final long elapsedRealtime = SystemClock.elapsedRealtime();
-                final long uptime = SystemClock.uptimeMillis();
-                if (mStats.mRecordAllHistory) {
-                    mStats.addHistoryEventLocked(elapsedRealtime, uptime,
-                            BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, reason, 0);
+            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
+                if (mTelephony == null) {
+                    mTelephony = TelephonyManager.from(mContext);
                 }
 
-                if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU) != 0) {
-                    mStats.updateCpuTimeLocked();
-                    mStats.updateKernelWakelocksLocked();
+                if (mTelephony != null) {
+                    modemReceiver = new SynchronousResultReceiver();
+                    mTelephony.requestModemActivityInfo(modemReceiver);
                 }
+            }
 
-                if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
-                    mStats.updateMobileRadioStateLocked(elapsedRealtime, modemActivityInfo);
+            WifiActivityEnergyInfo wifiInfo = null;
+            BluetoothActivityEnergyInfo bluetoothInfo = null;
+            ModemActivityInfo modemInfo = null;
+            try {
+                wifiInfo = awaitControllerInfo(wifiReceiver);
+            } catch (TimeoutException e) {
+                Slog.w(TAG, "Timeout reading wifi stats");
+            }
+
+            try {
+                bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
+            } catch (TimeoutException e) {
+                Slog.w(TAG, "Timeout reading bt stats");
+            }
+
+            try {
+                modemInfo = awaitControllerInfo(modemReceiver);
+            } catch (TimeoutException e) {
+                Slog.w(TAG, "Timeout reading modem stats");
+            }
+
+            synchronized (mStats) {
+                mStats.addHistoryEventLocked(
+                        SystemClock.elapsedRealtime(),
+                        SystemClock.uptimeMillis(),
+                        BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
+                        reason, 0);
+
+                mStats.updateCpuTimeLocked();
+                mStats.updateKernelWakelocksLocked();
+
+                if (wifiInfo != null) {
+                    if (wifiInfo.isValid()) {
+                        mStats.updateWifiStateLocked(extractDelta(wifiInfo));
+                    } else {
+                        Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
+                    }
                 }
 
-                if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
-                    mStats.updateWifiStateLocked(wifiEnergyInfo);
+                if (bluetoothInfo != null) {
+                    if (bluetoothInfo.isValid()) {
+                        mStats.updateBluetoothStateLocked(bluetoothInfo);
+                    } else {
+                        Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
+                    }
                 }
 
-                if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
-                    mStats.updateBluetoothStateLocked(bluetoothEnergyInfo);
+                if (modemInfo != null) {
+                    if (modemInfo.isValid()) {
+                        mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(),
+                                modemInfo);
+                    } else {
+                        Slog.e(TAG, "modem info is invalid: " + modemInfo);
+                    }
                 }
             }
         }
@@ -1439,7 +1499,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         }
         long ident = Binder.clearCallingIdentity();
         try {
-            updateExternalStats("get-health-stats-for-uid",
+            updateExternalStatsSync("get-health-stats-for-uid",
                     BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
             synchronized (mStats) {
                 return getHealthStatsForUidLocked(requestUid);
@@ -1464,7 +1524,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         long ident = Binder.clearCallingIdentity();
         int i=-1;
         try {
-            updateExternalStats("get-health-stats-for-uids",
+            updateExternalStatsSync("get-health-stats-for-uids",
                     BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
             synchronized (mStats) {
                 final int N = requestUids.length;
@@ -1499,7 +1559,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
 
     /**
      * Gets a HealthStatsParceler for the given uid. You should probably call
-     * updateExternalStats first.
+     * updateExternalStatsSync first.
      */
     HealthStatsParceler getHealthStatsForUidLocked(int requestUid) {
         final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter();
index ceb7db6..a2472ac 100644 (file)
@@ -529,7 +529,7 @@ public class TaskPersister {
             if (taskIdEnd > 0) {
                 final int taskId;
                 try {
-                    taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
+                    taskId = Integer.parseInt(filename.substring(0, taskIdEnd));
                     if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: Found taskId=" + taskId);
                 } catch (Exception e) {
                     Slog.wtf(TAG, "removeObsoleteFiles: Can't parse file=" + file.getName());
index 08d2399..5a5f861 100644 (file)
@@ -1227,7 +1227,7 @@ final class TaskRecord {
             if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
                     attrName + " value=" + attrValue);
             if (ATTR_TASKID.equals(attrName)) {
-                if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
+                if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
             } else if (ATTR_REALACTIVITY.equals(attrName)) {
                 realActivity = ComponentName.unflattenFromString(attrValue);
             } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
@@ -1246,13 +1246,13 @@ final class TaskRecord {
             } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
                 askedCompatMode = Boolean.valueOf(attrValue);
             } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.valueOf(attrValue);
+                userId = Integer.parseInt(attrValue);
             } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
                 userSetupComplete = Boolean.valueOf(attrValue);
             } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
-                effectiveUid = Integer.valueOf(attrValue);
+                effectiveUid = Integer.parseInt(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
-                taskType = Integer.valueOf(attrValue);
+                taskType = Integer.parseInt(attrValue);
             } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
                 firstActiveTime = Long.valueOf(attrValue);
             } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
@@ -1268,19 +1268,19 @@ final class TaskRecord {
             } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
                 taskDescription.restoreFromXml(attrName, attrValue);
             } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
-                taskAffiliation = Integer.valueOf(attrValue);
+                taskAffiliation = Integer.parseInt(attrValue);
             } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
-                prevTaskId = Integer.valueOf(attrValue);
+                prevTaskId = Integer.parseInt(attrValue);
             } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
-                nextTaskId = Integer.valueOf(attrValue);
+                nextTaskId = Integer.parseInt(attrValue);
             } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
-                taskAffiliationColor = Integer.valueOf(attrValue);
+                taskAffiliationColor = Integer.parseInt(attrValue);
             } else if (ATTR_CALLING_UID.equals(attrName)) {
-                callingUid = Integer.valueOf(attrValue);
+                callingUid = Integer.parseInt(attrValue);
             } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
                 callingPackage = attrValue;
             } else if (ATTR_RESIZE_MODE.equals(attrName)) {
-                resizeMode = Integer.valueOf(attrValue);
+                resizeMode = Integer.parseInt(attrValue);
                 resizeMode = (resizeMode == RESIZE_MODE_CROP_WINDOWS)
                         ? RESIZE_MODE_FORCE_RESIZEABLE : resizeMode;
             } else if (ATTR_PRIVILEGED.equals(attrName)) {
@@ -1288,9 +1288,9 @@ final class TaskRecord {
             } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
                 bounds = Rect.unflattenFromString(attrValue);
             } else if (ATTR_MINIMAL_WIDTH.equals(attrName)) {
-                minimalWidth = Integer.valueOf(attrValue);
+                minimalWidth = Integer.parseInt(attrValue);
             } else if (ATTR_MINIMAL_HEIGHT.equals(attrName)) {
-                minimalHeight = Integer.valueOf(attrValue);
+                minimalHeight = Integer.parseInt(attrValue);
             } else {
                 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
             }
index dc62609..5f9efe7 100644 (file)
@@ -548,8 +548,7 @@ public class NetworkDiagnostics {
             mMeasurement.description += " src{" + getSocketAddressString() + "}";
 
             // This needs to be fixed length so it can be dropped into the pre-canned packet.
-            final String sixRandomDigits =
-                    Integer.valueOf(mRandom.nextInt(900000) + 100000).toString();
+            final String sixRandomDigits = String.valueOf(mRandom.nextInt(900000) + 100000);
             mMeasurement.description += " qtype{" + mQueryType + "}"
                     + " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
 
index 2dafa3e..c770620 100644 (file)
@@ -222,7 +222,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
             removeClient(client);
         }
         if (DEBUG) Slog.v(TAG, "handleError(client="
-                + client != null ? client.getOwnerString() : "null" + ", error = " + error + ")");
+                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
         // This is the magic code that starts the next client when the old client finishes.
         if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
             mHandler.removeCallbacks(mResetClientState);
index 55f37b8..35628a2 100644 (file)
@@ -486,7 +486,7 @@ public class JobStore {
                 final List<JobStatus> jobs = new ArrayList<JobStatus>();
                 // Read in version info.
                 try {
-                    int version = Integer.valueOf(parser.getAttributeValue(null, "version"));
+                    int version = Integer.parseInt(parser.getAttributeValue(null, "version"));
                     if (version != JOBS_FILE_VERSION) {
                         Slog.d(TAG, "Invalid version number, aborting jobs file read.");
                         return null;
@@ -534,14 +534,14 @@ public class JobStore {
             try {
                 jobBuilder = buildBuilderFromXml(parser);
                 jobBuilder.setPersisted(true);
-                uid = Integer.valueOf(parser.getAttributeValue(null, "uid"));
+                uid = Integer.parseInt(parser.getAttributeValue(null, "uid"));
 
                 String val = parser.getAttributeValue(null, "priority");
                 if (val != null) {
-                    jobBuilder.setPriority(Integer.valueOf(val));
+                    jobBuilder.setPriority(Integer.parseInt(val));
                 }
                 val = parser.getAttributeValue(null, "sourceUserId");
-                sourceUserId = val == null ? -1 : Integer.valueOf(val);
+                sourceUserId = val == null ? -1 : Integer.parseInt(val);
             } catch (NumberFormatException e) {
                 Slog.e(TAG, "Error parsing job's required fields, skipping");
                 return null;
@@ -684,7 +684,7 @@ public class JobStore {
 
         private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
             // Pull out required fields from <job> attributes.
-            int jobId = Integer.valueOf(parser.getAttributeValue(null, "jobid"));
+            int jobId = Integer.parseInt(parser.getAttributeValue(null, "jobid"));
             String packageName = parser.getAttributeValue(null, "package");
             String className = parser.getAttributeValue(null, "class");
             ComponentName cname = new ComponentName(packageName, className);
@@ -720,7 +720,7 @@ public class JobStore {
             if (val != null) {
                 long initialBackoff = Long.valueOf(val);
                 val = parser.getAttributeValue(null, "backoff-policy");
-                int backoffPolicy = Integer.valueOf(val);  // Will throw NFE which we catch higher up.
+                int backoffPolicy = Integer.parseInt(val);  // Will throw NFE which we catch higher up.
                 jobBuilder.setBackoffCriteria(initialBackoff, backoffPolicy);
             }
         }
index 4a41705..78b3f41 100644 (file)
@@ -303,7 +303,7 @@ public class RankingHelper implements RankingConfig {
     private static int tryParseInt(String value, int defValue) {
         if (TextUtils.isEmpty(value)) return defValue;
         try {
-            return Integer.valueOf(value);
+            return Integer.parseInt(value);
         } catch (NumberFormatException e) {
             return defValue;
         }
index a11ee74..66c1a53 100644 (file)
@@ -174,12 +174,13 @@ public final class Installer extends SystemService {
         mInstaller.execute("destroy_app_profiles", pkgName);
     }
 
-    public void createUserConfig(int userid) throws InstallerException {
-        mInstaller.execute("mkuserconfig", userid);
+    public void createUserData(String uuid, int userId, int userSerial, int flags)
+            throws InstallerException {
+        mInstaller.execute("create_user_data", uuid, userId, userSerial, flags);
     }
 
-    public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
-        mInstaller.execute("rmuser", uuid, userid);
+    public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
+        mInstaller.execute("destroy_user_data", uuid, userId, flags);
     }
 
     public void markBootComplete(String instructionSet) throws InstallerException {
index dc81c65..4819de5 100644 (file)
@@ -109,7 +109,6 @@ import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
 import android.app.backup.IBackupManager;
-import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -13420,14 +13419,17 @@ public class PackageManagerService extends IPackageManager.Stub {
             final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
                     move.dataAppName);
             Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
+            final int[] userIds = sUserManager.getUserIds();
             synchronized (mInstallLock) {
                 // Clean up both app data and code
                 // All package moves are frozen until finished
-                try {
-                    mInstaller.destroyAppData(volumeUuid, move.packageName, UserHandle.USER_ALL,
-                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
-                } catch (InstallerException e) {
-                    Slog.w(TAG, String.valueOf(e));
+                for (int userId : userIds) {
+                    try {
+                        mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
+                                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, String.valueOf(e));
+                    }
                 }
                 removeCodePathLI(codeFile);
             }
@@ -17421,6 +17423,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
                 return true;
             }
         });
+
+        // Now that we're mostly running, clean up stale users and apps
+        reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
+        reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
     }
 
     @Override
@@ -18653,14 +18659,100 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
     }
 
     /**
+     * Prepare storage areas for given user on all mounted devices.
+     */
+    void prepareUserData(int userId, int userSerial, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+            }
+        }
+    }
+
+    private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+            boolean allowRecover) {
+        // Prepare storage and verify that serial numbers are consistent; if
+        // there's a mismatch we need to destroy to avoid leaking data
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
+            }
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
+            }
+
+            synchronized (mInstallLock) {
+                mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
+            }
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
+                    + " because we failed to prepare: " + e);
+            destroyUserDataLI(volumeUuid, userId, flags);
+
+            if (allowRecover) {
+                // Try one last time; if we fail again we're really in trouble
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
+            }
+        }
+    }
+
+    /**
+     * Destroy storage areas for given user on all mounted devices.
+     */
+    void destroyUserData(int userId, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                destroyUserDataLI(volumeUuid, userId, flags);
+            }
+        }
+    }
+
+    private void destroyUserDataLI(String volumeUuid, int userId, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            // Clean up app data, profile data, and media data
+            mInstaller.destroyUserData(volumeUuid, userId, flags);
+
+            // Clean up system data
+            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
+                }
+                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
+                }
+            }
+
+            // Data with special labels is now gone, so finish the job
+            storage.destroyUserStorage(volumeUuid, userId, flags);
+
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN,
+                    "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
+        }
+    }
+
+    /**
      * Examine all users present on given mounted volume, and destroy data
      * belonging to users that are no longer valid, or whose user ID has been
      * recycled.
      */
     private void reconcileUsers(String volumeUuid) {
-        // TODO: also reconcile DE directories
-        final File[] files = FileUtils
-                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
+        final List<File> files = new ArrayList<>();
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
         for (File file : files) {
             if (!file.isDirectory()) continue;
 
@@ -18691,11 +18783,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
 
             if (destroyUser) {
                 synchronized (mInstallLock) {
-                    try {
-                        mInstaller.removeUserDataDirs(volumeUuid, userId);
-                    } catch (InstallerException e) {
-                        Slog.w(TAG, "Failed to clean up user dirs", e);
-                    }
+                    destroyUserDataLI(volumeUuid, userId,
+                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                 }
             }
         }
@@ -19490,21 +19579,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
             mSettings.removeUserLPw(userHandle);
             mPendingBroadcasts.remove(userHandle);
             mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
-        }
-        synchronized (mInstallLock) {
-            final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
-                try {
-                    mInstaller.removeUserDataDirs(volumeUuid, userHandle);
-                } catch (InstallerException e) {
-                    Slog.w(TAG, "Failed to remove user data", e);
-                }
-            }
-            synchronized (mPackages) {
-                removeUnusedPackagesLILPw(userManager, userHandle);
-            }
+            removeUnusedPackagesLPw(userManager, userHandle);
         }
     }
 
@@ -19513,7 +19588,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
      * that are no longer in use by any other user.
      * @param userHandle the user being removed
      */
-    private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+    private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
         final boolean DEBUG_CLEAN_APKS = false;
         int [] users = userManager.getUserIds();
         Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
@@ -19563,11 +19638,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
     /** Called by UserManagerService */
     void createNewUser(int userHandle) {
         synchronized (mInstallLock) {
-            try {
-                mInstaller.createUserConfig(userHandle);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to create user config", e);
-            }
             mSettings.createNewUserLI(this, mInstaller, userHandle);
         }
         synchronized (mPackages) {
index 9b918f3..42f7166 100644 (file)
@@ -65,7 +65,6 @@ import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -2060,7 +2059,7 @@ public class UserManagerService extends IUserManager.Stub {
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
-            prepareUserStorage(userId, userInfo.serialNumber,
+            mPm.prepareUserData(userId, userInfo.serialNumber,
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             mPm.createNewUser(userId);
             userInfo.partial = false;
@@ -2300,9 +2299,9 @@ public class UserManagerService extends IUserManager.Stub {
             Slog.i(LOG_TAG,
                 "Destroying key for user " + userHandle + " failed, continuing anyway", e);
         }
+
         // Cleanup package manager settings
         mPm.cleanUpUser(this, userHandle);
-
         // Remove this user from the list
         synchronized (mUsersLock) {
             mUsers.remove(userHandle);
@@ -2322,24 +2321,12 @@ public class UserManagerService extends IUserManager.Stub {
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
         userFile.delete();
         updateUserIds();
-        File userDir = Environment.getUserSystemDirectory(userHandle);
-        File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
-        if (userDir.renameTo(renamedUserDir)) {
-            removeDirectoryRecursive(renamedUserDir);
-        } else {
-            removeDirectoryRecursive(userDir);
-        }
-    }
 
-    private void removeDirectoryRecursive(File parent) {
-        if (parent.isDirectory()) {
-            String[] files = parent.list();
-            for (String filename : files) {
-                File child = new File(parent, filename);
-                removeDirectoryRecursive(child);
-            }
-        }
-        parent.delete();
+        // Now that we've purged all the metadata above, destroy the actual data
+        // on disk; if we battery pull in here we'll finish cleaning up when
+        // reconciling after reboot.
+        mPm.destroyUserData(userHandle,
+                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
     }
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
@@ -2634,23 +2621,12 @@ public class UserManagerService extends IUserManager.Stub {
     }
 
     /**
-     * Prepare storage areas for given user on all mounted devices.
-     */
-    private void prepareUserStorage(int userId, int userSerial, int flags) {
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-            final String volumeUuid = vol.getFsUuid();
-            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
-        }
-    }
-
-    /**
      * Called right before a user is started. This gives us a chance to prepare
      * app storage and apply any user restrictions.
      */
     public void onBeforeStartUser(int userId) {
         final int userSerial = getUserSerialNumber(userId);
-        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
 
         if (userId != UserHandle.USER_SYSTEM) {
@@ -2666,7 +2642,7 @@ public class UserManagerService extends IUserManager.Stub {
      */
     public void onBeforeUnlockUser(@UserIdInt int userId) {
         final int userSerial = getUserSerialNumber(userId);
-        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
     }
 
index d1f1ffa..1686f14 100644 (file)
@@ -22,6 +22,7 @@ import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.res.Configuration.EMPTY;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -63,6 +64,7 @@ import android.content.pm.ResolveInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -2535,7 +2537,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
     @Override
     public View addStartingWindow(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
-            int icon, int logo, int windowFlags) {
+            int icon, int logo, int windowFlags, Configuration overrideConfig) {
         if (!SHOW_STARTING_ANIMATIONS) {
             return null;
         }
@@ -2560,10 +2562,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                 }
             }
 
-            PhoneWindow win = new PhoneWindow(context);
+            if (overrideConfig != null && overrideConfig != EMPTY) {
+                if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based"
+                        + " on overrideConfig" + overrideConfig + " for starting window");
+                final Context overrideContext = context.createConfigurationContext(overrideConfig);
+                overrideContext.setTheme(theme);
+                final TypedArray typedArray = overrideContext.obtainStyledAttributes(
+                        com.android.internal.R.styleable.Window);
+                final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
+                if (resId != 0 && overrideContext.getDrawable(resId) != null) {
+                    // We want to use the windowBackground for the override context if it is
+                    // available, otherwise we use the default one to make sure a themed starting
+                    // window is displayed for the app.
+                    if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig"
+                            + overrideConfig + " to starting window resId=" + resId);
+                    context = overrideContext;
+                }
+            }
+
+            final PhoneWindow win = new PhoneWindow(context);
             win.setIsStartingWindow(true);
 
-            Resources r = context.getResources();
+            final Resources r = context.getResources();
             win.setTitle(r.getText(labelRes, nonLocalizedLabel));
 
             win.setType(
@@ -2616,10 +2636,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
             wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
             view = win.getDecorView();
 
-            if (DEBUG_STARTING_WINDOW) Slog.d(
-                TAG, "Adding starting window for " + packageName
-                + " / " + appToken + ": "
-                + (view.getParent() != null ? view : null));
+            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for "
+                + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
 
             wm.addView(view, params);
 
index 49ff385..a9553a5 100644 (file)
@@ -19,10 +19,15 @@ import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
@@ -44,6 +49,7 @@ import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.utils.ManagedApplicationService.PendingEvent;
 import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
@@ -56,6 +62,7 @@ import java.lang.StringBuilder;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -85,6 +92,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC
 
     public static final String VR_MANAGER_BINDER_SERVICE = "vrmanager";
 
+    private static final int PENDING_STATE_DELAY_MS = 300;
+
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
 
@@ -107,8 +116,10 @@ public class VrManagerService extends SystemService implements EnabledComponentC
     private String mPreviousNotificationPolicyAccessPackage;
     private String mPreviousCoarseLocationPackage;
     private String mPreviousManageOverlayPackage;
+    private VrState mPendingState;
 
     private static final int MSG_VR_STATE_CHANGE = 0;
+    private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -127,12 +138,32 @@ public class VrManagerService extends SystemService implements EnabledComponentC
                     }
                     mRemoteCallbacks.finishBroadcast();
                 } break;
+                case MSG_PENDING_VR_STATE_CHANGE : {
+                    synchronized(mLock) {
+                        VrManagerService.this.consumeAndApplyPendingStateLocked();
+                    }
+                } break;
                 default :
                     throw new IllegalStateException("Unknown message type: " + msg.what);
             }
         }
     };
 
+    private static class VrState {
+        final boolean enabled;
+        final int userId;
+        final ComponentName targetPackageName;
+        final ComponentName callingPackage;
+
+        VrState(boolean enabled, ComponentName targetPackageName, int userId,
+                ComponentName callingPackage) {
+            this.enabled = enabled;
+            this.userId = userId;
+            this.targetPackageName = targetPackageName;
+            this.callingPackage = callingPackage;
+        }
+    };
+
     private static final BinderChecker sBinderChecker = new BinderChecker() {
         @Override
         public IInterface asInterface(IBinder binder) {
@@ -156,6 +187,13 @@ public class VrManagerService extends SystemService implements EnabledComponentC
                 return; // No active services
             }
 
+            // If there is a pending state change, we'd better deal with that first
+            consumeAndApplyPendingStateLocked();
+
+            if (mCurrentVrService == null) {
+                return; // No active services
+            }
+
             // There is an active service, update it if needed
             updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
                     mCurrentVrService.getUserId(), null);
@@ -258,6 +296,107 @@ public class VrManagerService extends SystemService implements EnabledComponentC
 
         publishLocalService(VrManagerInternal.class, new LocalService());
         publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
+
+        // If there are no VR packages installed on the device, then disable VR
+        // components, otherwise, enable them.
+        setEnabledStatusOfVrComponents();
+    }
+
+    private void setEnabledStatusOfVrComponents() {
+        ArraySet<ComponentName> vrComponents = SystemConfig.getInstance().getDefaultVrComponents();
+        if (vrComponents == null) {
+           return;
+        }
+
+        // We only want to enable VR components if there is a VR package installed on the device.
+        // The VR components themselves do not quality as a VR package, so exclude them.
+        ArraySet<String> vrComponentPackageNames = new ArraySet<>();
+        for (ComponentName componentName : vrComponents) {
+            vrComponentPackageNames.add(componentName.getPackageName());
+        }
+
+        // Check to see if there are any packages on the device, other than the VR component
+        // packages.
+        PackageManager pm = mContext.getPackageManager();
+        List<PackageInfo> packageInfos = pm.getInstalledPackages(
+                PackageManager.GET_CONFIGURATIONS);
+        boolean vrModeIsUsed = false;
+        for (PackageInfo packageInfo : packageInfos) {
+            if (packageInfo != null && packageInfo.packageName != null &&
+                    pm.getApplicationEnabledSetting(packageInfo.packageName) ==
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                vrModeIsUsed = enableVrComponentsIfVrModeUsed(pm, packageInfo,
+                        vrComponentPackageNames, vrComponents);
+                if (vrModeIsUsed) {
+                    break;
+                }
+            }
+        }
+
+        if (!vrModeIsUsed) {
+            Slog.i(TAG, "No VR packages found, disabling VR components");
+            setVrComponentsEnabledOrDisabled(vrComponents, false);
+
+            // Register to receive an intent when a new package is installed, in case that package
+            // requires VR components.
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            intentFilter.addDataScheme("package");
+            mContext.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    PackageManager pm = context.getPackageManager();
+                    final String packageName = intent.getData().getSchemeSpecificPart();
+                    if (packageName != null) {
+                        try {
+                            PackageInfo packageInfo = pm.getPackageInfo(packageName,
+                                    PackageManager.GET_CONFIGURATIONS);
+                            enableVrComponentsIfVrModeUsed(pm, packageInfo,
+                                    vrComponentPackageNames, vrComponents);
+                        } catch (NameNotFoundException e) {
+                        }
+                    }
+                };
+            }, intentFilter);
+        }
+    }
+
+    private void setVrComponentsEnabledOrDisabled(ArraySet<ComponentName> vrComponents,
+            boolean enabled) {
+        int state = enabled ?
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+        PackageManager pm = mContext.getPackageManager();
+        for (ComponentName componentName : vrComponents) {
+            try {
+                // Note that we must first check for the existance of the package before trying
+                // to set its enabled state.  This is to prevent PackageManager from throwing
+                // an excepton if the package is not found (not just a NameNotFoundException
+                // exception).
+                PackageInfo packageInfo = pm.getPackageInfo(componentName.getPackageName(),
+                        PackageManager.GET_CONFIGURATIONS);
+                pm.setApplicationEnabledSetting(componentName.getPackageName(), state , 0);
+            } catch (NameNotFoundException e) {
+            }
+        }
+    }
+
+    private boolean enableVrComponentsIfVrModeUsed(PackageManager pm, PackageInfo packageInfo,
+            ArraySet<String> vrComponentPackageNames, ArraySet<ComponentName> vrComponents) {
+        boolean isVrComponent = vrComponents != null &&
+                vrComponentPackageNames.contains(packageInfo.packageName);
+        if (packageInfo != null && packageInfo.reqFeatures != null && !isVrComponent) {
+            for (FeatureInfo featureInfo : packageInfo.reqFeatures) {
+                if (featureInfo.name != null &&
+                    (featureInfo.name.equals(PackageManager.FEATURE_VR_MODE) ||
+                     featureInfo.name.equals(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE))) {
+                    Slog.i(TAG, "VR package found, enabling VR components");
+                    setVrComponentsEnabledOrDisabled(vrComponents, true);
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     @Override
@@ -679,14 +818,40 @@ public class VrManagerService extends SystemService implements EnabledComponentC
                 sBinderChecker);
     }
 
+    private void consumeAndApplyPendingStateLocked() {
+        if (mPendingState != null) {
+            updateCurrentVrServiceLocked(mPendingState.enabled,
+                    mPendingState.targetPackageName, mPendingState.userId,
+                    mPendingState.callingPackage);
+            mPendingState = null;
+        }
+    }
+
     /*
      * Implementation of VrManagerInternal calls.  These are callable from system services.
      */
 
-    private boolean setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
+    private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
             int userId, @NonNull ComponentName callingPackage) {
+
         synchronized (mLock) {
-            return updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
+
+            if (!enabled && mCurrentVrService != null) {
+                // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
+                // and service bind/unbind in case we are immediately switching to another VR app.
+                if (mPendingState == null) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PENDING_VR_STATE_CHANGE,
+                            PENDING_STATE_DELAY_MS);
+                }
+
+                mPendingState = new VrState(enabled, targetPackageName, userId, callingPackage);
+                return;
+            } else {
+                mHandler.removeMessages(MSG_PENDING_VR_STATE_CHANGE);
+                mPendingState = null;
+            }
+
+            updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
         }
     }
 
index c240d07..52146c2 100644 (file)
@@ -149,8 +149,10 @@ class DimLayerController {
             if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
                 // Dim should cover the entire screen for system windows.
                 mDisplayContent.getLogicalDisplayRect(mTmpBounds);
-                state.dimLayer.setBounds(mTmpBounds);
+            } else {
+                dimLayerUser.getDimBounds(mTmpBounds);
             }
+            state.dimLayer.setBounds(mTmpBounds);
         }
     }
 
index 3bd7a96..0039c0a 100644 (file)
@@ -27,6 +27,7 @@ import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -84,9 +85,12 @@ public class DockedStackDividerController implements DimLayerUser {
 
     private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
 
+    private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private int mDividerWindowWidth;
+    private int mDividerWindowWidthInactive;
     private int mDividerInsets;
     private boolean mResizing;
     private WindowState mWindow;
@@ -130,6 +134,8 @@ public class DockedStackDividerController implements DimLayerUser {
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         mDividerInsets = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_insets);
+        mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
+                DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
     }
 
     void onConfigurationChanged() {
@@ -148,6 +154,10 @@ public class DockedStackDividerController implements DimLayerUser {
         return mDividerInsets;
     }
 
+    int getContentWidthInactive() {
+        return mDividerWindowWidthInactive;
+    }
+
     void setResizing(boolean resizing) {
         if (mResizing != resizing) {
             mResizing = resizing;
@@ -276,6 +286,9 @@ public class DockedStackDividerController implements DimLayerUser {
     }
 
     void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+        mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
+        mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
+                minimizedDock ? 1 : 0, 0).sendToTarget();
         final int size = mDockedStackListeners.beginBroadcast();
         for (int i = 0; i < size; ++i) {
             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -355,6 +368,12 @@ public class DockedStackDividerController implements DimLayerUser {
             return;
         }
 
+        // If the app that having visibility change is not the top visible one in the task,
+        // it does not affect whether the docked stack is minimized, ignore it.
+        if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
+            return;
+        }
+
         // If the stack is completely offscreen, this might just be an intermediate state when
         // docking a task/launching recents at the same time, but home doesn't actually get
         // visible after the state settles in.
@@ -644,4 +663,4 @@ public class DockedStackDividerController implements DimLayerUser {
     public String toShortString() {
         return TAG;
     }
-}
+}
\ No newline at end of file
index c322cd8..872bc6d 100644 (file)
@@ -53,6 +53,12 @@ import java.util.ArrayList;
 
 public class TaskStack implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
+    /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+     * restrict IME adjustment so that a min portion of top stack remains visible.*/
+    private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+    /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
+    private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
 
     /** Unique identifier */
     final int mStackId;
@@ -249,7 +255,12 @@ public class TaskStack implements DimLayer.DimLayerUser,
                 task.scrollLocked(mTmpRect2);
             } else if (task.isResizeable() && task.mOverrideConfig != Configuration.EMPTY) {
                 task.getBounds(mTmpRect2);
-                mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+                if (mAdjustedForIme && getDockSide() == DOCKED_TOP) {
+                    int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
+                    mTmpRect2.offset(0, offsetY);
+                } else {
+                    mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+                }
                 task.setTempInsetBounds(tempInsetBounds);
                 task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
             }
@@ -882,6 +893,7 @@ public class TaskStack implements DimLayer.DimLayerUser,
             mImeGoingAway = false;
             mAdjustImeAmount = 0f;
             updateAdjustedBounds();
+            mService.setResizeDimLayer(false, mStackId, 1.0f);
         } else {
             mImeGoingAway |= mAdjustedForIme;
         }
@@ -930,6 +942,11 @@ public class TaskStack implements DimLayer.DimLayerUser,
         }
     }
 
+    int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
+        return displayContentRect.top + (int)
+                ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+    }
+
     private boolean adjustForIME(final WindowState imeWin) {
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
@@ -953,23 +970,41 @@ public class TaskStack implements DimLayer.DimLayerUser,
         mLastContentBounds.set(contentBounds);
         final int yOffset = displayContentRect.bottom - contentBounds.bottom;
 
+        final int dividerWidth =
+                getDisplayContent().mDividerControllerLocked.getContentWidth();
+        final int dividerWidthInactive =
+                getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
+
         if (dockedSide == DOCKED_TOP) {
             // If this stack is docked on top, we make it smaller so the bottom stack is not
-            // occluded by IME. We shift its bottom up by the height of the IME (capped by
-            // the display content rect). Note that we don't change the task bounds.
-            int bottom = Math.max(
-                    mBounds.bottom - yOffset, displayContentRect.top);
+            // occluded by IME. We shift its bottom up by the height of the IME, but
+            // leaves at least 30% of the top stack visible.
+            final int minTopStackBottom =
+                    getMinTopStackBottom(displayContentRect, mBounds.bottom);
+            final int bottom = Math.max(
+                    mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive,
+                    minTopStackBottom);
             mTmpAdjustedBounds.set(mBounds);
             mTmpAdjustedBounds.bottom =
                     (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
             mFullyAdjustedImeBounds.set(mBounds);
         } else {
-            // If this stack is docked on bottom, we shift it up so that it's not occluded by
-            // IME. We try to move it up by the height of the IME window (although the best
-            // we could do is to make the top stack fully collapsed).
-            final int dividerWidth = getDisplayContent().mDividerControllerLocked
-                    .getContentWidth();
-            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
+            final int top;
+            final boolean isFocusedStack = mService.getFocusedStackLocked() == this;
+            if (isFocusedStack) {
+                // If this stack is docked on bottom and has focus, we shift it up so that it's not
+                // occluded by IME. We try to move it up by the height of the IME window, but only
+                // to the extent that leaves at least 30% of the top stack visible.
+                final int minTopStackBottom =
+                        getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth);
+                top = Math.max(
+                        mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive);
+            } else {
+                // If this stack is docked on bottom but doesn't have focus, we don't need to adjust
+                // for IME, but still need to apply a small adjustment due to the thinner divider.
+                top = mBounds.top - dividerWidth + dividerWidthInactive;
+            }
+
             mTmpAdjustedBounds.set(mBounds);
             mTmpAdjustedBounds.top =
                     (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
@@ -1043,6 +1078,12 @@ public class TaskStack implements DimLayer.DimLayerUser,
             mLastContentBounds.setEmpty();
         }
         setAdjustedBounds(mTmpAdjustedBounds);
+
+        final boolean isFocusedStack = mService.getFocusedStackLocked() == this;
+        if (mAdjustedForIme && adjust && !isFocusedStack) {
+            final float alpha = mAdjustImeAmount * IME_ADJUST_DIM_AMOUNT;
+            mService.setResizeDimLayer(true, mStackId, alpha);
+        }
     }
 
     boolean isAdjustedForMinimizedDockedStack() {
index 0217c09..ed1f428 100644 (file)
@@ -203,6 +203,10 @@ public class WindowLayersController {
         if (mDockDivider != null && mDockDivider.isVisibleLw()
                 && mService.mInputMethodWindow != null) {
             layer = assignAndIncreaseLayerIfNeeded(mService.mInputMethodWindow, layer);
+            for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
+                final WindowState dialog = mService.mInputMethodDialogs.get(i);
+                layer = assignAndIncreaseLayerIfNeeded(dialog, layer);
+            }
         }
 
         // We know that we will be animating a relaunching window in the near future, which will
index 2e0c187..79ff78e 100644 (file)
@@ -7445,17 +7445,16 @@ public class WindowManagerService extends IWindowManager.Stub
 
     void adjustForImeIfNeeded(final DisplayContent displayContent) {
         final WindowState imeWin = mInputMethodWindow;
-        final TaskStack focusedStack =
-                mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+        final TaskStack focusedStack = getFocusedStackLocked();
         final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
         if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
-                && dockVisible
-                && focusedStack != null
-                && focusedStack.getDockSide() == DOCKED_BOTTOM){
+                && dockVisible && focusedStack != null) {
+            final boolean isFocusOnBottom = focusedStack.getDockSide() == DOCKED_BOTTOM;
             final ArrayList<TaskStack> stacks = displayContent.getStacks();
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 final TaskStack stack = stacks.get(i);
-                if (stack.isVisibleLocked()) {
+                final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
+                if (stack.isVisibleLocked() && (isFocusOnBottom || isDockedOnBottom)) {
                     stack.setAdjustedForIme(imeWin);
                 }
             }
@@ -7599,6 +7598,10 @@ public class WindowManagerService extends IWindowManager.Stub
         return mCurrentFocus;
     }
 
+    TaskStack getFocusedStackLocked() {
+        return mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+    }
+
     private void showAuditSafeModeNotification() {
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
                 new Intent(Intent.ACTION_VIEW,
@@ -7800,6 +7803,8 @@ public class WindowManagerService extends IWindowManager.Stub
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_REMOVE_TIMEOUT = 52;
 
+        public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
+
         /**
          * Used to denote that an integer field in a message will not be used.
          */
@@ -7897,9 +7902,11 @@ public class WindowManagerService extends IWindowManager.Stub
 
                     View view = null;
                     try {
-                        view = mPolicy.addStartingWindow(
-                            wtoken.token, sd.pkg, sd.theme, sd.compatInfo,
-                            sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo, sd.windowFlags);
+                        final Configuration overrideConfig = wtoken != null && wtoken.mTask != null
+                                ? wtoken.mTask.mOverrideConfig : null;
+                        view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
+                            sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
+                            sd.windowFlags, overrideConfig);
                     } catch (Exception e) {
                         Slog.w(TAG_WM, "Exception when adding starting window", e);
                     }
@@ -8423,6 +8430,10 @@ public class WindowManagerService extends IWindowManager.Stub
                     }
                 }
                 break;
+                case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
+                    mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
@@ -9333,6 +9344,7 @@ public class WindowManagerService extends IWindowManager.Stub
         WindowState newFocus = computeFocusedWindowLocked();
         if (mCurrentFocus != newFocus) {
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+            TaskStack oldFocusedStack = getFocusedStackLocked();
             // This check makes sure that we don't already have the focus
             // change message pending.
             mH.removeMessages(H.REPORT_FOCUS_CHANGE);
@@ -9354,6 +9366,7 @@ public class WindowManagerService extends IWindowManager.Stub
             mLosingFocus.remove(newFocus);
 
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
+            TaskStack newFocusedStack = getFocusedStackLocked();
 
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
                 // Focus of the input method window changed. Perform layout if needed.
@@ -9383,6 +9396,20 @@ public class WindowManagerService extends IWindowManager.Stub
                 mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
             }
 
+            // TODO: Reset and re-apply IME adjustment if needed when stack focus changed.
+            // This makes sure divider starts an animation from pre-adjust position to final
+            // position. Ideally we want to skip the reset and animation from current position
+            // directly to final position.
+            final WindowState imeWin = mInputMethodWindow;
+            if (oldFocusedStack != null) {
+                oldFocusedStack.resetAdjustedForIme(true);
+            }
+            if (newFocusedStack != null) {
+                newFocusedStack.resetAdjustedForIme(true);
+            }
+            displayContent.mDividerControllerLocked.setAdjustedForIme(false, false, imeWin);
+            adjustForImeIfNeeded(displayContent);
+
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             return true;
         }
index bf69717..5077f32 100644 (file)
@@ -2608,7 +2608,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
         final int ph = containingFrame.height();
         final Task task = getTask();
         final boolean nonFullscreenTask = isInMultiWindowMode();
-        final boolean fitToDisplay = task != null && !nonFullscreenTask && !layoutInParentFrame();
+        final boolean fitToDisplay = (task == null || !nonFullscreenTask) && !layoutInParentFrame();
         float x, y;
         int w,h;
 
index 6722332..5a10275 100644 (file)
@@ -107,6 +107,7 @@ public class ApfFilter {
     private static final boolean VDBG = false;
 
     private static final int ETH_HEADER_LEN = 14;
+    private static final int ETH_DEST_ADDR_OFFSET = 0;
     private static final int ETH_ETHERTYPE_OFFSET = 12;
     private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
             (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
@@ -582,7 +583,6 @@ public class ApfFilter {
      * DROP_LABEL or PASS_LABEL and does not fall off the end.
      * Preconditions:
      *  - Packet being filtered is IPv4
-     *  - R1 is initialized to 0
      */
     @GuardedBy("this")
     private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
@@ -605,9 +605,8 @@ public class ApfFilter {
 
         // Drop all broadcasts besides DHCP addressed to us
         // If not a broadcast packet, pass
-        // NOTE: Relies on R1 being initialized to 0 which is the offset of the ethernet
-        //       destination MAC address
-        gen.addJumpIfBytesNotEqual(Register.R1, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+        gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
+        gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
         // If not UDP, drop
         gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
         gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
index 96c2ba5..d41fbce 100644 (file)
@@ -734,7 +734,11 @@ public class ApfGenerator {
      * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
      * packet at, an offset specified by {@code register}, match {@code bytes}.
      */
-    public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target) {
+    public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
+            throws IllegalInstructionException {
+        if (register == Register.R1) {
+            throw new IllegalInstructionException("JNEBS fails with R1");
+        }
         Instruction instruction = new Instruction(Opcodes.JNEBS, register);
         instruction.setUnsignedImm(bytes.length);
         instruction.setTargetLabel(target);
index 53377f9..612e5e8 100644 (file)
@@ -188,13 +188,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             }
 
             // Process existing model first.
-            if (model != null && model.getModelId() != soundModel.uuid) {
+            if (model != null && !model.getModelId().equals(soundModel.uuid)) {
                 // The existing model has a different UUID, should be replaced.
                 int status = cleanUpExistingKeyphraseModel(model);
-                removeKeyphraseModelLocked(keyphraseId);
                 if (status != STATUS_OK) {
                     return status;
                 }
+                removeKeyphraseModelLocked(keyphraseId);
                 model = null;
             }
 
@@ -478,8 +478,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             } else {
                 // Clear the ModelData state if successful.
                 modelData.clearState();
-                modelData.clearCallback();
-                modelData.setRecognitionConfig(null);
             }
         }
         return status;
@@ -498,15 +496,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             // Stop all recognition models.
             for (ModelData model : mModelDataMap.values()) {
                 if (model.isModelStarted()) {
-                    model.setRequested(false);
                     int status = stopRecognitionLocked(model,
                             false /* do not notify for synchronous calls */);
                     if (status != STATUS_OK) {
                         Slog.w(TAG, "Error stopping keyphrase model: " + model.getHandle());
                     }
                     model.clearState();
-                    model.clearCallback();
-                    model.setRecognitionConfig(null);
                 }
             }
             internalClearGlobalStateLocked();
@@ -849,7 +844,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
     private void internalClearModelStateLocked() {
         for (ModelData modelData : mModelDataMap.values()) {
             modelData.clearState();
-            modelData.clearCallback();
         }
     }
 
@@ -1194,6 +1188,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
         synchronized void clearState() {
             mModelState = MODEL_NOTLOADED;
             mModelHandle = INVALID_VALUE;
+            mRecognitionConfig = null;
+            mRequested = false;
+            mCallback = null;
         }
 
         synchronized void clearCallback() {
index 9c15f2b..0f68cca 100644 (file)
@@ -252,7 +252,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
         String[] usersStr = text.split(",");
         int[] users = new int[usersStr.length];
         for (int i = 0; i < usersStr.length; i++) {
-            users[i] = Integer.valueOf(usersStr[i]);
+            users[i] = Integer.parseInt(usersStr[i]);
         }
         return users;
     }
index f5c58c5..da0d048 100644 (file)
@@ -225,6 +225,30 @@ public class TelecomManager {
             "android.telecom.extra.UNKNOWN_CALL_HANDLE";
 
     /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the time the
+     * call was created. This value is in milliseconds since boot.
+     * @hide
+     */
+    public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
+            "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
+
+    /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the time
+     * telecom began routing the call. This value is in milliseconds since boot.
+     * @hide
+     */
+    public static final String EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS =
+            "android.telecom.extra.CALL_TELECOM_ROUTING_START_TIME_MILLIS";
+
+    /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the time
+     * telecom finished routing the call. This value is in milliseconds since boot.
+     * @hide
+     */
+    public static final String EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS =
+            "android.telecom.extra.CALL_TELECOM_ROUTING_END_TIME_MILLIS";
+
+    /**
      * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
      * containing the disconnect code.
      */
index 26730de..5ac697f 100644 (file)
@@ -547,6 +547,13 @@ public class CarrierConfigManager {
      */
     public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
 
+     /**
+      * Flag to indicate if Wi-Fi needs to be disabled in ECBM
+      * @hide
+      **/
+     public static final String
+              KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+
     /**
      * List operator-specific error codes and indices of corresponding error strings in
      * wfcOperatorErrorAlertMessages and wfcOperatorErrorNotificationMessages.
@@ -755,6 +762,7 @@ public class CarrierConfigManager {
         sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
         sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
         sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
index 0f9a3b9..25851e3 100644 (file)
@@ -135,8 +135,8 @@ public class NeighboringCellInfo implements Parcelable
                 mNetworkType = radioType;
                 // check if 0xFFFFFFFF for UNKNOWN_CID
                 if (!location.equalsIgnoreCase("FFFFFFFF")) {
-                    mCid = Integer.valueOf(location.substring(4), 16);
-                    mLac = Integer.valueOf(location.substring(0, 4), 16);
+                    mCid = Integer.parseInt(location.substring(4), 16);
+                    mLac = Integer.parseInt(location.substring(0, 4), 16);
                 }
                 break;
             case NETWORK_TYPE_UMTS:
@@ -144,7 +144,7 @@ public class NeighboringCellInfo implements Parcelable
             case NETWORK_TYPE_HSUPA:
             case NETWORK_TYPE_HSPA:
                 mNetworkType = radioType;
-                mPsc = Integer.valueOf(location, 16);
+                mPsc = Integer.parseInt(location, 16);
                 break;
             }
         } catch (NumberFormatException e) {
index ff8c71c..f3b0ce2 100644 (file)
@@ -1347,6 +1347,7 @@ public class SubscriptionManager {
         if (subInfo != null) {
             newConfig.mcc = subInfo.getMcc();
             newConfig.mnc = subInfo.getMnc();
+            if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
         }
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         DisplayMetrics newMetrics = new DisplayMetrics();
index 0bca628..394fc27 100644 (file)
@@ -25,6 +25,8 @@ import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.BatteryStats;
+import android.os.ResultReceiver;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.os.Bundle;
@@ -73,6 +75,14 @@ import java.util.regex.Pattern;
 public class TelephonyManager {
     private static final String TAG = "TelephonyManager";
 
+    /**
+     * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
+     * into the ResultReceiver Bundle.
+     * @hide
+     */
+    public static final String MODEM_ACTIVITY_RESULT_KEY =
+            BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
+
     private static ITelephonyRegistry sRegistry;
 
     /**
@@ -5051,19 +5061,23 @@ public class TelephonyManager {
     }
 
     /**
-     * Returns the modem activity info.
+     * Requests the modem activity info. The recipient will place the result
+     * in `result`.
+     * @param result The object on which the recipient will send the resulting
+     * {@link android.telephony.ModemActivityInfo} object.
      * @hide
      */
-    public ModemActivityInfo getModemActivityInfo() {
+    public void requestModemActivityInfo(ResultReceiver result) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getModemActivityInfo();
+                service.requestModemActivityInfo(result);
+                return;
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
         }
-        return null;
+        result.send(0, null);
     }
 
     /**
index b41d361..bb8aaad 100644 (file)
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.ResultReceiver;
 import android.net.Uri;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -1027,9 +1028,13 @@ interface ITelephony {
     String getLocaleFromDefaultSim();
 
     /**
-     * Return the modem activity info.
+     * Requests the modem activity info asynchronously.
+     * The implementor is expected to reply with the
+     * {@link android.telephony.ModemActivityInfo} object placed into the Bundle with the key
+     * {@link android.telephony.TelephonyManager#MODEM_ACTIVITY_RESULT_KEY}.
+     * The result code is ignored.
      */
-    ModemActivityInfo getModemActivityInfo();
+    oneway void requestModemActivityInfo(in ResultReceiver result);
 
     /**
      * Get the service state on specified subscription
index 907d76e..2c6be62 100644 (file)
@@ -37,10 +37,10 @@ interface ITelephonyRegistry {
     void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events,
             boolean notifyNow);
     void notifyCallState(int state, String incomingNumber);
-    void notifyCallStateForSubscriber(in int subId, int state, String incomingNumber);
+    void notifyCallStateForPhoneId(in int phoneId, in int subId, int state, String incomingNumber);
     void notifyServiceStateForPhoneId(in int phoneId, in int subId, in ServiceState state);
-    void notifySignalStrength(in SignalStrength signalStrength);
-    void notifySignalStrengthForSubscriber(in int subId, in SignalStrength signalStrength);
+    void notifySignalStrengthForPhoneId(in int phoneId, in int subId,
+            in SignalStrength signalStrength);
     void notifyMessageWaitingChangedForPhoneId(in int phoneId, in int subId, in boolean mwi);
     void notifyCallForwardingChanged(boolean cfi);
     void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
index f73e6ff..2a016cf 100644 (file)
@@ -781,7 +781,7 @@ class TestWebServer implements HttpConstants {
 
 
             if (testID.startsWith("test")) {
-                testNum = Integer.valueOf(testID.substring(4))-1;
+                testNum = Integer.parseInt(testID.substring(4))-1;
             }
 
             if ((testNum < 0) || (testNum > TestWebData.tests.length - 1)) {
index e0d8249..3ac1889 100644 (file)
@@ -192,7 +192,9 @@ public class BridgeRenderSession extends RenderSession {
 
     @Override
     public void setElapsedFrameTimeNanos(long nanos) {
-        mSession.setElapsedFrameTimeNanos(nanos);
+        if (mSession != null) {
+            mSession.setElapsedFrameTimeNanos(nanos);
+        }
     }
 
     @Override
index 3ef568c..834ad74 100644 (file)
@@ -134,6 +134,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
     private List<ViewInfo> mViewInfoList;
     private List<ViewInfo> mSystemViewInfoList;
     private Layout.Builder mLayoutBuilder;
+    private boolean mNewRenderSize;
 
     private static final class PostInflateException extends Exception {
         private static final long serialVersionUID = 1L;
@@ -200,6 +201,88 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
     }
 
     /**
+     * Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
+     */
+    private void measure(@NonNull SessionParams params) {
+        // only do the screen measure when needed.
+        if (mMeasuredScreenWidth != -1) {
+            return;
+        }
+
+        RenderingMode renderingMode = params.getRenderingMode();
+        HardwareConfig hardwareConfig = params.getHardwareConfig();
+
+        mNewRenderSize = true;
+        mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
+        mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
+
+        if (renderingMode != RenderingMode.NORMAL) {
+            int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
+                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                    : MeasureSpec.EXACTLY;
+            int heightMeasureSpecMode = renderingMode.isVertExpand() ?
+                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                    : MeasureSpec.EXACTLY;
+
+            // We used to compare the measured size of the content to the screen size but
+            // this does not work anymore due to the 2 following issues:
+            // - If the content is in a decor (system bar, title/action bar), the root view
+            //   will not resize even with the UNSPECIFIED because of the embedded layout.
+            // - If there is no decor, but a dialog frame, then the dialog padding prevents
+            //   comparing the size of the content to the screen frame (as it would not
+            //   take into account the dialog padding).
+
+            // The solution is to first get the content size in a normal rendering, inside
+            // the decor or the dialog padding.
+            // Then measure only the content with UNSPECIFIED to see the size difference
+            // and apply this to the screen size.
+
+            // first measure the full layout, with EXACTLY to get the size of the
+            // content as it is inside the decor/dialog
+            @SuppressWarnings("deprecation")
+            Pair<Integer, Integer> exactMeasure = measureView(
+                    mViewRoot, mContentRoot.getChildAt(0),
+                    mMeasuredScreenWidth, MeasureSpec.EXACTLY,
+                    mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+
+            // now measure the content only using UNSPECIFIED (where applicable, based on
+            // the rendering mode). This will give us the size the content needs.
+            @SuppressWarnings("deprecation")
+            Pair<Integer, Integer> result = measureView(
+                    mContentRoot, mContentRoot.getChildAt(0),
+                    mMeasuredScreenWidth, widthMeasureSpecMode,
+                    mMeasuredScreenHeight, heightMeasureSpecMode);
+
+            // now look at the difference and add what is needed.
+            if (renderingMode.isHorizExpand()) {
+                int measuredWidth = exactMeasure.getFirst();
+                int neededWidth = result.getFirst();
+                if (neededWidth > measuredWidth) {
+                    mMeasuredScreenWidth += neededWidth - measuredWidth;
+                }
+                if (mMeasuredScreenWidth < measuredWidth) {
+                    // If the screen width is less than the exact measured width,
+                    // expand to match.
+                    mMeasuredScreenWidth = measuredWidth;
+                }
+            }
+
+            if (renderingMode.isVertExpand()) {
+                int measuredHeight = exactMeasure.getSecond();
+                int neededHeight = result.getSecond();
+                if (neededHeight > measuredHeight) {
+                    mMeasuredScreenHeight += neededHeight - measuredHeight;
+                }
+                if (mMeasuredScreenHeight < measuredHeight) {
+                    // If the screen height is less than the exact measured height,
+                    // expand to match.
+                    mMeasuredScreenHeight = measuredHeight;
+                }
+            }
+        }
+    }
+
+    /**
      * Inflates the layout.
      * <p>
      * {@link #acquire(long)} must have been called before this.
@@ -245,6 +328,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
 
             setActiveToolbar(view, context, params);
 
+            measure(params);
+            measureView(mViewRoot, null /*measuredView*/,
+                    mMeasuredScreenWidth, MeasureSpec.EXACTLY,
+                    mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+            mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+            mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
+                    false);
+
             return SUCCESS.createResult();
         } catch (PostInflateException e) {
             return ERROR_INFLATION.createResult(e.getMessage(), e);
@@ -320,82 +411,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
                 return ERROR_NOT_INFLATED.createResult();
             }
 
-            RenderingMode renderingMode = params.getRenderingMode();
-            HardwareConfig hardwareConfig = params.getHardwareConfig();
-
-            // only do the screen measure when needed.
-            boolean newRenderSize = false;
-            if (mMeasuredScreenWidth == -1) {
-                newRenderSize = true;
-                mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
-                mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
-
-                if (renderingMode != RenderingMode.NORMAL) {
-                    int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
-                            MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
-                            : MeasureSpec.EXACTLY;
-                    int heightMeasureSpecMode = renderingMode.isVertExpand() ?
-                            MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
-                            : MeasureSpec.EXACTLY;
-
-                    // We used to compare the measured size of the content to the screen size but
-                    // this does not work anymore due to the 2 following issues:
-                    // - If the content is in a decor (system bar, title/action bar), the root view
-                    //   will not resize even with the UNSPECIFIED because of the embedded layout.
-                    // - If there is no decor, but a dialog frame, then the dialog padding prevents
-                    //   comparing the size of the content to the screen frame (as it would not
-                    //   take into account the dialog padding).
-
-                    // The solution is to first get the content size in a normal rendering, inside
-                    // the decor or the dialog padding.
-                    // Then measure only the content with UNSPECIFIED to see the size difference
-                    // and apply this to the screen size.
-
-                    // first measure the full layout, with EXACTLY to get the size of the
-                    // content as it is inside the decor/dialog
-                    @SuppressWarnings("deprecation")
-                    Pair<Integer, Integer> exactMeasure = measureView(
-                            mViewRoot, mContentRoot.getChildAt(0),
-                            mMeasuredScreenWidth, MeasureSpec.EXACTLY,
-                            mMeasuredScreenHeight, MeasureSpec.EXACTLY);
-
-                    // now measure the content only using UNSPECIFIED (where applicable, based on
-                    // the rendering mode). This will give us the size the content needs.
-                    @SuppressWarnings("deprecation")
-                    Pair<Integer, Integer> result = measureView(
-                            mContentRoot, mContentRoot.getChildAt(0),
-                            mMeasuredScreenWidth, widthMeasureSpecMode,
-                            mMeasuredScreenHeight, heightMeasureSpecMode);
-
-                    // now look at the difference and add what is needed.
-                    if (renderingMode.isHorizExpand()) {
-                        int measuredWidth = exactMeasure.getFirst();
-                        int neededWidth = result.getFirst();
-                        if (neededWidth > measuredWidth) {
-                            mMeasuredScreenWidth += neededWidth - measuredWidth;
-                        }
-                        if (mMeasuredScreenWidth < measuredWidth) {
-                            // If the screen width is less than the exact measured width,
-                            // expand to match.
-                            mMeasuredScreenWidth = measuredWidth;
-                        }
-                    }
-
-                    if (renderingMode.isVertExpand()) {
-                        int measuredHeight = exactMeasure.getSecond();
-                        int neededHeight = result.getSecond();
-                        if (neededHeight > measuredHeight) {
-                            mMeasuredScreenHeight += neededHeight - measuredHeight;
-                        }
-                        if (mMeasuredScreenHeight < measuredHeight) {
-                            // If the screen height is less than the exact measured height,
-                            // expand to match.
-                            mMeasuredScreenHeight = measuredHeight;
-                        }
-                    }
-                }
-            }
+            measure(params);
 
+            HardwareConfig hardwareConfig = params.getHardwareConfig();
             Result renderResult = SUCCESS.createResult();
             if (params.isLayoutOnly()) {
                 // delete the canvas and image to reset them on the next full rendering
@@ -412,7 +430,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
                 // it doesn't get cached.
                 boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
                     RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
-                if (newRenderSize || mCanvas == null || disableBitmapCaching) {
+                if (mNewRenderSize || mCanvas == null || disableBitmapCaching) {
+                    mNewRenderSize = false;
                     if (params.getImageFactory() != null) {
                         mImage = params.getImageFactory().getImage(
                                 mMeasuredScreenWidth,
index be5b8fc..9268a2b 100644 (file)
@@ -27,8 +27,8 @@ import android.net.Network;
 
 import android.net.DhcpInfo;
 
-
 import android.os.Messenger;
+import android.os.ResultReceiver;
 import android.os.WorkSource;
 
 /**
@@ -42,6 +42,14 @@ interface IWifiManager
 
     WifiActivityEnergyInfo reportActivityInfo();
 
+    /**
+     * Requests the controller activity info asynchronously.
+     * The implementor is expected to reply with the
+     * {@link android.net.wifi.WifiActivityEnergyInfo} object placed into the Bundle with the key
+     * {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. The result code is ignored.
+     */
+    oneway void requestActivityInfo(in ResultReceiver result);
+
     List<WifiConfiguration> getConfiguredNetworks();
 
     List<WifiConfiguration> getPrivilegedConfiguredNetworks();
index 2579d9f..a404a90 100644 (file)
@@ -5,7 +5,6 @@ import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
@@ -19,8 +18,6 @@ import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
-import java.util.concurrent.CountDownLatch;
-
 /** @hide */
 @SystemApi
 public class RttManager {
@@ -311,7 +308,7 @@ public class RttManager {
     }
 
     public RttCapabilities getRttCapabilities() {
-        synchronized (sCapabilitiesLock) {
+        synchronized (mCapabilitiesLock) {
             if (mRttCapabilities == null) {
                 try {
                     mRttCapabilities = mService.getRttCapabilities();
@@ -932,13 +929,13 @@ public class RttManager {
         validateChannel();
         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
         Log.i(TAG, "Send RTT request to RTT Service");
-        sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
+        mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
                 0, putListener(listener), parcelableParams);
     }
 
     public void stopRanging(RttListener listener) {
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+        mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
     }
 
     /**
@@ -975,7 +972,7 @@ public class RttManager {
         }
         validateChannel();
         int key = putListenerIfAbsent(callback);
-        sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+        mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
     }
 
     /**
@@ -998,7 +995,7 @@ public class RttManager {
             Log.e(TAG, "responder not enabled yet");
             return;
         }
-        sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+        mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
     }
 
     /**
@@ -1110,23 +1107,17 @@ public class RttManager {
     public static final int
             CMD_OP_ENALBE_RESPONDER_FAILED              = BASE + 8;
 
-    private Context mContext;
-    private IRttManager mService;
-    private RttCapabilities mRttCapabilities;
-
     private static final int INVALID_KEY = 0;
-    private static int sListenerKey = 1;
 
-    private static final SparseArray sListenerMap = new SparseArray();
-    private static final Object sListenerMapLock = new Object();
-    private static final Object sCapabilitiesLock = new Object();
+    private final Context mContext;
+    private final IRttManager mService;
+    private final SparseArray mListenerMap = new SparseArray();
+    private final Object mListenerMapLock = new Object();
+    private final Object mCapabilitiesLock = new Object();
 
-    private static AsyncChannel sAsyncChannel;
-    private static CountDownLatch sConnected;
-
-    private static final Object sThreadRefLock = new Object();
-    private static int sThreadRefCount;
-    private static HandlerThread sHandlerThread;
+    private RttCapabilities mRttCapabilities;
+    private int mListenerKey = 1;
+    private AsyncChannel mAsyncChannel;
 
     /**
      * Create a new WifiScanner instance.
@@ -1135,122 +1126,107 @@ public class RttManager {
      * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
      * @param context the application context
      * @param service the Binder interface
+     * @param looper Looper for running the callbacks.
+     *
      * @hide
      */
-
-    public RttManager(Context context, IRttManager service) {
+    public RttManager(Context context, IRttManager service, Looper looper) {
         mContext = context;
         mService = service;
-        init();
-    }
-
-    private void init() {
-        synchronized (sThreadRefLock) {
-            if (++sThreadRefCount == 1) {
-                Messenger messenger = null;
-                try {
-                    Log.d(TAG, "Get the messenger from " + mService);
-                    messenger = mService.getMessenger();
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                } catch (SecurityException e) {
-                    /* do nothing */
-                }
+        Messenger messenger = null;
+        try {
+            Log.d(TAG, "Get the messenger from " + mService);
+            messenger = mService.getMessenger();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
 
-                if (messenger == null) {
-                    sAsyncChannel = null;
-                    return;
-                }
+        if (messenger == null) {
+            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
+        }
 
-                sHandlerThread = new HandlerThread("RttManager");
-                sAsyncChannel = new AsyncChannel();
-                sConnected = new CountDownLatch(1);
+        mAsyncChannel = new AsyncChannel();
 
-                sHandlerThread.start();
-                Handler handler = new ServiceHandler(sHandlerThread.getLooper());
-                sAsyncChannel.connect(mContext, handler, messenger);
-                try {
-                    sConnected.await();
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "interrupted wait at init");
-                }
-            }
-        }
+        Handler handler = new ServiceHandler(looper);
+        mAsyncChannel.connectSync(mContext, handler, messenger);
+        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
+        // synchronously, which causes RttService to receive the wrong replyTo value.
+        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
     }
 
     private void validateChannel() {
-        if (sAsyncChannel == null) throw new IllegalStateException(
+        if (mAsyncChannel == null) throw new IllegalStateException(
                 "No permission to access and change wifi or a bad initialization");
     }
 
-    private static int putListener(Object listener) {
+    private int putListener(Object listener) {
         if (listener == null) return INVALID_KEY;
         int key;
-        synchronized (sListenerMapLock) {
+        synchronized (mListenerMapLock) {
             do {
-                key = sListenerKey++;
+                key = mListenerKey++;
             } while (key == INVALID_KEY);
-            sListenerMap.put(key, listener);
+            mListenerMap.put(key, listener);
         }
         return key;
     }
 
-    // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener.
-    private static int putListenerIfAbsent(Object listener) {
+    // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
+    private int putListenerIfAbsent(Object listener) {
         if (listener == null) return INVALID_KEY;
-        synchronized (sListenerMapLock) {
+        synchronized (mListenerMapLock) {
             int key = getListenerKey(listener);
             if (key != INVALID_KEY) {
                 return key;
             }
             do {
-                key = sListenerKey++;
+                key = mListenerKey++;
             } while (key == INVALID_KEY);
-            sListenerMap.put(key, listener);
+            mListenerMap.put(key, listener);
             return key;
         }
 
     }
 
-    private static Object getListener(int key) {
+    private Object getListener(int key) {
         if (key == INVALID_KEY) return null;
-        synchronized (sListenerMapLock) {
-            Object listener = sListenerMap.get(key);
+        synchronized (mListenerMapLock) {
+            Object listener = mListenerMap.get(key);
             return listener;
         }
     }
 
-    private static int getListenerKey(Object listener) {
+    private int getListenerKey(Object listener) {
         if (listener == null) return INVALID_KEY;
-        synchronized (sListenerMapLock) {
-            int index = sListenerMap.indexOfValue(listener);
+        synchronized (mListenerMapLock) {
+            int index = mListenerMap.indexOfValue(listener);
             if (index == -1) {
                 return INVALID_KEY;
             } else {
-                return sListenerMap.keyAt(index);
+                return mListenerMap.keyAt(index);
             }
         }
     }
 
-    private static Object removeListener(int key) {
+    private Object removeListener(int key) {
         if (key == INVALID_KEY) return null;
-        synchronized (sListenerMapLock) {
-            Object listener = sListenerMap.get(key);
-            sListenerMap.remove(key);
+        synchronized (mListenerMapLock) {
+            Object listener = mListenerMap.get(key);
+            mListenerMap.remove(key);
             return listener;
         }
     }
 
-    private static int removeListener(Object listener) {
+    private int removeListener(Object listener) {
         int key = getListenerKey(listener);
         if (key == INVALID_KEY) return key;
-        synchronized (sListenerMapLock) {
-            sListenerMap.remove(key);
+        synchronized (mListenerMapLock) {
+            mListenerMap.remove(key);
             return key;
         }
     }
 
-    private static class ServiceHandler extends Handler {
+    private class ServiceHandler extends Handler {
         ServiceHandler(Looper looper) {
             super(looper);
         }
@@ -1258,24 +1234,13 @@ public class RttManager {
         public void handleMessage(Message msg) {
             Log.i(TAG, "RTT manager get message: " + msg.what);
             switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
-                    } else {
-                        Log.e(TAG, "Failed to set up channel connection");
-                        // This will cause all further async API calls on the WifiManager
-                        // to fail and throw an exception
-                        sAsyncChannel = null;
-                    }
-                    return;
                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
-                    sConnected.countDown();
                     return;
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                     Log.e(TAG, "Channel connection lost");
                     // This will cause all further async API calls on the WifiManager
                     // to fail and throw an exception
-                    sAsyncChannel = null;
+                    mAsyncChannel = null;
                     getLooper().quit();
                     return;
             }
index 4c38c9b..29bf02c 100644 (file)
@@ -181,8 +181,8 @@ public final class WifiActivityEnergyInfo implements Parcelable {
      * @return if the record is valid
      */
     public boolean isValid() {
-        return ((mControllerTxTimeMs !=0) ||
-                (mControllerRxTimeMs !=0) ||
-                (mControllerIdleTimeMs !=0));
+        return ((mControllerTxTimeMs >=0) &&
+                (mControllerRxTimeMs >=0) &&
+                (mControllerIdleTimeMs >=0));
     }
 }