summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt17
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java517
-rw-r--r--core/java/android/app/ActivityManagerNative.java13
-rw-r--r--core/java/android/app/PendingIntent.java6
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java22
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/content/pm/ActivityInfo.java2
-rw-r--r--core/java/android/hardware/radio/RadioManager.java53
-rw-r--r--core/java/android/hardware/radio/RadioMetadata.java105
-rw-r--r--core/java/android/hardware/radio/RadioModule.java5
-rw-r--r--core/java/android/hardware/radio/RadioTuner.java4
-rw-r--r--core/java/android/os/ShellCommand.java11
-rw-r--r--core/java/android/os/UserManagerInternal.java20
-rw-r--r--core/java/android/security/net/config/ApplicationConfig.java2
-rw-r--r--core/java/android/security/net/config/NetworkSecurityConfig.java158
-rw-r--r--core/java/android/security/net/config/PinSet.java3
-rw-r--r--core/java/android/security/net/config/SystemCertificateSource.java25
-rw-r--r--core/java/android/security/net/config/UserCertificateSource.java54
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java2
-rw-r--r--core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl28
-rw-r--r--core/java/android/view/IWindow.aidl2
-rw-r--r--core/java/android/view/IWindowManager.aidl27
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java19
-rw-r--r--core/java/android/view/WindowManager.java12
-rw-r--r--core/java/android/view/WindowManagerGlobal.java2
-rw-r--r--core/java/com/android/internal/os/SomeArgs.java1
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java3
-rw-r--r--core/java/com/android/internal/widget/NonClientDecorView.java11
-rw-r--r--core/jni/android/graphics/BitmapRegionDecoder.cpp16
-rw-r--r--core/jni/android/graphics/Graphics.cpp2
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h4
-rw-r--r--core/jni/android_hardware_Radio.cpp31
-rw-r--r--core/res/res/values/dimens.xml8
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java93
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java8
-rw-r--r--libs/androidfw/tests/AppAsLib_test.cpp31
-rw-r--r--libs/androidfw/tests/data/appaslib/AndroidManifest.xml (renamed from core/res/res/layout/docked_stack_divider.xml)10
-rw-r--r--libs/androidfw/tests/data/appaslib/R.h20
-rw-r--r--libs/androidfw/tests/data/appaslib/appaslib_arsc.h68
-rw-r--r--libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h68
-rwxr-xr-xlibs/androidfw/tests/data/appaslib/build28
-rw-r--r--libs/androidfw/tests/data/appaslib/res/values/values.xml24
-rw-r--r--libs/hwui/DisplayListOp.h1
-rw-r--r--libs/hwui/OpReorderer.cpp116
-rw-r--r--libs/hwui/OpReorderer.h11
-rw-r--r--libs/hwui/RecordedOp.h1
-rw-r--r--libs/hwui/RecordingCanvas.cpp8
-rw-r--r--libs/hwui/RecordingCanvas.h17
-rw-r--r--libs/hwui/RenderNode.cpp54
-rw-r--r--libs/hwui/RenderNode.h7
-rw-r--r--libs/hwui/RenderProperties.h30
-rw-r--r--libs/hwui/unit_tests/DeviceInfoTests.cpp8
-rw-r--r--libs/hwui/unit_tests/LayerUpdateQueueTests.cpp2
-rw-r--r--libs/hwui/unit_tests/OpReordererTests.cpp181
-rw-r--r--libs/hwui/unit_tests/RecordingCanvasTests.cpp23
-rw-r--r--libs/hwui/unit_tests/TestUtils.h29
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java17
-rw-r--r--packages/SystemUI/res/drawable/docked_divider_handle.xml22
-rw-r--r--packages/SystemUI/res/layout/docked_stack_divider.xml35
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-land/styles.xml11
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/styles.xml10
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/Console.java196
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java135
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java322
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java99
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java82
-rw-r--r--services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java414
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java80
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java45
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java70
-rw-r--r--services/core/java/com/android/server/wm/DimLayerController.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java11
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java386
-rw-r--r--services/core/java/com/android/server/wm/DragState.java11
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java50
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java109
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java38
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java60
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java24
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java58
-rw-r--r--tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java77
-rw-r--r--tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java20
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java2
118 files changed, 3569 insertions, 1659 deletions
diff --git a/Android.mk b/Android.mk
index f0c0117bf44a..71bba0f16ea9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -259,6 +259,7 @@ LOCAL_SRC_FILES += \
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
+ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
core/java/android/view/IAssetAtlas.aidl \
core/java/android/view/IGraphicsStats.aidl \
core/java/android/view/IInputFilter.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3e63cd7237e1..73b522fa0aca 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5730,6 +5730,7 @@ package android.app.admin {
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
+ method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
diff --git a/api/system-current.txt b/api/system-current.txt
index bd674ef65f0b..87932ac9617b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5860,6 +5860,7 @@ package android.app.admin {
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
+ method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -14919,6 +14920,7 @@ package android.hardware.radio {
public static class RadioManager.FmBandConfig extends android.hardware.radio.RadioManager.BandConfig {
method public boolean getAf();
+ method public boolean getEa();
method public boolean getRds();
method public boolean getStereo();
method public boolean getTa();
@@ -14930,6 +14932,7 @@ package android.hardware.radio {
ctor public RadioManager.FmBandConfig.Builder(android.hardware.radio.RadioManager.FmBandConfig);
method public android.hardware.radio.RadioManager.FmBandConfig build();
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setAf(boolean);
+ method public android.hardware.radio.RadioManager.FmBandConfig.Builder setEa(boolean);
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setRds(boolean);
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setStereo(boolean);
method public android.hardware.radio.RadioManager.FmBandConfig.Builder setTa(boolean);
@@ -14937,6 +14940,7 @@ package android.hardware.radio {
public static class RadioManager.FmBandDescriptor extends android.hardware.radio.RadioManager.BandDescriptor {
method public boolean isAfSupported();
+ method public boolean isEaSupported();
method public boolean isRdsSupported();
method public boolean isStereoSupported();
method public boolean isTaSupported();
@@ -14976,6 +14980,7 @@ package android.hardware.radio {
method public boolean containsKey(java.lang.String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(java.lang.String);
+ method public android.hardware.radio.RadioMetadata.Clock getClock(java.lang.String);
method public int getInt(java.lang.String);
method public java.lang.String getString(java.lang.String);
method public java.util.Set<java.lang.String> keySet();
@@ -14985,6 +14990,7 @@ package android.hardware.radio {
field public static final java.lang.String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM";
field public static final java.lang.String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
field public static final java.lang.String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST";
+ field public static final java.lang.String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
field public static final java.lang.String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE";
field public static final java.lang.String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON";
field public static final java.lang.String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY";
@@ -15000,10 +15006,20 @@ package android.hardware.radio {
ctor public RadioMetadata.Builder(android.hardware.radio.RadioMetadata);
method public android.hardware.radio.RadioMetadata build();
method public android.hardware.radio.RadioMetadata.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+ method public android.hardware.radio.RadioMetadata.Builder putClock(java.lang.String, long, int);
method public android.hardware.radio.RadioMetadata.Builder putInt(java.lang.String, int);
method public android.hardware.radio.RadioMetadata.Builder putString(java.lang.String, java.lang.String);
}
+ public static final class RadioMetadata.Clock implements android.os.Parcelable {
+ ctor public RadioMetadata.Clock(long, int);
+ method public int describeContents();
+ method public int getTimezoneOffsetMinutes();
+ method public long getUtcEpochSeconds();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioMetadata.Clock> CREATOR;
+ }
+
public abstract class RadioTuner {
ctor public RadioTuner();
method public abstract int cancel();
@@ -15032,6 +15048,7 @@ package android.hardware.radio {
method public void onAntennaState(boolean);
method public void onConfigurationChanged(android.hardware.radio.RadioManager.BandConfig);
method public void onControlChanged(boolean);
+ method public void onEmergencyAnnouncement(boolean);
method public void onError(int);
method public void onMetadataChanged(android.hardware.radio.RadioMetadata);
method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 031bdbd1f222..eb7c71295617 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -28,24 +28,13 @@ import android.app.ActivityManagerNative;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.pm.VerificationParams;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -56,26 +45,13 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
-import libcore.io.IoUtils;
-
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.SizedInputStream;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
public final class Pm {
private static final String TAG = "Pm";
@@ -106,7 +82,7 @@ public final class Pm {
System.exit(exitCode);
}
- public int run(String[] args) throws IOException, RemoteException {
+ public int run(String[] args) throws RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
@@ -142,19 +118,19 @@ public final class Pm {
}
if ("install-create".equals(op)) {
- return runInstallCreate();
+ return runInstall();
}
if ("install-write".equals(op)) {
- return runInstallWrite();
+ return runInstall();
}
if ("install-commit".equals(op)) {
- return runInstallCommit();
+ return runInstall();
}
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
- return runInstallAbandon();
+ return runInstall();
}
if ("set-installer".equals(op)) {
@@ -299,6 +275,10 @@ public final class Pm {
return -1;
}
+ private int runInstall() {
+ return runShellCommand("package", mArgs);
+ }
+
/**
* Execute the list sub-command.
*
@@ -317,6 +297,10 @@ public final class Pm {
return runShellCommand("package", mArgs);
}
+ private int runUninstall() {
+ return runShellCommand("package", mArgs);
+ }
+
private int runPath() {
int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
@@ -370,49 +354,6 @@ public final class Pm {
}
}
- /**
- * Converts a failure code into a string by using reflection to find a matching constant
- * in PackageManager.
- */
- private String installFailureToString(LocalPackageInstallObserver obs) {
- final int result = obs.result;
- Field[] fields = PackageManager.class.getFields();
- for (Field f: fields) {
- if (f.getType() == int.class) {
- int modifiers = f.getModifiers();
- // only look at public final static fields.
- if (((modifiers & Modifier.FINAL) != 0) &&
- ((modifiers & Modifier.PUBLIC) != 0) &&
- ((modifiers & Modifier.STATIC) != 0)) {
- String fieldName = f.getName();
- if (fieldName.startsWith("INSTALL_FAILED_") ||
- fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
- // get the int value and compare it to result.
- try {
- if (result == f.getInt(null)) {
- StringBuilder sb = new StringBuilder(64);
- sb.append(fieldName);
- if (obs.extraPermission != null) {
- sb.append(" perm=");
- sb.append(obs.extraPermission);
- }
- if (obs.extraPackage != null) {
- sb.append(" pkg=" + obs.extraPackage);
- }
- return sb.toString();
- }
- } catch (IllegalAccessException e) {
- // this shouldn't happen since we only look for public static fields.
- }
- }
- }
- }
- }
-
- // couldn't find a matching constant? return the value
- return Integer.toString(result);
- }
-
// pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
private int runSetAppLink() {
int userId = UserHandle.USER_SYSTEM;
@@ -602,316 +543,6 @@ public final class Pm {
}
}
- private int runInstall() {
- int installFlags = 0;
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- String opt;
-
- String originatingUriString = null;
- String referrer = null;
- String abi = null;
-
- while ((opt=nextOption()) != null) {
- if (opt.equals("-l")) {
- installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextOptionData();
- if (installerPackageName == null) {
- System.err.println("Error: no value specified for -i");
- return 1;
- }
- } else if (opt.equals("-t")) {
- installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- // Override if -s option is specified.
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- // Override if -s option is specified.
- installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- originatingUriString = nextOptionData();
- if (originatingUriString == null) {
- System.err.println("Error: must supply argument for --originating-uri");
- return 1;
- }
- } else if (opt.equals("--referrer")) {
- referrer = nextOptionData();
- if (referrer == null) {
- System.err.println("Error: must supply argument for --referrer");
- return 1;
- }
- } else if (opt.equals("--abi")) {
- abi = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- userId = translateUserId(userId, "runInstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final Uri verificationURI;
- final Uri originatingURI;
- final Uri referrerURI;
-
- if (originatingUriString != null) {
- originatingURI = Uri.parse(originatingUriString);
- } else {
- originatingURI = null;
- }
-
- if (referrer != null) {
- referrerURI = Uri.parse(referrer);
- } else {
- referrerURI = null;
- }
-
- // Populate apkURI, must be present
- final String apkFilePath = nextArg();
- System.err.println("\tpkg: " + apkFilePath);
- if (apkFilePath == null) {
- System.err.println("Error: no package specified");
- return 1;
- }
-
- // Populate verificationURI, optionally present
- final String verificationFilePath = nextArg();
- if (verificationFilePath != null) {
- System.err.println("\tver: " + verificationFilePath);
- verificationURI = Uri.fromFile(new File(verificationFilePath));
- } else {
- verificationURI = null;
- }
-
- LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
- try {
- VerificationParams verificationParams = new VerificationParams(verificationURI,
- originatingURI, referrerURI, VerificationParams.NO_UID, null);
-
- mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
- installerPackageName, verificationParams, abi, userId);
-
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failure ["
- + installFailureToString(obs)
- + "]");
- return 1;
- }
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * @param userId The user id to be translated.
- * @param logContext Optional human readable text to provide some context in error log.
- * @return Translated concrete user id. This will include USER_ALL.
- */
- private int translateUserId(int userId, String logContext) {
- return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, true, true, logContext, "pm command");
- }
-
- private int runInstallCreate() throws RemoteException {
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-l")) {
- params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextArg();
- if (installerPackageName == null) {
- throw new IllegalArgumentException("Missing installer package");
- }
- } else if (opt.equals("-t")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- params.installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- params.installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- params.originatingUri = Uri.parse(nextOptionData());
- } else if (opt.equals("--referrer")) {
- params.referrerUri = Uri.parse(nextOptionData());
- } else if (opt.equals("-p")) {
- params.mode = SessionParams.MODE_INHERIT_EXISTING;
- params.appPackageName = nextOptionData();
- if (params.appPackageName == null) {
- throw new IllegalArgumentException("Missing inherit package name");
- }
- } else if (opt.equals("-S")) {
- params.setSize(Long.parseLong(nextOptionData()));
- } else if (opt.equals("--abi")) {
- params.abiOverride = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--install-location")) {
- params.installLocation = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--force-uuid")) {
- params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
- params.volumeUuid = nextOptionData();
- if ("internal".equals(params.volumeUuid)) {
- params.volumeUuid = null;
- }
- } else {
- throw new IllegalArgumentException("Unknown option " + opt);
- }
- }
-
- userId = translateUserId(userId, "runInstallCreate");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- params.installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
-
- // NOTE: adb depends on parsing this string
- System.out.println("Success: created install session [" + sessionId + "]");
- return 0;
- }
-
- private int runInstallWrite() throws IOException, RemoteException {
- long sizeBytes = -1;
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-S")) {
- sizeBytes = Long.parseLong(nextOptionData());
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
-
- final int sessionId = Integer.parseInt(nextArg());
- final String splitName = nextArg();
-
- String path = nextArg();
- if ("-".equals(path)) {
- path = null;
- } else if (path != null) {
- final File file = new File(path);
- if (file.isFile()) {
- sizeBytes = file.length();
- }
- }
-
- final SessionInfo info = mInstaller.getSessionInfo(sessionId);
-
- PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
-
- if (path != null) {
- in = new FileInputStream(path);
- } else {
- in = new SizedInputStream(System.in, sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[65536];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
-
- System.out.println("Success: streamed " + total + " bytes");
- return 0;
- } finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(session);
- }
- }
-
- private int runInstallCommit() throws RemoteException {
- final int sessionId = Integer.parseInt(nextArg());
-
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- session.commit(receiver.getIntentSender());
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
- return 0;
- } else {
- Log.e(TAG, "Failure details: " + result.getExtras());
- System.err.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
- private int runInstallAbandon() throws RemoteException {
- final int sessionId = Integer.parseInt(nextArg());
-
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
- session.abandon();
- System.out.println("Success");
- return 0;
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
private int runSetInstaller() throws RemoteException {
final String targetPackage = nextArg();
final String installerPackageName = nextArg();
@@ -1080,80 +711,6 @@ public final class Pm {
}
}
- private int runUninstall() throws RemoteException {
- int flags = 0;
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-k")) {
- flags |= PackageManager.DELETE_KEEP_DATA;
- } else if (opt.equals("--user")) {
- String param = nextArg();
- if (isNumber(param)) {
- userId = Integer.parseInt(param);
- } else {
- showUsage();
- System.err.println("Error: Invalid user: " + param);
- return 1;
- }
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- return showUsage();
- }
-
- userId = translateUserId(userId, "runUninstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- flags |= PackageManager.DELETE_ALL_USERS;
- } else {
- PackageInfo info;
- try {
- info = mPm.getPackageInfo(pkg, 0, userId);
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- if (info == null) {
- System.err.println("Failure - not installed for " + userId);
- return 1;
- }
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
- receiver.getIntentSender(), userId);
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
- return 0;
- } else {
- Log.e(TAG, "Failure details: " + result.getExtras());
- System.err.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- }
-
static class ClearDataObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1499,54 +1056,6 @@ public final class Pm {
return 1;
}
- private static String checkAbiArgument(String abi) {
- if (TextUtils.isEmpty(abi)) {
- throw new IllegalArgumentException("Missing ABI argument");
- }
-
- if ("-".equals(abi)) {
- return abi;
- }
-
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- return abi;
- }
- }
-
- throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
- }
-
- private static class LocalIntentReceiver {
- private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public int send(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- return 0;
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
private String nextOption() {
if (mNextArg >= mArgs.length) {
return null;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7c3fcc30f6cf..2ac6cf93cc09 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -769,7 +769,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case RESIZE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
- Rect r = Rect.CREATOR.createFromParcel(data);
+ final boolean hasRect = data.readInt() != 0;
+ Rect r = null;
+ if (hasRect) {
+ r = Rect.CREATOR.createFromParcel(data);
+ }
final boolean allowResizeInDockedMode = data.readInt() == 1;
resizeStack(stackId, r, allowResizeInDockedMode);
reply.writeNoException();
@@ -3590,7 +3594,12 @@ class ActivityManagerProxy implements IActivityManager
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
- r.writeToParcel(data, 0);
+ if (r != null) {
+ data.writeInt(1);
+ r.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
data.writeInt(allowResizeInDockedMode ? 1 : 0);
mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
reply.readException();
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index c42ba65b0dc2..edafe59ca454 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -432,7 +432,7 @@ public final class PendingIntent implements Parcelable {
* @param intents Array of Intents of the activities to be launched.
* @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
* {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
- * or any of the flags as supported by
+ * {@link #FLAG_IMMUTABLE} or any of the flags as supported by
* {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
* of the intent that can be supplied when the actual send happens.
*
@@ -502,7 +502,7 @@ public final class PendingIntent implements Parcelable {
* @param intent The Intent to be broadcast.
* @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
* {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
- * or any of the flags as supported by
+ * {@link #FLAG_IMMUTABLE} or any of the flags as supported by
* {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
* of the intent that can be supplied when the actual send happens.
*
@@ -556,7 +556,7 @@ public final class PendingIntent implements Parcelable {
* @param intent An Intent describing the service to be started.
* @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
* {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
- * or any of the flags as supported by
+ * {@link #FLAG_IMMUTABLE} or any of the flags as supported by
* {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
* of the intent that can be supplied when the actual send happens.
*
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0fdf3d3693c3..d7ffcc4190f6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3637,6 +3637,28 @@ public class DevicePolicyManager {
}
/**
+ * Called by a profile or device owner to get user restrictions set with
+ * {@link #addUserRestriction(ComponentName, String)}.
+ * <p>
+ * The target user may have more restrictions set by the system or other device owner / profile
+ * owner. To get all the user restrictions currently set, use
+ * {@link UserManager#getUserRestrictions()}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public Bundle getUserRestrictions(@NonNull ComponentName admin) {
+ Bundle ret = null;
+ if (mService != null) {
+ try {
+ ret = mService.getUserRestrictions(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return ret == null ? new Bundle() : ret;
+ }
+
+ /**
* Called by profile or device owners to hide or unhide packages. When a package is hidden it
* is unavailable for use, but the data and actual package file remain.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ccaa8cb82170..cfa58618138e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -147,6 +147,7 @@ interface IDevicePolicyManager {
ComponentName getRestrictionsProvider(int userHandle);
void setUserRestriction(in ComponentName who, in String key, boolean enable);
+ Bundle getUserRestrictions(in ComponentName who);
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 7ca39cb7c443..885255f81043 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -292,7 +292,7 @@ public class ActivityInfo extends ComponentInfo
/**
* @hide Bit in {@link #flags}: If set, this activity may be launched into an
* owned ActivityContainer such as that within an ActivityView. If not set and
- * this activity is launched into such a container a SecurityExcception will be
+ * this activity is launched into such a container a SecurityException will be
* thrown. Set from the {@link android.R.attr#allowEmbedded} attribute.
*/
public static final int FLAG_ALLOW_EMBEDDED = 0x80000000;
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 32930a798201..14bb923767ed 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -457,14 +457,16 @@ public class RadioManager {
private final boolean mRds;
private final boolean mTa;
private final boolean mAf;
+ private final boolean mEa;
FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
- boolean stereo, boolean rds, boolean ta, boolean af) {
+ boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
super(region, type, lowerLimit, upperLimit, spacing);
mStereo = stereo;
mRds = rds;
mTa = ta;
mAf = af;
+ mEa = ea;
}
/** Stereo is supported
@@ -492,6 +494,13 @@ public class RadioManager {
return mAf;
}
+ /** Emergency Announcement is supported
+ * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise.
+ */
+ public boolean isEaSupported() {
+ return mEa;
+ }
+
/* Parcelable implementation */
private FmBandDescriptor(Parcel in) {
super(in);
@@ -499,6 +508,7 @@ public class RadioManager {
mRds = in.readByte() == 1;
mTa = in.readByte() == 1;
mAf = in.readByte() == 1;
+ mEa = in.readByte() == 1;
}
public static final Parcelable.Creator<FmBandDescriptor> CREATOR
@@ -519,6 +529,7 @@ public class RadioManager {
dest.writeByte((byte) (mRds ? 1 : 0));
dest.writeByte((byte) (mTa ? 1 : 0));
dest.writeByte((byte) (mAf ? 1 : 0));
+ dest.writeByte((byte) (mEa ? 1 : 0));
}
@Override
@@ -529,7 +540,8 @@ public class RadioManager {
@Override
public String toString() {
return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo
- + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + "]";
+ + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf +
+ ", mEa =" + mEa + "]";
}
@Override
@@ -540,6 +552,7 @@ public class RadioManager {
result = prime * result + (mRds ? 1 : 0);
result = prime * result + (mTa ? 1 : 0);
result = prime * result + (mAf ? 1 : 0);
+ result = prime * result + (mEa ? 1 : 0);
return result;
}
@@ -560,6 +573,8 @@ public class RadioManager {
return false;
if (mAf != other.isAfSupported())
return false;
+ if (mEa != other.isEaSupported())
+ return false;
return true;
}
}
@@ -754,6 +769,7 @@ public class RadioManager {
private final boolean mRds;
private final boolean mTa;
private final boolean mAf;
+ private final boolean mEa;
FmBandConfig(FmBandDescriptor descriptor) {
super((BandDescriptor)descriptor);
@@ -761,15 +777,17 @@ public class RadioManager {
mRds = descriptor.isRdsSupported();
mTa = descriptor.isTaSupported();
mAf = descriptor.isAfSupported();
+ mEa = descriptor.isEaSupported();
}
FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
- boolean stereo, boolean rds, boolean ta, boolean af) {
+ boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
super(region, type, lowerLimit, upperLimit, spacing);
mStereo = stereo;
mRds = rds;
mTa = ta;
mAf = af;
+ mEa = ea;
}
/** Get stereo enable state
@@ -800,12 +818,21 @@ public class RadioManager {
return mAf;
}
+ /**
+ * Get Emergency announcement enable state
+ * @return the enable state.
+ */
+ public boolean getEa() {
+ return mEa;
+ }
+
private FmBandConfig(Parcel in) {
super(in);
mStereo = in.readByte() == 1;
mRds = in.readByte() == 1;
mTa = in.readByte() == 1;
mAf = in.readByte() == 1;
+ mEa = in.readByte() == 1;
}
public static final Parcelable.Creator<FmBandConfig> CREATOR
@@ -826,6 +853,7 @@ public class RadioManager {
dest.writeByte((byte) (mRds ? 1 : 0));
dest.writeByte((byte) (mTa ? 1 : 0));
dest.writeByte((byte) (mAf ? 1 : 0));
+ dest.writeByte((byte) (mEa ? 1 : 0));
}
@Override
@@ -837,7 +865,7 @@ public class RadioManager {
public String toString() {
return "FmBandConfig [" + super.toString()
+ ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa
- + ", mAf=" + mAf + "]";
+ + ", mAf=" + mAf + ", mEa =" + mEa + "]";
}
@Override
@@ -848,6 +876,7 @@ public class RadioManager {
result = prime * result + (mRds ? 1 : 0);
result = prime * result + (mTa ? 1 : 0);
result = prime * result + (mAf ? 1 : 0);
+ result = prime * result + (mEa ? 1 : 0);
return result;
}
@@ -868,6 +897,8 @@ public class RadioManager {
return false;
if (mAf != other.mAf)
return false;
+ if (mEa != other.mEa)
+ return false;
return true;
}
@@ -880,6 +911,7 @@ public class RadioManager {
private boolean mRds;
private boolean mTa;
private boolean mAf;
+ private boolean mEa;
/**
* Constructs a new Builder with the defaults from an {@link FmBandDescriptor} .
@@ -893,6 +925,7 @@ public class RadioManager {
mRds = descriptor.isRdsSupported();
mTa = descriptor.isTaSupported();
mAf = descriptor.isAfSupported();
+ mEa = descriptor.isEaSupported();
}
/**
@@ -906,6 +939,7 @@ public class RadioManager {
mRds = config.getRds();
mTa = config.getTa();
mAf = config.getAf();
+ mEa = config.getEa();
}
/**
@@ -917,7 +951,7 @@ public class RadioManager {
FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(),
mDescriptor.getType(), mDescriptor.getLowerLimit(),
mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
- mStereo, mRds, mTa, mAf);
+ mStereo, mRds, mTa, mAf, mEa);
return config;
}
@@ -956,6 +990,15 @@ public class RadioManager {
mAf = state;
return this;
}
+
+ /** Set Emergency Announcement enable state
+ * @param state The new enable state.
+ * @return the same Builder instance.
+ */
+ public Builder setEa(boolean state) {
+ mEa = state;
+ return this;
+ }
};
}
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index 8b1851b8397d..b7715da5ff61 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -95,11 +95,17 @@ public final class RadioMetadata implements Parcelable {
*/
public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
+ /**
+ * The clock.
+ */
+ public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
+
private static final int METADATA_TYPE_INVALID = -1;
private static final int METADATA_TYPE_INT = 0;
private static final int METADATA_TYPE_TEXT = 1;
private static final int METADATA_TYPE_BITMAP = 2;
+ private static final int METADATA_TYPE_CLOCK = 3;
private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
@@ -116,6 +122,7 @@ public final class RadioMetadata implements Parcelable {
METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK);
}
// keep in sync with: system/media/radio/include/system/radio_metadata.h
@@ -131,6 +138,7 @@ public final class RadioMetadata implements Parcelable {
private static final int NATIVE_KEY_GENRE = 8;
private static final int NATIVE_KEY_ICON = 9;
private static final int NATIVE_KEY_ART = 10;
+ private static final int NATIVE_KEY_CLOCK = 11;
private static final SparseArray<String> NATIVE_KEY_MAPPING;
@@ -147,6 +155,59 @@ public final class RadioMetadata implements Parcelable {
NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART);
+ NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK);
+ }
+
+ /**
+ * Provides a Clock that can be used to describe time as provided by the Radio.
+ *
+ * The clock is defined by the seconds since epoch at the UTC + 0 timezone
+ * and timezone offset from UTC + 0 represented in number of minutes.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Clock implements Parcelable {
+ private final long mUtcEpochSeconds;
+ private final int mTimezoneOffsetMinutes;
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mUtcEpochSeconds);
+ out.writeInt(mTimezoneOffsetMinutes);
+ }
+
+ public static final Parcelable.Creator<Clock> CREATOR
+ = new Parcelable.Creator<Clock>() {
+ public Clock createFromParcel(Parcel in) {
+ return new Clock(in);
+ }
+
+ public Clock[] newArray(int size) {
+ return new Clock[size];
+ }
+ };
+
+ public Clock(long utcEpochSeconds, int timezoneOffsetMinutes) {
+ mUtcEpochSeconds = utcEpochSeconds;
+ mTimezoneOffsetMinutes = timezoneOffsetMinutes;
+ }
+
+ private Clock(Parcel in) {
+ mUtcEpochSeconds = in.readLong();
+ mTimezoneOffsetMinutes = in.readInt();
+ }
+
+ public long getUtcEpochSeconds() {
+ return mUtcEpochSeconds;
+ }
+
+ public int getTimezoneOffsetMinutes() {
+ return mTimezoneOffsetMinutes;
+ }
}
private final Bundle mBundle;
@@ -212,6 +273,17 @@ public final class RadioMetadata implements Parcelable {
return bmp;
}
+ public Clock getClock(String key) {
+ Clock clock = null;
+ try {
+ clock = mBundle.getParcelable(key);
+ } catch (Exception e) {
+ // ignore, value was not a clock.
+ Log.w(TAG, "Failed to retrieve a key as Clock.", e);
+ }
+ return clock;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -389,6 +461,27 @@ public final class RadioMetadata implements Parcelable {
}
/**
+ * Put a {@link RadioMetadata.Clock} into the meta data. Custom keys may be used, but if the
+ * METADATA_KEYs defined in this class are used they may only be one of the following:
+ * <ul>
+ * <li>{@link #MEADATA_KEY_CLOCK}</li>
+ * </ul>
+ *
+ * @param utcSecondsSinceEpoch Number of seconds since epoch for UTC + 0 timezone.
+ * @param timezoneOffsetInMinutes Offset of timezone from UTC + 0 in minutes.
+ * @return the same Builder instance.
+ */
+ public Builder putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes) {
+ if (!METADATA_KEYS_TYPE.containsKey(key) ||
+ METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a RadioMetadata.Clock.");
+ }
+ mBundle.putParcelable(key, new Clock(utcSecondsSinceEpoch, timezoneOffsetMinutes));
+ return this;
+ }
+
+ /**
* Creates a {@link RadioMetadata} instance with the specified fields.
*
* @return a new {@link RadioMetadata} object
@@ -446,4 +539,16 @@ public final class RadioMetadata implements Parcelable {
return 0;
}
}
+
+ int putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes) {
+ Log.d(TAG, "putClockFromNative()");
+ String key = getKeyFromNativeKey(nativeKey);
+ if (!METADATA_KEYS_TYPE.containsKey(key) ||
+ METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) {
+ return -1;
+ }
+ mBundle.putParcelable(key, new RadioMetadata.Clock(
+ utcEpochSeconds, timezoneOffsetInMinutes));
+ return 0;
+ }
}
diff --git a/core/java/android/hardware/radio/RadioModule.java b/core/java/android/hardware/radio/RadioModule.java
index 15916ae60c59..fc7d0d2afbd4 100644
--- a/core/java/android/hardware/radio/RadioModule.java
+++ b/core/java/android/hardware/radio/RadioModule.java
@@ -89,6 +89,7 @@ public class RadioModule extends RadioTuner {
static final int EVENT_METADATA = 4;
static final int EVENT_TA = 5;
static final int EVENT_AF_SWITCH = 6;
+ static final int EVENT_EA = 7;
static final int EVENT_CONTROL = 100;
static final int EVENT_SERVER_DIED = 101;
@@ -170,6 +171,10 @@ public class RadioModule extends RadioTuner {
callback.onTrafficAnnouncement(msg.arg2 == 1);
}
break;
+ case EVENT_EA:
+ if (callback != null) {
+ callback.onEmergencyAnnouncement(msg.arg2 == 1);
+ }
case EVENT_CONTROL:
if (callback != null) {
callback.onControlChanged(msg.arg2 == 1);
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 376900a03444..5c82555663de 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -281,6 +281,10 @@ public abstract class RadioTuner {
*/
public void onTrafficAnnouncement(boolean active) {}
/**
+ * onEmergencyAnnouncement() is called when an emergency annoucement starts and stops.
+ */
+ public void onEmergencyAnnouncement(boolean active) {}
+ /**
* onAntennaState() is called when the antenna is connected or disconnected.
*/
public void onAntennaState(boolean connected) {}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 73c2c804bdf1..cad482b6bab9 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,8 +19,11 @@ package android.os;
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
+import java.io.BufferedInputStream;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.PrintWriter;
/**
@@ -43,6 +46,7 @@ public abstract class ShellCommand {
private FastPrintWriter mOutPrintWriter;
private FastPrintWriter mErrPrintWriter;
+ private InputStream mInputStream;
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) {
@@ -111,6 +115,13 @@ public abstract class ShellCommand {
return mErrPrintWriter;
}
+ public InputStream getInputStream() {
+ if (mInputStream == null) {
+ mInputStream = new BufferedInputStream(new FileInputStream(mIn));
+ }
+ return mInputStream;
+ }
+
/**
* Return the next option on the command line -- that is an argument that
* starts with '-'. If the next argument is not an option, null is returned.
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index b50cc79ad341..9178ec61f31d 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -19,6 +19,17 @@ package android.os;
* @hide Only for use within the system server.
*/
public abstract class UserManagerInternal {
+ public interface UserRestrictionsListener {
+ /**
+ * Called when a user restriction changes.
+ *
+ * @param userId target user id
+ * @param newRestrictions new user restrictions
+ * @param prevRestrictions user restrictions that were previously set
+ */
+ void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions);
+ }
+
/**
* Lock that must be held when calling certain methods in this class.
*
@@ -60,4 +71,13 @@ public abstract class UserManagerInternal {
*/
public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId,
Bundle baseRestrictions);
+
+ /** Return a user restriction. */
+ public abstract boolean getUserRestriction(int userId, String key);
+
+ /** Adds a listener to user restriction changes. */
+ public abstract void addUserRestrictionsListener(UserRestrictionsListener listener);
+
+ /** Remove a {@link UserRestrictionsListener}. */
+ public abstract void removeUserRestrictionsListener(UserRestrictionsListener listener);
}
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index c67535258c98..9bf344a0e2bc 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -48,7 +48,7 @@ public final class ApplicationConfig {
*/
public boolean hasPerDomainConfigs() {
ensureInitialized();
- return mConfigs == null || !mConfigs.isEmpty();
+ return mConfigs != null && !mConfigs.isEmpty();
}
/**
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 915fbefb7041..0a4ce1126d05 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -17,6 +17,9 @@
package android.security.net.config;
import android.util.ArraySet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -26,6 +29,12 @@ import javax.net.ssl.X509TrustManager;
* @hide
*/
public final class NetworkSecurityConfig {
+ /** @hide */
+ public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
+ /** @hide */
+ public static final boolean DEFAULT_HSTS_ENFORCED = false;
+ public static final NetworkSecurityConfig DEFAULT = getDefaultBuilder().build();
+
private final boolean mCleartextTrafficPermitted;
private final boolean mHstsEnforced;
private final PinSet mPins;
@@ -35,7 +44,7 @@ public final class NetworkSecurityConfig {
private X509TrustManager mTrustManager;
private final Object mTrustManagerLock = new Object();
- public NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
+ private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
mCleartextTrafficPermitted = cleartextTrafficPermitted;
mHstsEnforced = hstsEnforced;
@@ -83,4 +92,151 @@ public final class NetworkSecurityConfig {
mAnchors = null;
}
}
+
+ /**
+ * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
+ *
+ * <p>
+ * The default configuration has the following properties:
+ * <ol>
+ * <li>Cleartext traffic is permitted.</li>
+ * <li>HSTS is not enforced.</li>
+ * <li>No certificate pinning is used.</li>
+ * <li>The system and user added trusted certificate stores are trusted for connections.</li>
+ * </ol>
+ *
+ * @hide
+ */
+ public static final Builder getDefaultBuilder() {
+ return new Builder()
+ .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
+ .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
+ // System certificate store, does not bypass static pins.
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ // User certificate store, does not bypass static pins.
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
+ }
+
+ /**
+ * Builder for creating {@code NetworkSecurityConfig} objects.
+ * @hide
+ */
+ public static final class Builder {
+ private List<CertificatesEntryRef> mCertificatesEntryRefs;
+ private PinSet mPinSet;
+ private boolean mCleartextTrafficPermitted = DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
+ private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
+ private boolean mCleartextTrafficPermittedSet = false;
+ private boolean mHstsEnforcedSet = false;
+ private Builder mParentBuilder;
+
+ /**
+ * Sets the parent {@code Builder} for this {@code Builder}.
+ * The parent will be used to determine values not configured in this {@code Builder}
+ * in {@link Builder#build()}, recursively if needed.
+ */
+ public Builder setParent(Builder parent) {
+ // Sanity check to avoid adding loops.
+ Builder current = parent;
+ while (current != null) {
+ if (current == this) {
+ throw new IllegalArgumentException("Loops are not allowed in Builder parents");
+ }
+ current = current.getParent();
+ }
+ mParentBuilder = parent;
+ return this;
+ }
+
+ public Builder getParent() {
+ return mParentBuilder;
+ }
+
+ public Builder setPinSet(PinSet pinSet) {
+ mPinSet = pinSet;
+ return this;
+ }
+
+ private PinSet getEffectivePinSet() {
+ if (mPinSet != null) {
+ return mPinSet;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectivePinSet();
+ }
+ return PinSet.EMPTY_PINSET;
+ }
+
+ public Builder setCleartextTrafficPermitted(boolean cleartextTrafficPermitted) {
+ mCleartextTrafficPermitted = cleartextTrafficPermitted;
+ mCleartextTrafficPermittedSet = true;
+ return this;
+ }
+
+ private boolean getEffectiveCleartextTrafficPermitted() {
+ if (mCleartextTrafficPermittedSet) {
+ return mCleartextTrafficPermitted;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectiveCleartextTrafficPermitted();
+ }
+ return DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
+ }
+
+ public Builder setHstsEnforced(boolean hstsEnforced) {
+ mHstsEnforced = hstsEnforced;
+ mHstsEnforcedSet = true;
+ return this;
+ }
+
+ private boolean getEffectiveHstsEnforced() {
+ if (mHstsEnforcedSet) {
+ return mHstsEnforced;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectiveHstsEnforced();
+ }
+ return DEFAULT_HSTS_ENFORCED;
+ }
+
+ public Builder addCertificatesEntryRef(CertificatesEntryRef ref) {
+ if (mCertificatesEntryRefs == null) {
+ mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
+ }
+ mCertificatesEntryRefs.add(ref);
+ return this;
+ }
+
+ public Builder addCertificatesEntryRefs(Collection<? extends CertificatesEntryRef> refs) {
+ if (mCertificatesEntryRefs == null) {
+ mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
+ }
+ mCertificatesEntryRefs.addAll(refs);
+ return this;
+ }
+
+ private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() {
+ if (mCertificatesEntryRefs != null) {
+ return mCertificatesEntryRefs;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getEffectiveCertificatesEntryRefs();
+ }
+ return Collections.<CertificatesEntryRef>emptyList();
+ }
+
+ public boolean hasCertificateEntryRefs() {
+ return mCertificatesEntryRefs != null;
+ }
+
+ public NetworkSecurityConfig build() {
+ boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
+ boolean hstsEnforced = getEffectiveCleartextTrafficPermitted();
+ PinSet pinSet = getEffectivePinSet();
+ List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
+ return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
+ }
+ }
}
diff --git a/core/java/android/security/net/config/PinSet.java b/core/java/android/security/net/config/PinSet.java
index a9ee039fd01c..d3c975eb3101 100644
--- a/core/java/android/security/net/config/PinSet.java
+++ b/core/java/android/security/net/config/PinSet.java
@@ -17,10 +17,13 @@
package android.security.net.config;
import android.util.ArraySet;
+import java.util.Collections;
import java.util.Set;
/** @hide */
public final class PinSet {
+ public static final PinSet EMPTY_PINSET =
+ new PinSet(Collections.<Pin>emptySet(), Long.MAX_VALUE);
public final long expirationTime;
public final Set<Pin> pins;
diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java
index 640ebd9a9cf8..7649a977ff5c 100644
--- a/core/java/android/security/net/config/SystemCertificateSource.java
+++ b/core/java/android/security/net/config/SystemCertificateSource.java
@@ -36,18 +36,23 @@ import libcore.io.IoUtils;
* @hide
*/
public class SystemCertificateSource implements CertificateSource {
- private static Set<X509Certificate> sSystemCerts = null;
- private static final Object sLock = new Object();
+ private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
+ private Set<X509Certificate> mSystemCerts = null;
+ private final Object mLock = new Object();
- public SystemCertificateSource() {
+ private SystemCertificateSource() {
+ }
+
+ public static SystemCertificateSource getInstance() {
+ return INSTANCE;
}
@Override
public Set<X509Certificate> getCertificates() {
// TODO: loading all of these is wasteful, we should instead use a keystore style API.
- synchronized (sLock) {
- if (sSystemCerts != null) {
- return sSystemCerts;
+ synchronized (mLock) {
+ if (mSystemCerts != null) {
+ return mSystemCerts;
}
CertificateFactory certFactory;
try {
@@ -83,14 +88,14 @@ public class SystemCertificateSource implements CertificateSource {
IoUtils.closeQuietly(is);
}
}
- sSystemCerts = systemCerts;
- return sSystemCerts;
+ mSystemCerts = systemCerts;
+ return mSystemCerts;
}
}
public void onCertificateStorageChange() {
- synchronized (sLock) {
- sSystemCerts = null;
+ synchronized (mLock) {
+ mSystemCerts = null;
}
}
}
diff --git a/core/java/android/security/net/config/UserCertificateSource.java b/core/java/android/security/net/config/UserCertificateSource.java
index 77e2c88fe206..e9d5aa1e2f34 100644
--- a/core/java/android/security/net/config/UserCertificateSource.java
+++ b/core/java/android/security/net/config/UserCertificateSource.java
@@ -36,18 +36,23 @@ import libcore.io.IoUtils;
* @hide
*/
public class UserCertificateSource implements CertificateSource {
- private static Set<X509Certificate> sUserCerts = null;
- private static final Object sLock = new Object();
+ private static final UserCertificateSource INSTANCE = new UserCertificateSource();
+ private Set<X509Certificate> mUserCerts = null;
+ private final Object mLock = new Object();
- public UserCertificateSource() {
+ private UserCertificateSource() {
+ }
+
+ public static UserCertificateSource getInstance() {
+ return INSTANCE;
}
@Override
public Set<X509Certificate> getCertificates() {
// TODO: loading all of these is wasteful, we should instead use a keystore style API.
- synchronized (sLock) {
- if (sUserCerts != null) {
- return sUserCerts;
+ synchronized (mLock) {
+ if (mUserCerts != null) {
+ return mUserCerts;
}
CertificateFactory certFactory;
try {
@@ -57,32 +62,31 @@ public class UserCertificateSource implements CertificateSource {
}
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
final File userCaDir = new File(configDir, "cacerts-added");
- if (!userCaDir.isDirectory()) {
- throw new AssertionError(userCaDir + " is not a directory");
- }
-
Set<X509Certificate> userCerts = new ArraySet<X509Certificate>();
- for (String caFile : userCaDir.list()) {
- InputStream is = null;
- try {
- is = new BufferedInputStream(
- new FileInputStream(new File(userCaDir, caFile)));
- userCerts.add((X509Certificate) certFactory.generateCertificate(is));
- } catch (CertificateException | IOException e) {
- // Don't rethrow to be consistent with conscrypt's cert loading code.
- continue;
- } finally {
- IoUtils.closeQuietly(is);
+ // If the user hasn't added any certificates the directory may not exist.
+ if (userCaDir.isDirectory()) {
+ for (String caFile : userCaDir.list()) {
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(
+ new FileInputStream(new File(userCaDir, caFile)));
+ userCerts.add((X509Certificate) certFactory.generateCertificate(is));
+ } catch (CertificateException | IOException e) {
+ // Don't rethrow to be consistent with conscrypt's cert loading code.
+ continue;
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
}
}
- sUserCerts = userCerts;
- return sUserCerts;
+ mUserCerts = userCerts;
+ return mUserCerts;
}
}
public void onCertificateStorageChange() {
- synchronized (sLock) {
- sUserCerts = null;
+ synchronized (mLock) {
+ mUserCerts = null;
}
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d598291f783a..d146e5e8ce27 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -270,7 +270,7 @@ public abstract class WallpaperService extends Service {
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropRect) {
Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0, outsets);
mCaller.sendMessage(msg);
diff --git a/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl b/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl
new file mode 100644
index 000000000000..622b9ddbf588
--- /dev/null
+++ b/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.AppTransitionAnimationSpec;
+
+/**
+ * A cross-process future to fetch the specifications for app transitions.
+ *
+ * {@hide}
+ */
+interface IAppTransitionAnimationSpecsFuture {
+ AppTransitionAnimationSpec[] get();
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index cc4bcb6e2d77..9e478c1d1e73 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -47,7 +47,7 @@ oneway interface IWindow {
void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
- in Configuration newConfig);
+ in Configuration newConfig, in Rect backDropFrame);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e8a1e32a0e63..7b5f5abe7535 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
@@ -140,6 +141,15 @@ interface IWindowManager
void overridePendingAppTransitionMultiThumb(in AppTransitionAnimationSpec[] specs,
IRemoteCallback startedCallback, IRemoteCallback finishedCallback, boolean scaleUp);
void overridePendingAppTransitionInPlace(String packageName, int anim);
+
+ /**
+ * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is
+ * used for recents, where generating the thumbnails of the specs takes a non-trivial amount of
+ * time, so we want to move that off the critical path for starting the new activity.
+ */
+ void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
+ boolean scaleUp);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
@@ -209,6 +219,11 @@ interface IWindowManager
*/
void cancelTaskWindowTransition(int taskId);
+ /**
+ * Cancels the thumbnail transitions for the given task.
+ */
+ void cancelTaskThumbnailTransition(int taskId);
+
// These can only be called with the SET_ORIENTATION permission.
/**
* Update the current screen rotation based on the current state of
@@ -317,4 +332,16 @@ interface IWindowManager
* @return The frame statistics or null if the window does not exist.
*/
WindowContentFrameStats getWindowContentFrameStats(IBinder token);
+
+ /**
+ * @return the dock side the current docked stack is at; must be one of the
+ * WindowManagerGlobal.DOCKED_* values
+ */
+ int getDockedStackSide();
+
+ /**
+ * Sets whether we are currently in a drag resize operation where we are changing the docked
+ * stack size.
+ */
+ void setDockedStackResizing(boolean resizing);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index dddea210ed83..514f88bc7f24 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -668,7 +668,7 @@ public class SurfaceView extends View {
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropRect) {
SurfaceView surfaceView = mSurfaceView.get();
if (surfaceView != null) {
if (DEBUG) Log.v(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index faeb35313174..ea42399a07eb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -287,6 +287,7 @@ public final class ViewRootImpl implements ViewParent,
final Rect mPendingStableInsets = new Rect();
final Rect mPendingContentInsets = new Rect();
final Rect mPendingOutsets = new Rect();
+ final Rect mPendingBackDropFrame = new Rect();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -1553,6 +1554,10 @@ public final class ViewRootImpl implements ViewParent,
frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing;
+ // If the backdrop frame doesn't equal to a frame, we are starting a resize operation, so
+ // force it to be resized.
+ windowShouldResize |= !mPendingBackDropFrame.equals(mWinFrame);
+
// Determine whether to compute insets.
// If there are no inset listeners remaining then we may still need to compute
// insets in case the old insets were non-empty and must be reset.
@@ -1733,7 +1738,7 @@ public final class ViewRootImpl implements ViewParent,
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
if (mDragResizing != dragResizing) {
if (dragResizing) {
- startDragResizing(frame);
+ startDragResizing(mPendingBackDropFrame);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
@@ -3269,6 +3274,7 @@ public final class ViewRootImpl implements ViewParent,
mPendingStableInsets.set((Rect) args.arg6);
mPendingVisibleInsets.set((Rect) args.arg3);
mPendingOutsets.set((Rect) args.arg7);
+ mPendingBackDropFrame.set((Rect) args.arg8);
args.recycle();
@@ -3294,6 +3300,8 @@ public final class ViewRootImpl implements ViewParent,
mWinFrame.top = t;
mWinFrame.bottom = t + h;
+ mPendingBackDropFrame.set(mWinFrame);
+
if (mView != null) {
forceLayout(mView);
}
@@ -5647,7 +5655,7 @@ public final class ViewRootImpl implements ViewParent,
public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropFrame) {
if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
+ " contentInsets=" + contentInsets.toShortString()
+ " visibleInsets=" + visibleInsets.toShortString()
@@ -5658,7 +5666,7 @@ public final class ViewRootImpl implements ViewParent,
if (mDragResizing) {
synchronized (mWindowCallbacks) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- mWindowCallbacks.get(i).onWindowSizeIsChanging(frame);
+ mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame);
}
}
}
@@ -5679,6 +5687,7 @@ public final class ViewRootImpl implements ViewParent,
args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
+ args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
msg.obj = args;
mHandler.sendMessage(msg);
}
@@ -6677,11 +6686,11 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig) {
+ Configuration newConfig, Rect backDropFrame) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
- visibleInsets, stableInsets, outsets, reportDraw, newConfig);
+ visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame);
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 92e473db556f..2d0435f4c7e3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -50,6 +50,18 @@ import android.util.Log;
* @see android.content.Context#WINDOW_SERVICE
*/
public interface WindowManager extends ViewManager {
+
+ /** @hide */
+ int DOCKED_INVALID = -1;
+ /** @hide */
+ int DOCKED_LEFT = 1;
+ /** @hide */
+ int DOCKED_TOP = 2;
+ /** @hide */
+ int DOCKED_RIGHT = 3;
+ /** @hide */
+ int DOCKED_BOTTOM = 4;
+
/**
* Exception that is thrown when trying to add view whose
* {@link LayoutParams} {@link LayoutParams#token}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index ab99b9efae69..43d643e51c32 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -71,7 +71,7 @@ public final class WindowManagerGlobal {
/**
* The window is being resized by dragging one of the window corners,
- * in this case the surface would be fullsreen-sized. The client should
+ * in this case the surface would be fullscreen-sized. The client should
* render to the actual frame location (instead of (0,curScrollY)).
*/
public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8;
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index c05e0d8f1256..8fb56d4757b6 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -47,6 +47,7 @@ public final class SomeArgs {
public Object arg5;
public Object arg6;
public Object arg7;
+ public Object arg8;
public int argi1;
public int argi2;
public int argi3;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 07bfce727964..8699843eb83b 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -35,7 +35,8 @@ public class BaseIWindow extends IWindow.Stub {
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
- Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig) {
+ Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
+ Rect backDropFrame) {
if (reportDraw) {
try {
mSession.finishDrawing(this);
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index de542b1bc65f..de6e2286fde9 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -372,7 +372,7 @@ public class NonClientDecorView extends LinearLayout
@Override
public void onRequestDraw(boolean reportNextDraw) {
if (mFrameRendererThread != null) {
- mFrameRendererThread.onRequsetDraw(reportNextDraw);
+ mFrameRendererThread.onRequestDraw(reportNextDraw);
} else if (reportNextDraw) {
// If render thread is gone, just report immediately.
if (isAttachedToWindow()) {
@@ -517,7 +517,12 @@ public class NonClientDecorView extends LinearLayout
public void run() {
try {
Looper.prepare();
- mChoreographer = Choreographer.getInstance();
+ synchronized (this) {
+ mChoreographer = Choreographer.getInstance();
+
+ // Draw at least once.
+ mChoreographer.postFrameCallback(this);
+ }
Looper.loop();
} finally {
releaseRenderer();
@@ -580,7 +585,7 @@ public class NonClientDecorView extends LinearLayout
}
}
- public void onRequsetDraw(boolean reportNextDraw) {
+ public void onRequestDraw(boolean reportNextDraw) {
synchronized (this) {
mReportNextDraw = reportNextDraw;
mOldTargetRect.set(0, 0, 0, 0);
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 2df3a4641aeb..3cdf640ae941 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -39,14 +39,14 @@
using namespace android;
-class SkBitmapRegionDecoder {
+class BitmapRegionDecoder {
public:
- SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
+ BitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
fDecoder = decoder;
fWidth = width;
fHeight = height;
}
- ~SkBitmapRegionDecoder() {
+ ~BitmapRegionDecoder() {
delete fDecoder;
}
@@ -91,7 +91,7 @@ static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream
return nullObjectReturn("decoder->buildTileIndex returned false");
}
- SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
+ BitmapRegionDecoder *bm = new BitmapRegionDecoder(decoder, width, height);
return GraphicsJNI::createBitmapRegionDecoder(env, bm);
}
@@ -166,7 +166,7 @@ static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
*/
static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
jint start_x, jint start_y, jint width, jint height, jobject options) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
jobject tileBitmap = NULL;
SkImageDecoder *decoder = brd->getDecoder();
int sampleSize = 1;
@@ -245,17 +245,17 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
}
static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
return static_cast<jint>(brd->getHeight());
}
static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
return static_cast<jint>(brd->getWidth());
}
static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
- SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
delete brd;
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 068517a12288..ed4401978f7b 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -439,7 +439,7 @@ int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
}
-jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap)
{
SkASSERT(bitmap != NULL);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index bcd834b0501b..90f8291fe846 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -12,7 +12,7 @@
#include <Canvas.h>
#include <jni.h>
-class SkBitmapRegionDecoder;
+class BitmapRegionDecoder;
class SkCanvas;
namespace android {
@@ -90,7 +90,7 @@ public:
static jobject createRegion(JNIEnv* env, SkRegion* region);
- static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
+ static jobject createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap);
static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable);
diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp
index b9dd77af8c8a..ec6471e3dc83 100644
--- a/core/jni/android_hardware_Radio.cpp
+++ b/core/jni/android_hardware_Radio.cpp
@@ -93,6 +93,7 @@ static struct {
jfieldID mRds;
jfieldID mTa;
jfieldID mAf;
+ jfieldID mEa;
} gRadioFmBandConfigFields;
static const char* const kRadioAmBandConfigClassPathName =
@@ -117,6 +118,7 @@ static struct {
jmethodID putIntFromNative;
jmethodID putStringFromNative;
jmethodID putBitmapFromNative;
+ jmethodID putClockFromNative;
} gRadioMetadataMethods;
static Mutex gLock;
@@ -171,7 +173,8 @@ static jint convertBandDescriptorFromNative(JNIEnv *env,
nBandconfig->band.fm.stereo,
nBandconfig->band.fm.rds != RADIO_RDS_NONE,
nBandconfig->band.fm.ta,
- nBandconfig->band.fm.af);
+ nBandconfig->band.fm.af,
+ nBandconfig->band.fm.ea);
} else if (nBandconfig->band.type == RADIO_BAND_AM) {
*jBandDescriptor = env->NewObject(gRadioAmBandDescriptorClass, gRadioAmBandDescriptorCstor,
nBandconfig->region, nBandconfig->band.type,
@@ -205,7 +208,8 @@ static jint convertBandConfigFromNative(JNIEnv *env,
nBandconfig->band.fm.stereo,
nBandconfig->band.fm.rds != RADIO_RDS_NONE,
nBandconfig->band.fm.ta,
- nBandconfig->band.fm.af);
+ nBandconfig->band.fm.af,
+ nBandconfig->band.fm.ea);
} else if (nBandconfig->band.type == RADIO_BAND_AM) {
*jBandConfig = env->NewObject(gRadioAmBandConfigClass, gRadioAmBandConfigCstor,
nBandconfig->region, nBandconfig->band.type,
@@ -284,6 +288,18 @@ static jint convertMetadataFromNative(JNIEnv *env,
}
env->DeleteLocalRef(jData);
} break;
+ case RADIO_METADATA_TYPE_CLOCK: {
+ ALOGV("%s RADIO_METADATA_TYPE_CLOCK %d", __FUNCTION__, key);
+ radio_metadata_clock_t *clock = (radio_metadata_clock_t *) value;
+ jStatus =
+ env->CallIntMethod(*jMetadata,
+ gRadioMetadataMethods.putClockFromNative,
+ key, (jint) clock->utc_seconds_since_epoch,
+ (jint) clock->timezone_offset_in_minutes);
+ if (jStatus == 0) {
+ jCount++;
+ }
+ } break;
}
}
return jCount;
@@ -351,6 +367,7 @@ static jint convertBandConfigToNative(JNIEnv *env,
nBandconfig->region);
nBandconfig->band.fm.ta = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mTa);
nBandconfig->band.fm.af = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mAf);
+ nBandconfig->band.fm.ea = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mEa);
} else if (env->IsInstanceOf(jBandConfig, gRadioAmBandConfigClass)) {
nBandconfig->band.am.stereo =
env->GetBooleanField(jBandConfig, gRadioAmBandConfigFields.mStereo);
@@ -518,6 +535,7 @@ void JNIRadioCallback::onEvent(struct radio_event *event)
break;
case RADIO_EVENT_ANTENNA:
case RADIO_EVENT_TA:
+ case RADIO_EVENT_EA:
case RADIO_EVENT_CONTROL:
jArg2 = event->on ? 1 : 0;
break;
@@ -878,7 +896,7 @@ int register_android_hardware_Radio(JNIEnv *env)
jclass fmBandDescriptorClass = FindClassOrDie(env, kRadioFmBandDescriptorClassPathName);
gRadioFmBandDescriptorClass = MakeGlobalRefOrDie(env, fmBandDescriptorClass);
gRadioFmBandDescriptorCstor = GetMethodIDOrDie(env, fmBandDescriptorClass, "<init>",
- "(IIIIIZZZZ)V");
+ "(IIIIIZZZZZ)V");
jclass amBandDescriptorClass = FindClassOrDie(env, kRadioAmBandDescriptorClassPathName);
gRadioAmBandDescriptorClass = MakeGlobalRefOrDie(env, amBandDescriptorClass);
@@ -894,11 +912,13 @@ int register_android_hardware_Radio(JNIEnv *env)
jclass fmBandConfigClass = FindClassOrDie(env, kRadioFmBandConfigClassPathName);
gRadioFmBandConfigClass = MakeGlobalRefOrDie(env, fmBandConfigClass);
gRadioFmBandConfigCstor = GetMethodIDOrDie(env, fmBandConfigClass, "<init>",
- "(IIIIIZZZZ)V");
+ "(IIIIIZZZZZ)V");
gRadioFmBandConfigFields.mStereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z");
gRadioFmBandConfigFields.mRds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z");
gRadioFmBandConfigFields.mTa = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z");
gRadioFmBandConfigFields.mAf = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z");
+ gRadioFmBandConfigFields.mEa =
+ GetFieldIDOrDie(env, fmBandConfigClass, "mEa", "Z");
jclass amBandConfigClass = FindClassOrDie(env, kRadioAmBandConfigClassPathName);
@@ -924,6 +944,9 @@ int register_android_hardware_Radio(JNIEnv *env)
gRadioMetadataMethods.putBitmapFromNative = GetMethodIDOrDie(env, metadataClass,
"putBitmapFromNative",
"(I[B)I");
+ gRadioMetadataMethods.putClockFromNative = GetMethodIDOrDie(env, metadataClass,
+ "putClockFromNative",
+ "(IJI)I");
RegisterMethodsOrDie(env, kRadioManagerClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index bfb0d103d7f2..28756f5d0384 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -45,8 +45,12 @@
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
- <!-- Width of a divider bar used to resize docked stacks. -->
- <dimen name="docked_stack_divider_thickness">24dp</dimen>
+ <!-- Width of the window of the divider bar used to resize docked stacks. -->
+ <dimen name="docked_stack_divider_thickness">48dp</dimen>
+
+ <!-- How much the content in the divider is inset from the window bounds when resting. Used to
+ calculate the bounds of the stacks-->
+ <dimen name="docked_stack_divider_insets">18dp</dimen>
<!-- Min width for a tablet device -->
<dimen name="min_xlarge_screen_width">800dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5c65366b41b8..cda7faa1f09b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1481,6 +1481,7 @@
<java-symbol type="bool" name="config_supportAutoRotation" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
<java-symbol type="dimen" name="docked_stack_divider_thickness" />
+ <java-symbol type="dimen" name="docked_stack_divider_insets" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
@@ -1724,7 +1725,6 @@
<java-symbol type="integer" name="config_undockedHdmiRotation" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
- <java-symbol type="layout" name="docked_stack_divider" />
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index b2044e1dcb4b..d05c66a8437c 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -144,7 +144,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mAlpha = alpha;
if (mCurrDrawable != null) {
if (mEnterAnimationEnd == 0) {
- mCurrDrawable.mutate().setAlpha(alpha);
+ mCurrDrawable.setAlpha(alpha);
} else {
animate(false);
}
@@ -162,7 +162,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mDrawableContainerState.mDither != dither) {
mDrawableContainerState.mDither = dither;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setDither(mDrawableContainerState.mDither);
+ mCurrDrawable.setDither(mDrawableContainerState.mDither);
}
}
}
@@ -175,7 +175,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDrawableContainerState.mColorFilter = colorFilter;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setColorFilter(colorFilter);
+ mCurrDrawable.setColorFilter(colorFilter);
}
}
}
@@ -188,7 +188,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDrawableContainerState.mTintList = tint;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setTintList(tint);
+ mCurrDrawable.setTintList(tint);
}
}
}
@@ -201,7 +201,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDrawableContainerState.mTintMode = tintMode;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setTintMode(tintMode);
+ mCurrDrawable.setTintMode(tintMode);
}
}
}
@@ -244,7 +244,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mDrawableContainerState.mAutoMirrored != mirrored) {
mDrawableContainerState.mAutoMirrored = mirrored;
if (mCurrDrawable != null) {
- mCurrDrawable.mutate().setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+ mCurrDrawable.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
}
}
}
@@ -266,7 +266,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mCurrDrawable != null) {
mCurrDrawable.jumpToCurrentState();
if (mHasAlpha) {
- mCurrDrawable.mutate().setAlpha(mAlpha);
+ mCurrDrawable.setAlpha(mAlpha);
}
}
if (mExitAnimationEnd != 0) {
@@ -499,8 +499,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
* @param d The drawable to initialize.
*/
private void initializeDrawableForDisplay(Drawable d) {
- d.mutate();
-
if (mDrawableContainerState.mEnterFadeDuration <= 0 && mHasAlpha) {
d.setAlpha(mAlpha);
}
@@ -540,13 +538,12 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mCurrDrawable != null) {
if (mEnterAnimationEnd != 0) {
if (mEnterAnimationEnd <= now) {
- mCurrDrawable.mutate().setAlpha(mAlpha);
+ mCurrDrawable.setAlpha(mAlpha);
mEnterAnimationEnd = 0;
} else {
int animAlpha = (int)((mEnterAnimationEnd-now)*255)
/ mDrawableContainerState.mEnterFadeDuration;
- if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha);
- mCurrDrawable.mutate().setAlpha(((255-animAlpha)*mAlpha)/255);
+ mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255);
animating = true;
}
}
@@ -563,8 +560,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
} else {
int animAlpha = (int)((mExitAnimationEnd-now)*255)
/ mDrawableContainerState.mExitFadeDuration;
- if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha);
- mLastDrawable.mutate().setAlpha((animAlpha*mAlpha)/255);
+ mLastDrawable.setAlpha((animAlpha*mAlpha)/255);
animating = true;
}
}
@@ -656,7 +652,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
int mChangingConfigurations;
int mChildrenChangingConfigurations;
- SparseArray<ConstantStateFuture> mDrawableFutures;
+ SparseArray<ConstantState> mDrawableFutures;
Drawable[] mDrawables;
int mNumChildren;
@@ -757,7 +753,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDrawables = new Drawable[origDr.length];
mNumChildren = orig.mNumChildren;
- final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures;
+ final SparseArray<ConstantState> origDf = orig.mDrawableFutures;
if (origDf != null) {
mDrawableFutures = origDf.clone();
} else {
@@ -770,8 +766,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
final int N = mNumChildren;
for (int i = 0; i < N; i++) {
if (origDr[i] != null) {
- if (origDr[i].getConstantState() != null) {
- mDrawableFutures.put(i, new ConstantStateFuture(origDr[i]));
+ final ConstantState cs = origDr[i].getConstantState();
+ if (cs != null) {
+ mDrawableFutures.put(i, cs);
} else {
mDrawables[i] = origDr[i];
}
@@ -815,18 +812,26 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return mDrawables.length;
}
- private final void createAllFutures() {
+ private void createAllFutures() {
if (mDrawableFutures != null) {
final int futureCount = mDrawableFutures.size();
for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) {
final int index = mDrawableFutures.keyAt(keyIndex);
- mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this);
+ final ConstantState cs = mDrawableFutures.valueAt(keyIndex);
+ mDrawables[index] = prepareDrawable(cs.newDrawable(mSourceRes));
}
mDrawableFutures = null;
}
}
+ private Drawable prepareDrawable(Drawable child) {
+ child.setLayoutDirection(mLayoutDirection);
+ child.setCallback(mOwner);
+ child = child.mutate();
+ return child;
+ }
+
public final int getChildCount() {
return mNumChildren;
}
@@ -851,7 +856,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
if (mDrawableFutures != null) {
final int keyIndex = mDrawableFutures.indexOfKey(index);
if (keyIndex >= 0) {
- final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
+ final ConstantState cs = mDrawableFutures.valueAt(keyIndex);
+ final Drawable prepared = prepareDrawable(cs.newDrawable(mSourceRes));
mDrawables[index] = prepared;
mDrawableFutures.removeAt(keyIndex);
if (mDrawableFutures.size() == 0) {
@@ -938,7 +944,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return true;
}
} else {
- final ConstantStateFuture future = mDrawableFutures.get(i);
+ final ConstantState future = mDrawableFutures.get(i);
if (future != null && future.canApplyTheme()) {
return true;
}
@@ -1173,49 +1179,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
return pixelCount;
}
-
- /**
- * Class capable of cloning a Drawable from another Drawable's
- * ConstantState.
- */
- private static class ConstantStateFuture {
- private final ConstantState mConstantState;
-
- private ConstantStateFuture(Drawable source) {
- mConstantState = source.getConstantState();
- }
-
- /**
- * Obtains and prepares the Drawable represented by this future.
- *
- * @param state the container into which this future will be placed
- * @return a prepared Drawable
- */
- public Drawable get(DrawableContainerState state) {
- final Drawable result;
- if (state.mSourceRes == null) {
- result = mConstantState.newDrawable();
- } else {
- result = mConstantState.newDrawable(state.mSourceRes);
- }
- result.setLayoutDirection(state.mLayoutDirection);
- result.setCallback(state.mOwner);
-
- if (state.mMutated) {
- result.mutate();
- }
-
- return result;
- }
-
- /**
- * Whether the constant state wrapped by this future can apply a
- * theme.
- */
- public boolean canApplyTheme() {
- return mConstantState.canApplyTheme();
- }
- }
}
protected void setConstantState(DrawableContainerState state) {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 1ede1052d21b..651b45334ee0 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -1601,8 +1601,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
}
+ // Don't apply padding and insets for children that don't have
+ // an intrinsic dimension.
final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
- final int w = minWidth + insetL + insetR + padL + padR;
+ final int w = minWidth < 0 ? -1 : minWidth + insetL + insetR + padL + padR;
if (w > width) {
width = w;
}
@@ -1631,8 +1633,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
continue;
}
+ // Don't apply padding and insets for children that don't have
+ // an intrinsic dimension.
final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
- final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
+ final int h = minHeight < 0 ? -1 : minHeight + r.mInsetT + r.mInsetB + padT + padB;
if (h > height) {
height = h;
}
diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp
index bdb0c3d38f6f..8489acf6246f 100644
--- a/libs/androidfw/tests/AppAsLib_test.cpp
+++ b/libs/androidfw/tests/AppAsLib_test.cpp
@@ -16,7 +16,6 @@
#include <androidfw/ResourceTypes.h>
-#include "data/basic/R.h"
#include "data/appaslib/R.h"
#include <gtest/gtest.h>
@@ -25,29 +24,45 @@ using namespace android;
namespace {
-#include "data/basic/basic_arsc.h"
+#include "data/appaslib/appaslib_arsc.h"
+#include "data/appaslib/appaslib_lib_arsc.h"
+// This tests the app resources loaded as app.
TEST(AppAsLibTest, loadedAsApp) {
ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len));
Res_value val;
- ssize_t block = table.getResource(base::R::integer::number2, &val);
+ ssize_t block = table.getResource(appaslib::R::app::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(base::R::array::integerArray1, val.data);
+ ASSERT_EQ(appaslib::R::app::array::integerArray1, val.data);
}
+// This tests the app resources loaded as shared-lib.
TEST(AppAsLibTest, loadedAsSharedLib) {
ResTable table;
// Load as shared library.
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len, NULL, 0, -1, false, true));
+ ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len, NULL, 0, -1, false, true));
Res_value val;
- ssize_t block = table.getResource(appaslib::R::integer::number2, &val);
+ ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(appaslib::R::array::integerArray1, val.data);
+ ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data);
+}
+
+// This tests the shared-lib loaded with appAsLib as true.
+TEST(AppAsLibTest, loadedSharedLib) {
+ ResTable table;
+ // Load shared library with appAsLib as true.
+ ASSERT_EQ(NO_ERROR, table.add(appaslib_lib_arsc, appaslib_lib_arsc_len, NULL, 0, -1, false, true));
+
+ Res_value val;
+ ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data);
}
}
diff --git a/core/res/res/layout/docked_stack_divider.xml b/libs/androidfw/tests/data/appaslib/AndroidManifest.xml
index aa6e68d57af9..e00045b0aa12 100644
--- a/core/res/res/layout/docked_stack_divider.xml
+++ b/libs/androidfw/tests/data/appaslib/AndroidManifest.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/docked_stack_divider_thickness"
- android:layout_height="match_parent"
- android:background="@android:color/black"
- />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h
index f89d4bfdd15e..3af921a7ba65 100644
--- a/libs/androidfw/tests/data/appaslib/R.h
+++ b/libs/androidfw/tests/data/appaslib/R.h
@@ -19,19 +19,33 @@
namespace appaslib {
namespace R {
-
+namespace lib {
namespace integer {
enum {
- number2 = 0x02040001, // default
+ number1 = 0x02020000, // default
};
}
namespace array {
enum {
- integerArray1 = 0x02060000, // default
+ integerArray1 = 0x02030000, // default
+ };
+}
+} // namespace lib
+
+namespace app {
+namespace integer {
+ enum {
+ number1 = 0x7f020000, // default
};
}
+namespace array {
+ enum {
+ integerArray1 = 0x7f030000, // default
+ };
+}
+} // namespace app
} // namespace R
} // namespace appaslib
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h
new file mode 100644
index 000000000000..be176ab5e63f
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h
@@ -0,0 +1,68 @@
+unsigned char appaslib_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x04, 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, 0xdc, 0x02, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 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, 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, 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, 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, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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,
+ 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00,
+ 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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,
+ 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+ 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x34, 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, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x7f,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x34, 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, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x03, 0x00, 0x00, 0x00
+};
+unsigned int appaslib_arsc_len = 772;
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h
new file mode 100644
index 000000000000..099285a17aad
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h
@@ -0,0 +1,68 @@
+unsigned char appaslib_lib_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x04, 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, 0xdc, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 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, 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, 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, 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, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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,
+ 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00,
+ 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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,
+ 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+ 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x34, 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, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x34, 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, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x03, 0x00, 0x00, 0x00
+};
+unsigned int appaslib_lib_arsc_len = 772;
diff --git a/libs/androidfw/tests/data/appaslib/build b/libs/androidfw/tests/data/appaslib/build
new file mode 100755
index 000000000000..e4bd88b4032c
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
+
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc appaslib.arsc && \
+xxd -i appaslib.arsc > appaslib_arsc.h && \
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f --shared-lib && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc appaslib_lib.arsc && \
+xxd -i appaslib_lib.arsc > appaslib_lib_arsc.h \
+
diff --git a/libs/androidfw/tests/data/appaslib/res/values/values.xml b/libs/androidfw/tests/data/appaslib/res/values/values.xml
new file mode 100644
index 000000000000..39b99a6bfb81
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/res/values/values.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="number1">@array/integerArray1</integer>
+ <integer-array name="integerArray1">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </integer-array>
+</resources>
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index cb638a476b14..772aa72b7fdc 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1398,6 +1398,7 @@ class DrawRenderNodeOp : public DrawBoundedOp {
friend class RenderNode; // grant RenderNode access to info of child
friend class DisplayList; // grant DisplayList access to info of child
friend class DisplayListCanvas;
+ friend class TestUtils;
public:
DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
: DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 163f7cc4b1d0..68f80ea359cd 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -16,12 +16,14 @@
#include "OpReorderer.h"
-#include "utils/PaintUtils.h"
-#include "RenderNode.h"
#include "LayerUpdateQueue.h"
+#include "RenderNode.h"
+#include "utils/FatVector.h"
+#include "utils/PaintUtils.h"
-#include "SkCanvas.h"
-#include "utils/Trace.h"
+#include <SkCanvas.h>
+#include <utils/Trace.h>
+#include <utils/TypeHelpers.h>
namespace android {
namespace uirenderer {
@@ -359,7 +361,7 @@ void OpReorderer::onViewportInitialized() {}
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
- if (node.applyViewProperties(mCanvasState)) {
+ if (node.applyViewProperties(mCanvasState, mAllocator)) {
// not rejected so render
if (node.getLayer()) {
// HW layer
@@ -375,6 +377,93 @@ void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
}
}
+typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair;
+
+template <typename V>
+static void buildZSortedChildList(V* zTranslatedNodes,
+ const DisplayList& displayList, const DisplayList::Chunk& chunk) {
+ if (chunk.beginChildIndex == chunk.endChildIndex) return;
+
+ for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
+ RenderNodeOp* childOp = displayList.getChildren()[i];
+ RenderNode* child = childOp->renderNode;
+ float childZ = child->properties().getZ();
+
+ if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
+ zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp));
+ childOp->skipInOrderDraw = true;
+ } else if (!child->properties().getProjectBackwards()) {
+ // regular, in order drawing DisplayList
+ childOp->skipInOrderDraw = false;
+ }
+ }
+
+ // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end());
+}
+
+template <typename V>
+static size_t findNonNegativeIndex(const V& zTranslatedNodes) {
+ for (size_t i = 0; i < zTranslatedNodes.size(); i++) {
+ if (zTranslatedNodes[i].key >= 0.0f) return i;
+ }
+ return zTranslatedNodes.size();
+}
+
+template <typename V>
+void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
+ || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) {
+ // no 3d children to draw
+ return;
+ }
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+ size_t drawIndex, shadowIndex, endIndex;
+ if (mode == ChildrenSelectMode::Negative) {
+ drawIndex = 0;
+ endIndex = nonNegativeIndex;
+ shadowIndex = endIndex; // draw no shadows
+ } else {
+ drawIndex = nonNegativeIndex;
+ endIndex = size;
+ shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+ }
+
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
+ // attempt to render the shadow if the caster about to be drawn is its caster,
+ // OR if its caster's Z value is similar to the previous potential caster
+ if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) {
+ deferShadow(*casterNodeOp);
+
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
+ deferRenderNodeOp(*childOp);
+ drawIndex++;
+ }
+}
+
+void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+ // TODO
+}
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -388,17 +477,20 @@ void OpReorderer::deferImpl(const DisplayList& displayList) {
MAP_OPS(OP_RECEIVER)
};
for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
+ buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
+
+ defer3dChildren(ChildrenSelectMode::Negative, zTranslatedNodes);
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
const RecordedOp* op = displayList.getOps()[opIndex];
receivers[op->opId](*this, *op);
}
+ defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes);
}
}
-void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
- if (op.renderNode->nothingToDraw()) {
- return;
- }
+void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+ if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
// apply state from RecordedOp
@@ -412,6 +504,12 @@ void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
mCanvasState.restoreToCount(count);
}
+void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+ if (!op.skipInOrderDraw) {
+ deferRenderNodeOp(op);
+ }
+}
+
static batchid_t tessellatedBatchId(const SkPaint& paint) {
return paint.getPathEffect()
? OpBatchType::AlphaMaskTexture
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 77be40292205..936b6ed9566e 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -182,6 +182,10 @@ public:
virtual GLuint getTargetFbo() const override { return 0; }
private:
+ enum class ChildrenSelectMode {
+ Negative,
+ Positive
+ };
void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
void restoreForLayer();
@@ -195,8 +199,15 @@ private:
// should always be surrounded by a save/restore pair
void deferNodePropsAndOps(RenderNode& node);
+ void deferShadow(const RenderNodeOp& casterOp);
+
void deferImpl(const DisplayList& displayList);
+ template <typename V>
+ void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
+
+ void deferRenderNodeOp(const RenderNodeOp& op);
+
void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
/**
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 9ae868a9858c..04af8e36348a 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -93,6 +93,7 @@ struct RenderNodeOp : RecordedOp {
: SUPER_PAINTLESS(RenderNodeOp)
, renderNode(renderNode) {}
RenderNode * renderNode; // not const, since drawing modifies it (somehow...)
+ bool skipInOrderDraw = false;
};
struct BitmapOp : RecordedOp {
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 7c460b1bbc1a..e9885552369d 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -40,7 +40,7 @@ void RecordingCanvas::reset(int width, int height) {
mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3());
- mDeferredBarrierType = kBarrier_InOrder;
+ mDeferredBarrierType = DeferredBarrierType::InOrder;
mState.setDirtyClip(false);
mRestoreSaveCount = -1;
}
@@ -432,17 +432,17 @@ size_t RecordingCanvas::addOp(RecordedOp* op) {
// TODO: validate if "addDrawOp" quickrejection logic is useful before adding
int insertIndex = mDisplayList->ops.size();
mDisplayList->ops.push_back(op);
- if (mDeferredBarrierType != kBarrier_None) {
+ if (mDeferredBarrierType != DeferredBarrierType::None) {
// op is first in new chunk
mDisplayList->chunks.emplace_back();
DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
newChunk.beginOpIndex = insertIndex;
newChunk.endOpIndex = insertIndex + 1;
- newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
+ newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
int nextChildIndex = mDisplayList->children.size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
- mDeferredBarrierType = kBarrier_None;
+ mDeferredBarrierType = DeferredBarrierType::None;
} else {
// standard case - append to existing chunk
mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 8a564758e7b1..fc84c98b76a5 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -39,6 +39,11 @@ class OpReceiver;
struct RecordedOp;
class RecordingCanvas: public Canvas, public CanvasStateClient {
+ enum class DeferredBarrierType {
+ None,
+ InOrder,
+ OutOfOrder,
+ };
public:
RecordingCanvas(size_t width, size_t height);
virtual ~RecordingCanvas();
@@ -49,7 +54,10 @@ public:
// ----------------------------------------------------------------------------
// MISC HWUI OPERATIONS - TODO: CATEGORIZE
// ----------------------------------------------------------------------------
- void insertReorderBarrier(bool enableReorder) {}
+ void insertReorderBarrier(bool enableReorder) {
+ mDeferredBarrierType = enableReorder
+ ? DeferredBarrierType::OutOfOrder : DeferredBarrierType::InOrder;
+ }
void drawRenderNode(RenderNode* renderNode);
// ----------------------------------------------------------------------------
@@ -176,11 +184,6 @@ public:
virtual bool drawTextAbsolutePos() const override { return false; }
private:
- enum DeferredBarrierType {
- kBarrier_None,
- kBarrier_InOrder,
- kBarrier_OutOfOrder,
- };
void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
@@ -290,7 +293,7 @@ private:
CanvasState mState;
std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
ResourceCache& mResourceCache;
- DeferredBarrierType mDeferredBarrierType = kBarrier_None;
+ DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None;
DisplayList* mDisplayList = nullptr;
bool mHighContrastText = false;
SkAutoTUnref<SkDrawFilter> mDrawFilter;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 0601944905a8..15ca718481fe 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -282,9 +282,11 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
damageSelf(info);
transformUpdateNeeded = true;
#if HWUI_NEW_OPS
- } else if (mLayer->viewportWidth != getWidth() || mLayer->viewportHeight != getHeight()) {
- // TODO: allow it to grow larger
- if (getWidth() > mLayer->texture.width || getHeight() > mLayer->texture.height) {
+ } else if (mLayer->viewportWidth != (uint32_t) getWidth()
+ || mLayer->viewportHeight != (uint32_t)getHeight()) {
+ // TODO: allow node's layer to grow larger
+ if ((uint32_t)getWidth() > mLayer->texture.width
+ || (uint32_t)getHeight() > mLayer->texture.height) {
#else
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
@@ -304,7 +306,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
if (info.errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << getName();
- const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
+ const int maxTextureSize = Caches::getInstance().maxTextureSize;
if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
err << ", size " << getWidth() << "x" << getHeight()
<< " exceeds max size " << maxTextureSize;
@@ -518,7 +520,7 @@ void RenderNode::decParentRefCount() {
}
}
-bool RenderNode::applyViewProperties(CanvasState& canvasState) const {
+bool RenderNode::applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const {
const Outline& outline = properties().getOutline();
if (properties().getAlpha() <= 0
|| (outline.getShouldClip() && outline.isEmpty())
@@ -542,6 +544,48 @@ bool RenderNode::applyViewProperties(CanvasState& canvasState) const {
canvasState.concatMatrix(*properties().getTransformMatrix());
}
}
+
+ const bool isLayer = properties().effectiveLayerType() != LayerType::None;
+ int clipFlags = properties().getClippingFlags();
+ if (properties().getAlpha() < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+ }
+ if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) {
+ // simply scale rendering content's alpha
+ canvasState.scaleAlpha(properties().getAlpha());
+ } else {
+ // savelayer needed to create an offscreen buffer
+ Rect layerBounds(0, 0, getWidth(), getHeight());
+ if (clipFlags) {
+ properties().getClippingRectForFlags(clipFlags, &layerBounds);
+ clipFlags = 0; // all clipping done by savelayer
+ }
+ LOG_ALWAYS_FATAL("TODO: savelayer");
+ }
+
+ if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) {
+ // pretend alpha always causes savelayer to warn about
+ // performance problem affecting old versions
+ ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), getWidth(), getHeight());
+ }
+ }
+ if (clipFlags) {
+ Rect clipRect;
+ properties().getClippingRectForFlags(clipFlags, &clipRect);
+ canvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+ SkRegion::kIntersect_Op);
+ }
+
+ // TODO: support nesting round rect clips
+ if (mProperties.getRevealClip().willClip()) {
+ Rect bounds;
+ mProperties.getRevealClip().getBounds(&bounds);
+ canvasState.setClippingRoundRect(allocator,
+ bounds, mProperties.getRevealClip().getRadius());
+ } else if (mProperties.getOutline().willClip()) {
+ canvasState.setClippingOutline(allocator, &(mProperties.getOutline()));
+ }
return !canvasState.quickRejectConservative(
0, 0, properties().getWidth(), properties().getHeight());
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 3500cb200a51..bae5ebe3f754 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -171,11 +171,11 @@ public:
return mStagingProperties;
}
- uint32_t getWidth() {
+ int getWidth() const {
return properties().getWidth();
}
- uint32_t getHeight() {
+ int getHeight() const {
return properties().getHeight();
}
@@ -188,7 +188,7 @@ public:
AnimatorManager& animators() { return mAnimatorManager; }
// Returns false if the properties dictate the subtree contained in this RenderNode won't render
- bool applyViewProperties(CanvasState& canvasState) const;
+ bool applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const;
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
@@ -201,7 +201,6 @@ public:
|| properties().getScaleY() == 0;
}
- // Only call if RenderNode has DisplayList...
const DisplayList* getDisplayList() const {
return mDisplayList;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index abef806a6975..ca7789e6f19a 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,23 +16,24 @@
#ifndef RENDERNODEPROPERTIES_H
#define RENDERNODEPROPERTIES_H
-#include <algorithm>
-#include <stddef.h>
-#include <vector>
-#include <cutils/compiler.h>
-#include <androidfw/ResourceTypes.h>
-#include <utils/Log.h>
+#include "Caches.h"
+#include "DeviceInfo.h"
+#include "Rect.h"
+#include "RevealClip.h"
+#include "Outline.h"
+#include "utils/MathUtils.h"
#include <SkCamera.h>
#include <SkMatrix.h>
#include <SkRegion.h>
#include <SkXfermode.h>
-#include "Caches.h"
-#include "Rect.h"
-#include "RevealClip.h"
-#include "Outline.h"
-#include "utils/MathUtils.h"
+#include <algorithm>
+#include <stddef.h>
+#include <vector>
+#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
class SkBitmap;
class SkColorFilter;
@@ -608,10 +609,11 @@ public:
}
bool promotedToLayer() const {
- const int maxTextureSize = Caches::getInstance().maxTextureSize;
+ const DeviceInfo* deviceInfo = DeviceInfo::get();
+ LOG_ALWAYS_FATAL_IF(!deviceInfo, "DeviceInfo uninitialized");
return mLayerProperties.mType == LayerType::None
- && mPrimitiveFields.mWidth <= maxTextureSize
- && mPrimitiveFields.mHeight <= maxTextureSize
+ && mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
+ && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
&& (mComputedFields.mNeedLayerForFunctors
|| (!MathUtils::isZero(mPrimitiveFields.mAlpha)
&& mPrimitiveFields.mAlpha < 1
diff --git a/libs/hwui/unit_tests/DeviceInfoTests.cpp b/libs/hwui/unit_tests/DeviceInfoTests.cpp
index c3c68aeb8818..17236bdf0bf7 100644
--- a/libs/hwui/unit_tests/DeviceInfoTests.cpp
+++ b/libs/hwui/unit_tests/DeviceInfoTests.cpp
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-
-#include "DeviceInfo.h"
+#include <DeviceInfo.h>
#include <gtest/gtest.h>
@@ -23,10 +22,9 @@ using namespace android;
using namespace android::uirenderer;
TEST(DeviceInfo, basic) {
- const DeviceInfo* di = DeviceInfo::get();
- EXPECT_EQ(nullptr, di) << "DeviceInfo was already initialized?";
+ // can't assert state before init - another test may have initialized the singleton
DeviceInfo::initialize();
- di = DeviceInfo::get();
+ const DeviceInfo* di = DeviceInfo::get();
ASSERT_NE(nullptr, di) << "DeviceInfo initialization failed";
EXPECT_EQ(2048, di->maxTextureSize()) << "Max texture size didn't match";
}
diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
index 9d625bc62696..ef205ec50ffc 100644
--- a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
@@ -32,7 +32,7 @@ TEST(LayerUpdateQueue, construct) {
// sync node properties, so properties() reflects correct width and height
static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) {
sp<RenderNode> node = TestUtils::createNode(0, 0, width, height);
- TestUtils::syncNodePropertiesAndDisplayList(node);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
return node;
}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index 09b10c3af4f9..f67c24a1bce8 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -198,8 +198,7 @@ TEST(OpReorderer, renderNode) {
canvas.restore();
});
- TestUtils::syncNodePropertiesAndDisplayList(child);
- TestUtils::syncNodePropertiesAndDisplayList(parent);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
std::vector< sp<RenderNode> > nodes;
nodes.push_back(parent.get());
@@ -225,7 +224,7 @@ TEST(OpReorderer, clipped) {
SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- TestUtils::syncNodePropertiesAndDisplayList(node);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
std::vector< sp<RenderNode> > nodes;
nodes.push_back(node.get());
@@ -409,7 +408,7 @@ TEST(OpReorderer, hwLayerSimple) {
OffscreenBuffer** bufferHandle = node->getLayerHandle();
*bufferHandle = (OffscreenBuffer*) 0x0124;
- TestUtils::syncNodePropertiesAndDisplayList(node);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
std::vector< sp<RenderNode> > nodes;
nodes.push_back(node.get());
@@ -483,7 +482,8 @@ public:
}
};
TEST(OpReorderer, hwLayerComplex) {
- sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, [](RecordingCanvas& canvas) {
+ auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
+ [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
@@ -493,7 +493,8 @@ TEST(OpReorderer, hwLayerComplex) {
*(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
RenderNode* childPtr = child.get();
- sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
+ [childPtr](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(0, 0, 200, 200, paint);
@@ -506,8 +507,7 @@ TEST(OpReorderer, hwLayerComplex) {
parent->setPropertyFieldsDirty(RenderNode::GENERIC);
*(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
- TestUtils::syncNodePropertiesAndDisplayList(child);
- TestUtils::syncNodePropertiesAndDisplayList(parent);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
std::vector< sp<RenderNode> > nodes;
nodes.push_back(parent.get());
@@ -527,5 +527,170 @@ TEST(OpReorderer, hwLayerComplex) {
*(parent->getLayerHandle()) = nullptr;
}
+
+class ZReorderTestRenderer : public TestRendererBase {
+public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
+ EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
+ }
+};
+static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
+ canvas->drawRect(0, 0, 100, 100, paint);
+}
+static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [expectedDrawOrder](RecordingCanvas& canvas) {
+ drawOrderedRect(&canvas, expectedDrawOrder);
+ });
+ node->mutateStagingProperties().setTranslationZ(z);
+ node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
+ canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
+}
+TEST(OpReorderer, zReorder) {
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
+ drawOrderedRect(&canvas, 1);
+ canvas.insertReorderBarrier(true);
+ drawOrderedNode(&canvas, 6, 2.0f);
+ drawOrderedRect(&canvas, 3);
+ drawOrderedNode(&canvas, 4, 0.0f);
+ drawOrderedRect(&canvas, 5);
+ drawOrderedNode(&canvas, 2, -2.0f);
+ drawOrderedNode(&canvas, 7, 2.0f);
+ canvas.insertReorderBarrier(false);
+ drawOrderedRect(&canvas, 8);
+ drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
+ });
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, nodes);
+
+ ZReorderTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(10, renderer.getIndex());
+};
+
+
+class PropertyTestRenderer : public TestRendererBase {
+public:
+ PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
+ : mCallback(callback) {}
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(mIndex++, 0);
+ mCallback(op, state);
+ }
+ std::function<void(const RectOp&, const BakedOpState&)> mCallback;
+};
+
+static void testProperty(
+ std::function<int(RenderProperties&)> propSetupCallback,
+ std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties()));
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(100, 100), 200, 200, nodes);
+
+ PropertyTestRenderer renderer(opValidateCallback);
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
+}
+
+TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
+ testProperty([](RenderProperties& properties) {
+ properties.setAlpha(0.5f);
+ properties.setHasOverlappingRendering(false);
+ return RenderNode::ALPHA | RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
+ });
+}
+
+TEST(OpReorderer, renderPropClipping) {
+ testProperty([](RenderProperties& properties) {
+ properties.setClipToBounds(true);
+ properties.setClipBounds(Rect(10, 20, 300, 400));
+ return RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
+ << "Clip rect should be intersection of node bounds and clip bounds";
+ });
+}
+
+TEST(OpReorderer, renderPropRevealClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableRevealClip().set(true, 50, 50, 25);
+ return RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ ASSERT_NE(nullptr, state.roundRectClipState);
+ EXPECT_TRUE(state.roundRectClipState->highPriority);
+ EXPECT_EQ(25, state.roundRectClipState->radius);
+ EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
+ });
+}
+
+TEST(OpReorderer, renderPropOutlineClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableOutline().setShouldClip(true);
+ properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
+ return RenderNode::GENERIC;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ ASSERT_NE(nullptr, state.roundRectClipState);
+ EXPECT_FALSE(state.roundRectClipState->highPriority);
+ EXPECT_EQ(5, state.roundRectClipState->radius);
+ EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
+ });
+}
+
+TEST(OpReorderer, renderPropTransform) {
+ testProperty([](RenderProperties& properties) {
+ properties.setLeftTopRightBottom(10, 10, 110, 110);
+
+ SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
+ properties.setStaticMatrix(&staticMatrix);
+
+ // ignored, since static overrides animation
+ SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
+ properties.setAnimationMatrix(&animationMatrix);
+
+ properties.setTranslationX(10);
+ properties.setTranslationY(20);
+ properties.setScaleX(0.5f);
+ properties.setScaleY(0.7f);
+ return RenderNode::GENERIC
+ | RenderNode::TRANSLATION_X | RenderNode::TRANSLATION_Y
+ | RenderNode::SCALE_X | RenderNode::SCALE_Y;
+ }, [](const RectOp& op, const BakedOpState& state) {
+ Matrix4 matrix;
+ matrix.loadTranslate(10, 10, 0); // left, top
+ matrix.scale(1.2f, 1.2f, 1); // static matrix
+ // ignore animation matrix, since static overrides it
+
+ // translation xy
+ matrix.translate(10, 20);
+
+ // scale xy (from default pivot - center)
+ matrix.translate(50, 50);
+ matrix.scale(0.5f, 0.7f, 1);
+ matrix.translate(-50, -50);
+ EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
+ << "Op draw matrix must match expected combination of transformation properties";
+ });
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index dcf1f648a9b5..83b37ab733a2 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -25,7 +25,7 @@ namespace uirenderer {
static void playbackOps(const DisplayList& displayList,
std::function<void(const RecordedOp&)> opReceiver) {
- for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ for (auto& chunk : displayList.getChunks()) {
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
RecordedOp* op = displayList.getOps()[opIndex];
opReceiver(*op);
@@ -224,5 +224,26 @@ TEST(RecordingCanvas, saveLayerRotateClipped) {
EXPECT_EQ(3, count);
}
+TEST(RecordingCanvas, testReorderBarrier) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.insertReorderBarrier(true);
+ canvas.insertReorderBarrier(false);
+ canvas.insertReorderBarrier(false);
+ canvas.insertReorderBarrier(true);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.insertReorderBarrier(false);
+ });
+
+ auto chunks = dl->getChunks();
+ EXPECT_EQ(0u, chunks[0].beginOpIndex);
+ EXPECT_EQ(1u, chunks[0].endOpIndex);
+ EXPECT_FALSE(chunks[0].reorderChildren);
+
+ EXPECT_EQ(1u, chunks[1].beginOpIndex);
+ EXPECT_EQ(2u, chunks[1].endOpIndex);
+ EXPECT_TRUE(chunks[1].reorderChildren);
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index 770f413352c2..28e0fd8f3464 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -16,6 +16,8 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
+#include <DeviceInfo.h>
+#include <DisplayList.h>
#include <Matrix.h>
#include <Rect.h>
#include <RenderNode.h>
@@ -23,6 +25,12 @@
#include <renderthread/RenderThread.h>
#include <Snapshot.h>
+#if HWUI_NEW_OPS
+#include <RecordedOp.h>
+#else
+#include <DisplayListOp.h>
+#endif
+
#include <memory>
namespace android {
@@ -90,6 +98,10 @@ public:
}
static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
+ // if RenderNodes are being sync'd/used, device info will be needed, since
+ // DeviceInfo::maxTextureSize() affects layer property
+ DeviceInfo::initialize();
+
sp<RenderNode> node = new RenderNode();
node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
@@ -112,9 +124,8 @@ public:
return node;
}
- static void syncNodePropertiesAndDisplayList(sp<RenderNode>& node) {
- node->syncProperties();
- node->syncDisplayList();
+ static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
+ syncHierarchyPropertiesAndDisplayListImpl(node.get());
}
typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
@@ -142,6 +153,18 @@ public:
TestTask task(rtCallback);
renderthread::RenderThread::getInstance().queueAndWait(&task);
}
+private:
+ static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
+ node->syncProperties();
+ node->syncDisplayList();
+ auto displayList = node->getDisplayList();
+ if (displayList) {
+ for (auto&& childOp : displayList->getChildren()) {
+ syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode);
+ }
+ }
+ }
+
}; // class TestUtils
} /* namespace uirenderer */
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 57ee319f8ff3..3d78028a72f8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -603,6 +603,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (DEBUG) Log.d(TAG, "received broadcast " + action);
@@ -656,6 +657,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) {
@@ -788,6 +790,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
return new SimData(state, slotId, subId);
}
+ @Override
public String toString() {
return "SimData{state=" + simState + ",slotId=" + slotId + ",subId=" + subId + "}";
}
@@ -1693,5 +1696,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
for (int subId : mServiceStates.keySet()) {
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
+ if (mFpm != null && mFpm.isHardwareDetected()) {
+ final int userId = ActivityManager.getCurrentUser();
+ final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ pw.println(" Fingerprint state (user=" + userId + ")");
+ pw.println(" allowed=" + isUnlockingWithFingerprintAllowed());
+ pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId));
+ pw.println(" authSinceBoot="
+ + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+ pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
+ pw.println(" possible=" + isUnlockWithFingerprintPossible(userId));
+ pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
+ pw.println(" timedout=" + hasFingerprintUnlockTimedOut(userId));
+ pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ }
}
}
diff --git a/packages/SystemUI/res/drawable/docked_divider_handle.xml b/packages/SystemUI/res/drawable/docked_divider_handle.xml
new file mode 100644
index 000000000000..ea32548b78cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/docked_divider_handle.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2 (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">
+ <size android:height="@dimen/docked_divider_handle_height"
+ android:width="@dimen/docked_divider_handle_width" />
+ <corners radius="1dp" />
+ <solid android:color="@color/docked_divider_handle" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
new file mode 100644
index 000000000000..5f42856b5929
--- /dev/null
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.systemui.stackdivider.DividerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <View
+ style="@style/DockedDividerBackground"
+ android:id="@+id/docked_divider_background"
+ android:background="@color/docked_divider_background"/>
+
+ <ImageButton
+ style="@style/DockedDividerHandle"
+ android:id="@+id/docked_divider_handle"
+ android:layout_height="48dp"
+ android:layout_width="48dp"
+ android:background="@null"
+ android:src="@drawable/docked_divider_handle"/>
+
+</com.android.systemui.stackdivider.DividerView>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 6ef1adac3e02..456391d61216 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -22,4 +22,7 @@
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
+
+ <dimen name="docked_divider_handle_width">2dp</dimen>
+ <dimen name="docked_divider_handle_height">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index 89191984b9e8..dd396d921638 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -18,4 +18,15 @@
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
<item name="android:layout_width">360dp</item>
</style>
+
+ <style name="DockedDividerBackground">
+ <item name="android:layout_width">12dp</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_gravity">center_horizontal</item>
+ </style>
+
+ <style name="DockedDividerHandle">
+ <item name="android:layout_gravity">center_vertical</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f40f0d9aecdb..26a0577327e4 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -143,4 +143,7 @@
<color name="fab_ripple">#1fffffff</color><!-- 12% white -->
<color name="fab_shape">#ff009688</color><!-- Teal 500 -->
+
+ <color name="docked_divider_background">#ff000000</color>
+ <color name="docked_divider_handle">#ffffff</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c41352460c9e..73f63a968c29 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -577,4 +577,11 @@
<!-- TODO: Remove this -->
<dimen name="qs_header_neg_padding">-8dp</dimen>
+
+ <!-- How high we lift the divider when touching -->
+ <dimen name="docked_stack_divider_lift_elevation">6dp</dimen>
+
+ <dimen name="docked_divider_handle_width">24dp</dimen>
+ <dimen name="docked_divider_handle_height">2dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9e942f0b6904..4462a03e0ad3 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -304,4 +304,14 @@
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
+ <style name="DockedDividerBackground">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">12dp</item>
+ <item name="android:layout_gravity">center_vertical</item>
+ </style>
+
+ <style name="DockedDividerHandle">
+ <item name="android:layout_gravity">center_horizontal</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 9551fb7bd535..949efc588dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -27,6 +27,8 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
+import com.android.systemui.stackdivider.Divider;
+
import java.util.HashMap;
import java.util.Map;
@@ -51,6 +53,7 @@ public class SystemUIApplication extends Application {
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
com.android.systemui.keyboard.KeyboardUI.class,
+ Divider.class
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index ebfacac1b1fb..a429447e18e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -21,6 +21,7 @@ package com.android.systemui.recents;
*/
public class Constants {
+ // TODO: Move into RecentsMetrics
public static class Metrics {
// DO NOT MODIFY THE ORDER OF THESE METRICS
public static final int DismissSourceKeyboard = 0;
@@ -28,6 +29,7 @@ public class Constants {
public static final int DismissSourceHeaderButton = 2;
}
+ // TODO: Move into RecentsDebugFlags
public static class DebugFlags {
public static class App {
@@ -37,7 +39,7 @@ public class Constants {
public static final boolean EnableTaskFiltering = false;
// Enables dismiss-all
public static final boolean EnableDismissAll = false;
- // Enables fast-toggling
+ // Enables fast-toggling by just tapping on the recents button
public static final boolean EnableFastToggleRecents = false;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
@@ -57,14 +59,4 @@ public class Constants {
public static final int SystemServicesProxyMockTaskCount = 100;
}
}
-
- public static class Values {
- public static class App {
- public static int AppWidgetHostId = 1024;
- }
-
- public static class TaskStackView {
- public static final int FilterStartDelay = 25;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index ab05bb08a0ff..6874247e5fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -32,19 +32,20 @@ import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -57,7 +58,6 @@ import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
-import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -104,9 +104,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Runnables to finish the Recents activity
FinishRecentsRunnable mFinishLaunchHomeRunnable;
- // Runnable to be executed after we paused ourselves
- Runnable mAfterPauseRunnable;
-
// The trigger to automatically launch the current task
DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
@Override
@@ -140,8 +137,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
try {
startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
- Console.logError(RecentsActivity.this,
- getString(R.string.recents_launch_error_message, "Home"));
+ Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
}
}
}
@@ -348,7 +344,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Initialize the widget host (the host id is static and does not change)
if (!Constants.DebugFlags.App.DisableSearchBar) {
- mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
+ mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID);
}
mPackageMonitor = new RecentsPackageMonitor();
mPackageMonitor.register(this);
@@ -426,9 +422,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
protected void onPause() {
super.onPause();
- if (mAfterPauseRunnable != null) {
- mRecentsView.post(mAfterPauseRunnable);
- mAfterPauseRunnable = null;
+
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ // Stop the fast-toggle dozer
+ mIterateTrigger.stopDozing();
}
if (Constants.DebugFlags.App.EnableFastToggleRecents) {
@@ -513,12 +510,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
// As we iterate to the next/previous task, cancel any current/lagging window
// transition animations
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (launchState.launchedToTaskId != -1) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.cancelWindowTransition(launchState.launchedToTaskId);
- }
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
@@ -577,10 +569,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
- public void onTaskViewClicked() {
- }
-
- @Override
public void onTaskLaunchFailed() {
// Return to Home
dismissRecentsToHome(true);
@@ -591,11 +579,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mFinishLaunchHomeRunnable.run();
}
- @Override
- public void runAfterPause(Runnable r) {
- mAfterPauseRunnable = r;
- }
-
/**** EventBus events ****/
public final void onBusEvent(ToggleRecentsEvent event) {
@@ -664,6 +647,17 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
}
+ public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ int launchToTaskId = launchState.launchedToTaskId;
+ if (launchToTaskId != -1 &&
+ (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.cancelWindowTransition(launchState.launchedToTaskId);
+ ssp.cancelThumbnailTransition(getTaskId());
+ }
+ }
+
public final void onBusEvent(AppWidgetProviderChangedEvent event) {
refreshSearchWidgetView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 9ee6f5b4d8e2..01ffd2a5afdc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -19,7 +19,6 @@ package com.android.systemui.recents;
/**
* The launch state of the RecentsActivity.
*
- * TODO: We will be refactoring this out RecentsConfiguration.
* Current Constraints:
* - needed in onStart() before onNewIntent()
* - needs to be reset when Recents is hidden
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index fc96c11e4124..573db98a889d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -26,6 +26,8 @@ import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEven
/** Our special app widget host for the Search widget */
public class RecentsAppWidgetHost extends AppWidgetHost {
+ public static final int HOST_ID = 1024;
+
boolean mIsListening;
public RecentsAppWidgetHost(Context context, int hostId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 675ef2fe55ab..db65e003f32f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.recents;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
@@ -32,24 +30,24 @@ import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.Log;
import android.util.MutableBoolean;
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.View;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
@@ -57,14 +55,16 @@ import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+
/**
* An implementation of the Recents component for the current user. For secondary users, this can
* be called remotely from the system user.
@@ -75,7 +75,12 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
private final static String TAG = "RecentsImpl";
private final static boolean DEBUG = false;
- private final static int sMinToggleDelay = 350;
+ // The minimum amount of time between each recents button press that we will handle
+ private final static int MIN_TOGGLE_DELAY_MS = 350;
+ // The duration within which the user releasing the alt tab (from when they pressed alt tab)
+ // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this
+ // duration, then we will toggle recents after this duration.
+ private final static int FAST_ALT_TAB_DELAY_MS = 225;
public final static String RECENTS_PACKAGE = "com.android.systemui";
public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
@@ -156,6 +161,14 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
// Variables to keep track of if we need to start recents after binding
boolean mTriggeredFromAltTab;
long mLastToggleTime;
+ DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
+ @Override
+ public void run() {
+ // When this fires, then the user has not released alt-tab for at least
+ // FAST_ALT_TAB_DELAY_MS milliseconds
+ showRecents(mTriggeredFromAltTab);
+ }
+ });
Bitmap mThumbnailTransitionBitmapCache;
Task mThumbnailTransitionBitmapCacheKey;
@@ -164,7 +177,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
public RecentsImpl(Context context) {
mContext = context;
mHandler = new Handler();
- mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
+ mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID);
Resources res = mContext.getResources();
LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -239,6 +252,29 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
@Override
public void showRecents(boolean triggeredFromAltTab) {
mTriggeredFromAltTab = triggeredFromAltTab;
+ if (mFastAltTabTrigger.hasTriggered()) {
+ // We are calling this from the doze trigger, so just fall through to show Recents
+ mFastAltTabTrigger.resetTrigger();
+ } else if (mFastAltTabTrigger.isDozing()) {
+ // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab,
+ // otherwise, this is an additional tab (alt-tab*), which means that we should trigger
+ // immediately (fall through and disable the pending trigger)
+ // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started
+ // so we may actually additional signal to handle multiple quick tab cases. The
+ // severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS
+ // duration though
+ if (!triggeredFromAltTab) {
+ return;
+ }
+ mFastAltTabTrigger.stopDozing();
+ } else {
+ // Otherwise, the doze trigger is not running, and if this is an alt tab, we should
+ // start the trigger and then wait for the hide (or for it to elapse)
+ if (triggeredFromAltTab) {
+ mFastAltTabTrigger.startDozing();
+ return;
+ }
+ }
try {
// Check if the top task is in the home stack, and start the recents activity
@@ -249,13 +285,24 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
startRecentsActivity(topTask, isTopTaskHome.value);
}
} catch (ActivityNotFoundException e) {
- Console.logRawError("Failed to launch RecentAppsIntent", e);
+ Log.e(TAG, "Failed to launch RecentsActivity", e);
}
}
@Override
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
if (mBootCompleted) {
+ if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
+ // The user has released alt-tab before the trigger has run, so just show the next
+ // task immediately
+ showNextTask();
+
+ // Cancel the fast alt-tab trigger
+ mFastAltTabTrigger.stopDozing();
+ mFastAltTabTrigger.resetTrigger();
+ return;
+ }
+
// Defer to the activity to handle hiding recents, if it handles it, then it must still
// be visible
EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
@@ -265,6 +312,11 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
@Override
public void toggleRecents() {
+ // Skip this toggle if we are already waiting to trigger recents via alt-tab
+ if (mFastAltTabTrigger.isDozing()) {
+ return;
+ }
+
mTriggeredFromAltTab = false;
try {
@@ -283,7 +335,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
// better than showing a janky screenshot).
// NOTE: Ideally, the screenshot mechanism would take the window transform into
// account
- if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
return;
}
@@ -296,7 +348,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
// better than showing a janky screenshot).
// NOTE: Ideally, the screenshot mechanism would take the window transform into
// account
- if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
return;
}
@@ -305,7 +357,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
mLastToggleTime = SystemClock.elapsedRealtime();
}
} catch (ActivityNotFoundException e) {
- Console.logRawError("Failed to launch RecentAppsIntent", e);
+ Log.e(TAG, "Failed to launch RecentsActivity", e);
}
}
@@ -335,10 +387,58 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
// Do nothing
}
+ /**
+ * Transitions to the next recent task in the stack.
+ */
+ public void showNextTask() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ TaskStack focusedStack = plan.getTaskStack();
+
+ // Return early if there are no tasks in the focused stack
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+ ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
+ // Return early if there is no running task
+ if (runningTask == null) return;
+ // Return early if the running task is in the home stack (optimization)
+ if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
+
+ // Find the task in the recents list
+ ArrayList<Task> tasks = focusedStack.getTasks();
+ Task toTask = null;
+ ActivityOptions launchOpts = null;
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 1; i--) {
+ Task task = tasks.get(i);
+ if (task.key.id == runningTask.id) {
+ toTask = tasks.get(i - 1);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_target,
+ R.anim.recents_launch_prev_affiliated_task_source);
+ break;
+ }
+ }
+
+ // Return early if there is no next task
+ if (toTask == null) {
+ ssp.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_bounce));
+ return;
+ }
+
+ // Launch the task
+ ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+ }
+
+ /**
+ * Transitions to the next affiliated task.
+ */
public void showRelativeAffiliatedTask(boolean showNextTask) {
- // Return early if there is no focused stack
SystemServicesProxy ssp = Recents.getSystemServices();
- int focusedStackId = ssp.getFocusedStack();
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, true /* isTopTaskHome */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
new file mode 100644
index 000000000000..7604de1d05d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+
+/**
+ * This is sent when we want to cancel the enter-recents window animation for the launch task.
+ */
+public class CancelEnterRecentsWindowAnimationEvent extends EventBus.Event {
+
+ // This is set for the task that is launching, which allows us to ensure that we are not
+ // cancelling the same task animation (it will just be overwritten instead)
+ public final Task launchTask;
+
+ public CancelEnterRecentsWindowAnimationEvent(Task launchTask) {
+ this.launchTask = launchTask;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
deleted file mode 100644
index 28ac9d38967f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.misc;
-
-
-import android.content.ComponentCallbacks2;
-import android.content.Context;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class Console {
- // Timer
- public static final Map<Object, Long> mTimeLogs = new HashMap<Object, Long>();
-
- // Colors
- public static final String AnsiReset = "\u001B[0m";
- public static final String AnsiBlack = "\u001B[30m";
- public static final String AnsiRed = "\u001B[31m"; // SystemUIHandshake
- public static final String AnsiGreen = "\u001B[32m"; // MeasureAndLayout
- public static final String AnsiYellow = "\u001B[33m"; // SynchronizeViewsWithModel
- public static final String AnsiBlue = "\u001B[34m"; // TouchEvents, Search
- public static final String AnsiPurple = "\u001B[35m"; // Draw
- public static final String AnsiCyan = "\u001B[36m"; // ClickEvents
- public static final String AnsiWhite = "\u001B[37m";
-
- // Console enabled state
- public static boolean Enabled = false;
-
- /** Logs a key */
- public static void log(String key) {
- log(true, key, "", AnsiReset);
- }
-
- /** Logs a conditioned key */
- public static void log(boolean condition, String key) {
- if (condition) {
- log(condition, key, "", AnsiReset);
- }
- }
-
- /** Logs a key in a specific color */
- public static void log(boolean condition, String key, Object data) {
- if (condition) {
- log(condition, key, data, AnsiReset);
- }
- }
-
- /** Logs a key with data in a specific color */
- public static void log(boolean condition, String key, Object data, String color) {
- if (condition) {
- System.out.println(color + key + AnsiReset + " " + data.toString());
- }
- }
-
- /** Logs an error */
- public static void logError(Context context, String msg) {
- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
- Log.e("Recents", msg);
- }
-
- /** Logs a raw error */
- public static void logRawError(String msg, Exception e) {
- Log.e("Recents", msg, e);
- }
-
- /** Logs a divider bar */
- public static void logDivider(boolean condition) {
- if (condition) {
- System.out.println("==== [" + System.currentTimeMillis() +
- "] ============================================================");
- }
- }
-
- /** Starts a time trace */
- public static void logStartTracingTime(boolean condition, String key) {
- if (condition) {
- long curTime = System.currentTimeMillis();
- mTimeLogs.put(key, curTime);
- Console.log(condition, "[Recents|" + key + "]",
- "started @ " + curTime);
- }
- }
-
- /** Continues a time trace */
- public static void logTraceTime(boolean condition, String key, String desc) {
- if (condition) {
- long timeDiff = System.currentTimeMillis() - mTimeLogs.get(key);
- Console.log(condition, "[Recents|" + key + "|" + desc + "]",
- "+" + timeDiff + "ms");
- }
- }
-
- /** Logs a stack trace */
- public static void logStackTrace() {
- logStackTrace("", 99);
- }
-
- /** Logs a stack trace to a certain depth */
- public static void logStackTrace(int depth) {
- logStackTrace("", depth);
- }
-
- /** Logs a stack trace to a certain depth with a key */
- public static void logStackTrace(String key, int depth) {
- int offset = 0;
- StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- String tinyStackTrace = "";
- // Skip over the known stack trace classes
- for (int i = 0; i < callStack.length; i++) {
- StackTraceElement el = callStack[i];
- String className = el.getClassName();
- if (className.indexOf("dalvik.system.VMStack") == -1 &&
- className.indexOf("java.lang.Thread") == -1 &&
- className.indexOf("recents.Console") == -1) {
- break;
- } else {
- offset++;
- }
- }
- // Build the pretty stack trace
- int start = Math.min(offset + depth, callStack.length);
- int end = offset;
- String indent = "";
- for (int i = start - 1; i >= end; i--) {
- StackTraceElement el = callStack[i];
- tinyStackTrace += indent + " -> " + el.getClassName() +
- "[" + el.getLineNumber() + "]." + el.getMethodName();
- if (i > end) {
- tinyStackTrace += "\n";
- indent += " ";
- }
- }
- log(true, key, tinyStackTrace, AnsiRed);
- }
-
-
- /** Returns the stringified MotionEvent action */
- public static String motionEventActionToString(int action) {
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- return "Down";
- case MotionEvent.ACTION_UP:
- return "Up";
- case MotionEvent.ACTION_MOVE:
- return "Move";
- case MotionEvent.ACTION_CANCEL:
- return "Cancel";
- case MotionEvent.ACTION_POINTER_DOWN:
- return "Pointer Down";
- case MotionEvent.ACTION_POINTER_UP:
- return "Pointer Up";
- default:
- return "" + action;
- }
- }
-
- public static String trimMemoryLevelToString(int level) {
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
- return "UI Hidden";
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
- return "Running Moderate";
- case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
- return "Background";
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
- return "Running Low";
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- return "Moderate";
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
- return "Critical";
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- return "Complete";
- default:
- return "" + level;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 336d2db443f9..dc8f5476f254 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -29,44 +29,53 @@ public class DozeTrigger {
boolean mIsDozing;
boolean mHasTriggered;
int mDozeDurationMilliseconds;
- Runnable mSleepRunnable;
+ Runnable mOnSleepRunnable;
// Sleep-runnable
Runnable mDozeRunnable = new Runnable() {
@Override
public void run() {
- mSleepRunnable.run();
mIsDozing = false;
mHasTriggered = true;
+ mOnSleepRunnable.run();
}
};
- public DozeTrigger(int dozeDurationMilliseconds, Runnable sleepRunnable) {
+ public DozeTrigger(int dozeDurationMilliseconds, Runnable onSleepRunnable) {
mHandler = new Handler();
mDozeDurationMilliseconds = dozeDurationMilliseconds;
- mSleepRunnable = sleepRunnable;
+ mOnSleepRunnable = onSleepRunnable;
}
- /** Starts dozing. This also resets the trigger flag. */
+ /**
+ * Starts dozing and queues the onSleepRunnable to be called. This also resets the trigger flag.
+ */
public void startDozing() {
forcePoke();
mHasTriggered = false;
}
- /** Stops dozing. */
+ /**
+ * Stops dozing and prevents the onSleepRunnable from being called.
+ */
public void stopDozing() {
mHandler.removeCallbacks(mDozeRunnable);
mIsDozing = false;
}
- /** Poke this dozer to wake it up for a little bit, if it is dozing. */
+ /**
+ * Poke this dozer to wake it up if it is dozing, delaying the onSleepRunnable from being
+ * called for a for the doze duration.
+ */
public void poke() {
if (mIsDozing) {
forcePoke();
}
}
- /** Poke this dozer to wake it up for a little bit. */
+ /**
+ * Poke this dozer to wake it up even if it is not currently dozing.
+ */
void forcePoke() {
mHandler.removeCallbacks(mDozeRunnable);
mHandler.postDelayed(mDozeRunnable, mDozeDurationMilliseconds);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index c87a901341d1..401b4487d621 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -19,6 +19,7 @@ package com.android.systemui.recents.misc;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.util.Log;
import java.util.ArrayList;
@@ -28,6 +29,8 @@ import java.util.ArrayList;
*/
public class ReferenceCountedTrigger {
+ private static final String TAG = "ReferenceCountedTrigger";
+
Context mContext;
int mCount;
ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
@@ -94,8 +97,7 @@ public class ReferenceCountedTrigger {
if (mErrorRunnable != null) {
mErrorRunnable.run();
} else {
- new Throwable("Invalid ref count").printStackTrace();
- Console.logError(mContext, "Invalid ref count");
+ Log.e(TAG, "Invalid ref count");
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 4ee0357123aa..0432ac99de2f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -363,6 +363,19 @@ public class SystemServicesProxy {
}
}
+ /**
+ * Cancels the current thumbnail transtion to/from Recents for the given task id.
+ */
+ public void cancelThumbnailTransition(int taskId) {
+ if (mWm == null) return;
+
+ try {
+ WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the top task thumbnail for the given task id */
public Bitmap getTaskThumbnail(int taskId) {
if (mAm == null) return null;
@@ -775,8 +788,7 @@ public class SystemServicesProxy {
taskId, INVALID_STACK_ID, options == null ? null : options.toBundle());
return true;
} catch (Exception e) {
- Console.logError(context,
- context.getString(R.string.recents_launch_error_message, taskName));
+ Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
}
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 495c8fd2bc5d..6734012c4e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -17,14 +17,12 @@
package com.android.systemui.recents.model;
import android.animation.ObjectAnimator;
-import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
-import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1f80460714ba..5f94fa76f380 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -30,6 +31,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -38,6 +40,8 @@ import android.view.WindowManagerGlobal;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
@@ -47,6 +51,7 @@ import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
@@ -60,8 +65,8 @@ import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
/**
@@ -78,10 +83,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
- public void onTaskViewClicked();
public void onTaskLaunchFailed();
public void onAllTaskViewsDismissed();
- public void runAfterPause(Runnable r);
}
LayoutInflater mInflater;
@@ -104,6 +107,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
Rect mSystemInsets = new Rect();
+
+ @GuardedBy("this")
+ List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs;
+
public RecentsView(Context context) {
super(context);
}
@@ -240,6 +247,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
ctx.postAnimationTrigger.decrement();
+ // If we are going home, cancel the previous task's window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+
// Notify of the exit animation
EventBus.getDefault().send(new DismissRecentsToHomeAnimationStarted());
}
@@ -412,56 +422,37 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
- private void postDrawHeaderThumbnailTransitionRunnable(final TaskStackView view,
- final TaskView clickedView, final int offsetX, final int offsetY,
- final float stackScroll,
- final ActivityOptions.OnAnimationStartedListener animStartedListener,
- final int destinationStack) {
- Runnable r = new Runnable() {
- @Override
- public void run() {
- overrideDrawHeaderThumbnailTransition(view, clickedView, offsetX, offsetY,
- stackScroll, animStartedListener, destinationStack);
-
- }
- };
-
- mCb.runAfterPause(r);
- }
-
- private void overrideDrawHeaderThumbnailTransition(TaskStackView stackView,
- TaskView clickedTask, int offsetX, int offsetY, float stackScroll,
- final ActivityOptions.OnAnimationStartedListener animStartedListener,
- int destinationStack) {
- List<AppTransitionAnimationSpec> specs = getAppTransitionAnimationSpecs(stackView,
- clickedTask, offsetX, offsetY, stackScroll, destinationStack);
- if (specs == null) {
- return;
- }
-
- IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
+ private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final TaskStackView stackView,
+ final TaskView clickedTask, final int offsetX, final int offsetY,
+ final float stackScroll, final int destinationStack) {
+ return new IAppTransitionAnimationSpecsFuture.Stub() {
@Override
- public void sendResult(Bundle data) throws RemoteException {
+ public AppTransitionAnimationSpec[] get() throws RemoteException {
post(new Runnable() {
@Override
public void run() {
- if (animStartedListener != null) {
- animStartedListener.onAnimationStarted();
+ synchronized (RecentsView.this) {
+ mAppTransitionAnimationSpecs = getAppTransitionAnimationSpecs(stackView,
+ clickedTask, offsetX, offsetY, stackScroll, destinationStack);
+ RecentsView.this.notifyAll();
}
}
});
+ synchronized (RecentsView.this) {
+ while (mAppTransitionAnimationSpecs == null) {
+ try {
+ RecentsView.this.wait();
+ } catch (InterruptedException e) {}
+ }
+ if (mAppTransitionAnimationSpecs == null) {
+ return null;
+ }
+ AppTransitionAnimationSpec[] specs
+ = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()];
+ return mAppTransitionAnimationSpecs.toArray(specs);
+ }
}
};
-
- AppTransitionAnimationSpec[] specsArray =
- new AppTransitionAnimationSpec[specs.size()];
- try {
- WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionMultiThumb(
- specs.toArray(specsArray), callback, null, true /* scaleUp */);
-
- } catch (RemoteException e) {
- Log.w(TAG, "Error overriding app transition", e);
- }
}
private List<AppTransitionAnimationSpec> getAppTransitionAnimationSpecs(TaskStackView stackView,
@@ -568,30 +559,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
return new AppTransitionAnimationSpec(taskId, b, rect);
}
- /**
- * Cancels any running window transitions for the launched task (the task animating into
- * Recents).
- */
- private void cancelLaunchedTaskWindowTransition(final Task task) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (launchState.launchedToTaskId != -1 &&
- launchState.launchedToTaskId != task.key.id) {
- ssp.cancelWindowTransition(launchState.launchedToTaskId);
- }
- }
-
/**** TaskStackView.TaskStackCallbacks Implementation ****/
@Override
public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
final TaskStack stack, final Task task, final boolean lockToTask,
final boolean boundsValid, final Rect bounds, int destinationStack) {
- // Notify any callbacks of the launching of a new task
- if (mCb != null) {
- mCb.onTaskViewClicked();
- }
// Upfront the processing of the thumbnail
TaskViewTransform transform = new TaskViewTransform();
@@ -613,8 +586,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// Compute the thumbnail to scale up from
final SystemServicesProxy ssp = Recents.getSystemServices();
boolean screenPinningRequested = false;
- ActivityOptions opts = null;
+ ActivityOptions opts = ActivityOptions.makeBasic();
ActivityOptions.OnAnimationStartedListener animStartedListener = null;
+ final IAppTransitionAnimationSpecsFuture transitionFuture;
if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
task.thumbnail.getHeight() > 0) {
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@@ -622,7 +596,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void onAnimationStarted() {
// If we are launching into another task, cancel the previous task's
// window transition
- cancelLaunchedTaskWindowTransition(task);
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
if (lockToTask) {
// Request screen pinning after the animation runs
@@ -636,21 +610,18 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
};
- postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
- animStartedListener, destinationStack);
- opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
- offsetX, offsetY, (int) transform.rect.width(), (int) transform.rect.height(),
- sourceView.getHandler(), animStartedListener);
+ transitionFuture = getAppTransitionFuture(stackView, tv, offsetX, offsetY, stackScroll,
+ destinationStack);
screenPinningRequested = true;
} else {
- opts = ActivityOptions.makeBasic();
+ transitionFuture = null;
}
if (boundsValid) {
opts.setBounds(bounds.isEmpty() ? null : bounds);
}
final ActivityOptions launchOpts = opts;
final boolean finalScreenPinningRequested = screenPinningRequested;
+ final OnAnimationStartedListener finalAnimStartedListener = animStartedListener;
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
@@ -678,6 +649,28 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
MetricsLogger.count(getContext(), "overview_task_launch_failed", 1);
}
}
+ if (transitionFuture != null) {
+ IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (finalAnimStartedListener != null) {
+ finalAnimStartedListener.onAnimationStarted();
+ }
+ }
+ });
+ }
+ };
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
+ callback, true /* scaleUp */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 7f5c768d870f..51091c38eec0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -31,7 +31,6 @@ import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.LinkedList;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fc3a6819c953..3ef597f7c849 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -34,7 +34,6 @@ import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
@@ -228,6 +227,42 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return mImmutableTaskViews;
}
+ /**
+ * Returns the front most task view.
+ *
+ * @param stackTasksOnly if set, will return the front most task view in the stack (by default
+ * the front most task view will be freeform since they are placed above
+ * stack tasks)
+ */
+ private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = taskViewCount - 1; i >= 0; i--) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+ if (stackTasksOnly && task.isFreeformTask()) {
+ continue;
+ }
+ return tv;
+ }
+ return null;
+ }
+
+ /**
+ * Finds the child view given a specific {@param task}.
+ */
+ public TaskView getChildViewForTask(Task t) {
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ if (tv.getTask() == t) {
+ return tv;
+ }
+ }
+ return null;
+ }
+
/** Resets this TaskStackView for reuse. */
void reset() {
// Reset the focused task
@@ -288,19 +323,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
- /** Finds the child view given a specific task. */
- public TaskView getChildViewForTask(Task t) {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- if (tv.getTask() == t) {
- return tv;
- }
- }
- return null;
- }
-
/** Returns the stack algorithm for this task stack. */
public TaskStackLayoutAlgorithm getStackAlgorithm() {
return mLayoutAlgorithm;
@@ -635,13 +657,49 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**
* Sets the focused task relative to the currently focused task.
*
+ * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+ * if the currently focused task is not a stack task, will set the focus
+ * to the first visible stack task
* @param animated determines whether to actually draw the highlight along with the change in
* focus.
*/
- public void setRelativeFocusedTask(boolean forward, boolean animated) {
- // Find the next index to focus
- int newIndex = mFocusedTaskIndex + (forward ? -1 : 1);
- setFocusedTask(newIndex, true, animated);
+ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
+ int newIndex = -1;
+ if (mFocusedTaskIndex != -1) {
+ if (stackTasksOnly) {
+ List<Task> tasks = mStack.getTasks();
+ newIndex = mFocusedTaskIndex;
+ Task task = tasks.get(mFocusedTaskIndex);
+ if (task.isFreeformTask()) {
+ // Try and focus the front most stack task
+ TaskView tv = getFrontMostTaskView(stackTasksOnly);
+ if (tv != null) {
+ newIndex = mStack.indexOfTask(tv.getTask());
+ }
+ } else {
+ // Try the next task if it is a stack task
+ int tmpNewIndex = mFocusedTaskIndex + (forward ? -1 : 1);
+ if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
+ Task t = tasks.get(tmpNewIndex);
+ if (!t.isFreeformTask()) {
+ newIndex = tmpNewIndex;
+ }
+ }
+ }
+ } else {
+ // No restrictions, lets just move to the new task
+ newIndex = mFocusedTaskIndex + (forward ? -1 : 1);
+ }
+ } else {
+ // We don't have a focused task, so focus the first visible task view
+ TaskView tv = getFrontMostTaskView(stackTasksOnly);
+ if (tv != null) {
+ newIndex = mStack.indexOfTask(tv.getTask());
+ }
+ }
+ if (newIndex != -1) {
+ setFocusedTask(newIndex, true, animated);
+ }
}
/**
@@ -703,11 +761,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
switch (action) {
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
- setRelativeFocusedTask(true, false /* animated */);
+ setRelativeFocusedTask(true, false /* stackTasksOnly */, false /* animated */);
return true;
}
case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
- setRelativeFocusedTask(false, false /* animated */);
+ setRelativeFocusedTask(false, false /* stackTasksOnly */, false /* animated */);
return true;
}
}
@@ -1324,11 +1382,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
public final void onBusEvent(FocusNextTaskViewEvent event) {
- setRelativeFocusedTask(true, true);
+ setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */);
}
public final void onBusEvent(FocusPreviousTaskViewEvent event) {
- setRelativeFocusedTask(false, true);
+ setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
}
public final void onBusEvent(DismissFocusedTaskViewEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index a32b2422aa24..45f573d46787 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents.views;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
@@ -113,7 +112,7 @@ public class TaskStackViewFilterAlgorithm {
tv.prepareTaskTransformForFilterTaskHidden(fromTransform);
tv.updateViewPropertiesToTaskTransform(fromTransform, 0);
- toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+ toTransform.startDelay = offset * 25;
childViewTransformsOut.put(tv, toTransform);
// Use the movement of the new views to calculate the duration of the animation
@@ -166,7 +165,7 @@ public class TaskStackViewFilterAlgorithm {
(int) tv.getTranslationY()));
}
- toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+ toTransform.startDelay = offset * 25;
childViewTransformsOut.put(tv, toTransform);
offset++;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 59c9708840e2..81c89a1a0dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -31,6 +31,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
@@ -149,6 +150,11 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
if (mSv.getTaskViews().size() == 0) {
return false;
}
+ // Short circuit while we are alt-tabbing
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ if (launchState.launchedWithAltTab) {
+ return false;
+ }
final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
int action = ev.getAction();
@@ -305,9 +311,11 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Find the front most task and scroll the next task to the front
float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL);
if (vScroll > 0) {
- mSv.setRelativeFocusedTask(true, false /* animated */);
+ mSv.setRelativeFocusedTask(true, true /* stackTasksOnly */,
+ false /* animated */);
} else {
- mSv.setRelativeFocusedTask(false, false /* animated */);
+ mSv.setRelativeFocusedTask(false, true /* stackTasksOnly */,
+ false /* animated */);
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index dd1474d9633e..4f4b91ad5691 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -29,6 +29,7 @@ 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.Log;
import android.view.MotionEvent;
@@ -80,7 +81,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
boolean mTaskDataLoaded;
boolean mIsFocused;
boolean mIsFocusAnimated;
- boolean mFocusAnimationsEnabled;
boolean mClipViewInStack;
AnimateableViewBounds mViewBounds;
@@ -642,6 +642,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
setDim(getDimFromTaskProgress());
}
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (Constants.DebugFlags.App.EnableFastToggleRecents && mIsFocused) {
+ Paint tmpPaint = new Paint();
+ Rect tmpRect = new Rect();
+ tmpRect.set(0, 0, getWidth(), getHeight());
+ tmpPaint.setColor(0xFFFF0000);
+ tmpPaint.setStrokeWidth(35);
+ tmpPaint.setStyle(Paint.Style.STROKE);
+ canvas.drawRect(tmpRect, tmpPaint);
+ }
+ }
+
/**** View focus state ****/
/**
@@ -672,6 +687,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
clearAccessibilityFocus();
}
}
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ invalidate();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
new file mode 100644
index 000000000000..dd894ce418a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.content.res.Configuration;
+import android.view.LayoutInflater;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+/**
+ * Controls the docked stack divider.
+ */
+public class Divider extends SystemUI {
+ private static final String TAG = "Divider";
+ private int mDividerWindowWidth;
+ private DividerWindowManager mWindowManager;
+
+ @Override
+ public void start() {
+ mWindowManager = new DividerWindowManager(mContext);
+ mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ update(mContext.getResources().getConfiguration());
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ update(newConfig);
+ }
+
+ private void addDivider(Configuration configuration) {
+ DividerView view = (DividerView)
+ LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
+ final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
+ final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
+ final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
+ mWindowManager.add(view, width, height);
+ view.setWindowManager(mWindowManager);
+ }
+
+ private void removeDivider() {
+ mWindowManager.remove();
+ }
+
+ private void update(Configuration configuration) {
+ removeDivider();
+ addDivider(configuration);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
new file mode 100644
index 000000000000..5f983c5435e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.DisplayInfo;
+
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+/**
+ * Calculates the snap targets and the snap position given a position and a velocity. All positions
+ * here are to be interpreted as the left/top edge of the divider rectangle.
+ */
+public class DividerSnapAlgorithm {
+
+ private final Context mContext;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final int mDividerSize;
+ private final ArrayList<SnapTarget> mTargets;
+
+ /** The first target which is still splitting the screen */
+ private final SnapTarget mFirstSplitTarget;
+
+ /** The last target which is still splitting the screen */
+ private final SnapTarget mLastSplitTarget;
+
+ private final SnapTarget mDismissStartTarget;
+ private final SnapTarget mDismissEndTarget;
+
+ public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils,
+ int dividerSize, boolean isHorizontalDivision) {
+ mContext = ctx;
+ mFlingAnimationUtils = flingAnimationUtils;
+ mDividerSize = dividerSize;
+ mTargets = calculateTargets(isHorizontalDivision);
+ mFirstSplitTarget = mTargets.get(1);
+ mLastSplitTarget = mTargets.get(mTargets.size() - 2);
+ mDismissStartTarget = mTargets.get(0);
+ mDismissEndTarget = mTargets.get(mTargets.size() - 1);
+ }
+
+ public SnapTarget calculateSnapTarget(int position, float velocity) {
+ if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return snap(position);
+ }
+ if (position < mFirstSplitTarget.position && velocity < 0) {
+ return mDismissStartTarget;
+ }
+ if (position > mLastSplitTarget.position && velocity > 0) {
+ return mDismissEndTarget;
+ }
+ if (velocity < 0) {
+ return mFirstSplitTarget;
+ } else {
+ return mLastSplitTarget;
+ }
+ }
+
+ private SnapTarget snap(int position) {
+ int minIndex = -1;
+ int minDistance = Integer.MAX_VALUE;
+ int size = mTargets.size();
+ for (int i = 0; i < size; i++) {
+ int distance = Math.abs(position - mTargets.get(i).position);
+ if (distance < minDistance) {
+ minIndex = i;
+ minDistance = distance;
+ }
+ }
+ return mTargets.get(minIndex);
+ }
+
+ private ArrayList<SnapTarget> calculateTargets(boolean isHorizontalDivision) {
+ ArrayList<SnapTarget> targets = new ArrayList<>();
+ DisplayMetrics info = mContext.getResources().getDisplayMetrics();
+ int dividerMax = isHorizontalDivision
+ ? info.heightPixels
+ : info.widthPixels;
+
+ // TODO: Better calculation
+ targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START));
+ targets.add(new SnapTarget((int) (0.35f * dividerMax) - mDividerSize / 2,
+ SnapTarget.FLAG_NONE));
+ targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE));
+ targets.add(new SnapTarget((int) (0.65f * dividerMax) - mDividerSize / 2,
+ SnapTarget.FLAG_NONE));
+ targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END));
+ return targets;
+ }
+
+ /**
+ * Represents a snap target for the divider.
+ */
+ public static class SnapTarget {
+ public static final int FLAG_NONE = 0;
+
+ /** If the divider reaches this value, the left/top task should be dismissed. */
+ public static final int FLAG_DISMISS_START = 1;
+
+ /** If the divider reaches this value, the right/bottom task should be dismissed */
+ public static final int FLAG_DISMISS_END = 2;
+
+ public final int position;
+ public final int flag;
+
+ public SnapTarget(int position, int flag) {
+ this.position = position;
+ this.flag = flag;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
new file mode 100644
index 000000000000..59d4011446f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+import com.android.systemui.R;
+import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
+
+/**
+ * Docked stack divider.
+ */
+public class DividerView extends FrameLayout implements OnTouchListener,
+ OnComputeInternalInsetsListener {
+
+ private static final String TAG = "DividerView";
+
+ private ImageButton mHandle;
+ private View mBackground;
+ private int mStartX;
+ private int mStartY;
+ private int mStartPosition;
+ private int mDockSide;
+ private final int[] mTempInt2 = new int[2];
+
+ private int mDividerInsets;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
+ private int mDividerWindowWidth;
+ private int mDividerSize;
+ private int mTouchElevation;
+
+ private final Rect mTmpRect = new Rect();
+ private final Rect mLastResizeRect = new Rect();
+ private final WindowManagerProxy mWindowManagerProxy = new WindowManagerProxy();
+ private Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mTouchResponseInterpolator =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+ private DividerWindowManager mWindowManager;
+ private VelocityTracker mVelocityTracker;
+ private FlingAnimationUtils mFlingAnimationUtils;
+
+ public DividerView(Context context) {
+ super(context);
+ }
+
+ public DividerView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mHandle = (ImageButton) findViewById(R.id.docked_divider_handle);
+ mBackground = findViewById(R.id.docked_divider_background);
+ mHandle.setOnTouchListener(this);
+ mDividerWindowWidth = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ mDividerInsets = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ mDividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ mTouchElevation = getResources().getDimensionPixelSize(
+ R.dimen.docked_stack_divider_lift_elevation);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+ android.R.interpolator.fast_out_slow_in);
+ mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f);
+ updateDisplayInfo();
+ boolean landscape = getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ mHandle.setPointerShape(
+ landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ public void setWindowManager(DividerWindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ convertToScreenCoordinates(event);
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ mStartX = (int) event.getX();
+ mStartY = (int) event.getY();
+ getLocationOnScreen(mTempInt2);
+ mDockSide = mWindowManagerProxy.getDockSide();
+ if (isHorizontalDivision()) {
+ mStartPosition = mTempInt2[1] + mDividerInsets;
+ } else {
+ mStartPosition = mTempInt2[0] + mDividerInsets;
+ }
+ if (mDockSide != WindowManager.DOCKED_INVALID) {
+ mWindowManagerProxy.setResizing(true);
+ mWindowManager.setSlippery(false);
+ liftBackground();
+ return true;
+ } else {
+ return false;
+ }
+ case MotionEvent.ACTION_MOVE:
+ mVelocityTracker.addMovement(event);
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ if (mDockSide != WindowManager.DOCKED_INVALID) {
+ resizeStack(calculatePosition(x, y));
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mVelocityTracker.addMovement(event);
+
+ x = (int) event.getRawX();
+ y = (int) event.getRawY();
+
+ mVelocityTracker.computeCurrentVelocity(1000);
+ fling(x, y, mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+
+ mWindowManager.setSlippery(true);
+ releaseBackground();
+ break;
+ }
+ return true;
+ }
+
+ private void convertToScreenCoordinates(MotionEvent event) {
+ event.setLocation(event.getRawX(), event.getRawY());
+ }
+
+ private void fling(int x, int y, float xVelocity, float yVelocity) {
+ int position = calculatePosition(x, y);
+ float velocity = isHorizontalDivision() ? yVelocity : xVelocity;
+ final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
+ mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity);
+
+ ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ resizeStack((Integer) animation.getAnimatedValue());
+ }
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ commitSnapFlags(snapTarget);
+ mWindowManagerProxy.setResizing(false);
+ mDockSide = WindowManager.DOCKED_INVALID;
+ }
+ });
+ mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity);
+ anim.start();
+ }
+
+ private void commitSnapFlags(SnapTarget target) {
+ if (target.flag == SnapTarget.FLAG_NONE) {
+ return;
+ }
+ boolean dismissOrMaximize;
+ if (target.flag == SnapTarget.FLAG_DISMISS_START) {
+ dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
+ || mDockSide == WindowManager.DOCKED_TOP;
+ } else {
+ dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
+ || mDockSide == WindowManager.DOCKED_BOTTOM;
+ }
+ if (dismissOrMaximize) {
+ mWindowManagerProxy.dismissDockedStack();
+ } else {
+ mWindowManagerProxy.maximizeDockedStack();
+ }
+ }
+
+ private void liftBackground() {
+ if (isHorizontalDivision()) {
+ mBackground.animate().scaleY(1.5f);
+ } else {
+ mBackground.animate().scaleX(1.5f);
+ }
+ mBackground.animate()
+ .setInterpolator(mTouchResponseInterpolator)
+ .setDuration(150)
+ .translationZ(mTouchElevation);
+
+ // Lift handle as well so it doesn't get behind the background, even though it doesn't
+ // cast shadow.
+ mHandle.animate()
+ .setInterpolator(mTouchResponseInterpolator)
+ .setDuration(150)
+ .translationZ(mTouchElevation);
+ }
+
+ private void releaseBackground() {
+ mBackground.animate()
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(200)
+ .translationZ(0)
+ .scaleX(1f)
+ .scaleY(1f);
+ mHandle.animate()
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(200)
+ .translationZ(0);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateDisplayInfo();
+ }
+
+ private void updateDisplayInfo() {
+ DisplayMetrics info = mContext.getResources().getDisplayMetrics();
+ mDisplayWidth = info.widthPixels;
+ mDisplayHeight = info.heightPixels;
+ }
+
+ private int calculatePosition(int touchX, int touchY) {
+ return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX);
+ }
+
+ private boolean isHorizontalDivision() {
+ return mDockSide == WindowManager.DOCKED_TOP
+ || mDockSide == WindowManager.DOCKED_BOTTOM;
+ }
+
+ private int calculateXPosition(int touchX) {
+ return mStartPosition + touchX - mStartX;
+ }
+
+ private int calculateYPosition(int touchY) {
+ return mStartPosition + touchY - mStartY;
+ }
+
+ private void resizeStack(int position) {
+ mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ switch (mDockSide) {
+ case WindowManager.DOCKED_LEFT:
+ mTmpRect.right = position;
+ break;
+ case WindowManager.DOCKED_TOP:
+ mTmpRect.bottom = position;
+ break;
+ case WindowManager.DOCKED_RIGHT:
+ mTmpRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
+ break;
+ case WindowManager.DOCKED_BOTTOM:
+ mTmpRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
+ break;
+ }
+ if (mTmpRect.equals(mLastResizeRect)) {
+ return;
+ }
+
+ // Make sure shadows are updated
+ mBackground.invalidate();
+
+ mLastResizeRect.set(mTmpRect);
+ mWindowManagerProxy.resizeDockedStack(mTmpRect);
+ }
+
+ @Override
+ public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
+ inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(),
+ mHandle.getBottom());
+ inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(),
+ mBackground.getRight(), mBackground.getBottom(), Op.UNION);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
new file mode 100644
index 000000000000..225187455f3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.View;
+import android.view.WindowManager;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+/**
+ * Manages the window parameters of the docked stack divider.
+ */
+public class DividerWindowManager {
+
+ private static final String WINDOW_TITLE = "DockedStackDivider";
+
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLp;
+ private View mView;
+
+ public DividerWindowManager(Context ctx) {
+ mWindowManager = ctx.getSystemService(WindowManager.class);
+ }
+
+ public void add(View view, int width, int height) {
+ mLp = new WindowManager.LayoutParams(
+ width, height, TYPE_DOCK_DIVIDER,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
+ | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
+ PixelFormat.TRANSLUCENT);
+ mLp.setTitle(WINDOW_TITLE);
+ mWindowManager.addView(view, mLp);
+ mView = view;
+ }
+
+ public void remove() {
+ if (mView != null) {
+ mWindowManager.removeView(mView);
+ }
+ mView = null;
+ }
+
+ public void setSlippery(boolean slippery) {
+ boolean changed = false;
+ if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) {
+ mLp.flags |= FLAG_SLIPPERY;
+ changed = true;
+ } else if (!slippery && (mLp.flags & FLAG_SLIPPERY) != 0) {
+ mLp.flags &= ~FLAG_SLIPPERY;
+ changed = true;
+ }
+ if (changed) {
+ mWindowManager.updateViewLayout(mView, mLp);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
new file mode 100644
index 000000000000..94809bc664b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.app.ActivityManagerNative;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.view.WindowManager.DOCKED_INVALID;
+
+/**
+ * Proxy to simplify calls into window manager/activity manager
+ */
+public class WindowManagerProxy {
+
+ private static final String TAG = "WindowManagerProxy";
+
+ @GuardedBy("mResizeRect")
+ private final Rect mResizeRect = new Rect();
+ private final Rect mTmpRect = new Rect();
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+ private final Runnable mResizeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mResizeRect) {
+ mTmpRect.set(mResizeRect);
+ }
+ try {
+ ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID,
+ mTmpRect, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+ };
+
+ public void resizeDockedStack(Rect rect) {
+ synchronized (mResizeRect) {
+ mResizeRect.set(rect);
+ }
+ mExecutor.execute(mResizeRunnable);
+ }
+
+ public void dismissDockedStack() {
+ try {
+ ActivityManagerNative.getDefault().removeStack(DOCKED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove stack: " + e);
+ }
+ }
+
+ public void maximizeDockedStack() {
+ try {
+ ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+
+ public void setResizing(boolean resizing) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling setDockedStackResizing: " + e);
+ }
+ }
+
+ public int getDockSide() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get dock side: " + e);
+ }
+ return DOCKED_INVALID;
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e27dba6fa2f7..e13d3e7d7fa9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17444,14 +17444,16 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("Removing home stack is not allowed.");
}
synchronized (this) {
+ long origId = Binder.clearCallingIdentity();
ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack != null) {
ArrayList<TaskRecord> tasks = stack.getAllTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
- removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ removeTaskByIdLocked(tasks.get(i).taskId, false /* killProcess */,
!REMOVE_FROM_RECENTS);
}
}
+ Binder.restoreCallingIdentity(origId);
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c35b91ffb4f0..031f6d2016b4 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -319,7 +319,7 @@ final class TaskRecord {
mCallingPackage = callingPackage;
mResizeable = resizeable || mService.mForceResizableActivites;
mPrivileged = privileged;
- ActivityInfo info = mActivities.get(0).info;
+ ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null;
mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d89f47cc622d..058d681c6ac9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -76,6 +76,7 @@ import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
@@ -88,6 +89,8 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -397,6 +400,12 @@ public class AudioService extends IAudioService.Stub {
// Broadcast receiver for device connections intent broadcasts
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
+ /** Interface for UserManagerService. */
+ private final UserManagerInternal mUserManagerInternal;
+
+ private final UserRestrictionsListener mUserRestrictionsListener =
+ new AudioServiceUserRestrictionsListener();
+
// Devices currently connected
// Use makeDeviceListKey() to make a unique key for this list.
private class DeviceListSpec {
@@ -598,6 +607,8 @@ public class AudioService extends IAudioService.Stub {
mPlatformType = AudioSystem.getPlatformType(context);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
@@ -694,6 +705,8 @@ public class AudioService extends IAudioService.Stub {
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
+
+ mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
}
public void systemReady() {
@@ -1039,17 +1052,32 @@ public class AudioService extends IAudioService.Stub {
System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
UserHandle.USER_CURRENT);
- boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
- 0, UserHandle.USER_CURRENT) == 1;
+ final int currentUser = getCurrentUserId();
+
+ // In addition to checking the system setting, also check the current user restriction.
+ // Because of the delay before persisting VOLUME_MASTER_MUTE, there's a window where
+ // DISALLOW_ADJUST_VOLUME will be ignored when it's set right before switching users.
+ boolean masterMute = (System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
+ 0, UserHandle.USER_CURRENT) == 1)
+ || mUserManagerInternal.getUserRestriction(
+ currentUser, UserManager.DISALLOW_ADJUST_VOLUME);
if (mUseFixedVolume) {
masterMute = false;
AudioSystem.setMasterVolume(1.0f);
}
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
+ }
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
boolean microphoneMute =
- System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1;
+ (System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1)
+ || mUserManagerInternal.getUserRestriction(
+ currentUser, UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Mic mute %s, user=%d", microphoneMute, currentUser));
+ }
AudioSystem.muteMicrophone(microphoneMute);
// Each stream will read its own persisted settings
@@ -1767,6 +1795,16 @@ public class AudioService extends IAudioService.Stub {
!= PackageManager.PERMISSION_GRANTED) {
return;
}
+ setMasterMuteInternalNoCallerCheck(mute, flags, userId);
+ }
+
+ private void setMasterMuteInternalNoCallerCheck(boolean mute, int flags, int userId) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Master mute %s, %d, user=%d", mute, flags, userId));
+ }
+ if (mUseFixedVolume) {
+ return; // If using fixed volume, we don't mute.
+ }
if (getCurrentUserId() == userId) {
if (mute != AudioSystem.getMasterMute()) {
setSystemAudioMute(mute);
@@ -1841,7 +1879,8 @@ public class AudioService extends IAudioService.Stub {
return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
}
- /** @see AudioManager#setMicrophoneMute(boolean, int) */
+ /** @see AudioManager#setMicrophoneMute(boolean) */
+ @Override
public void setMicrophoneMute(boolean on, String callingPackage, int userId) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
@@ -1863,7 +1902,13 @@ public class AudioService extends IAudioService.Stub {
!= PackageManager.PERMISSION_GRANTED) {
return;
}
+ setMicrophoneMuteNoCallerCheck(on, userId);
+ }
+ private void setMicrophoneMuteNoCallerCheck(boolean on, int userId) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, String.format("Mic mute %s, user=%d", on, userId));
+ }
// If mute is for current user actually mute, else just persist the setting
// which will be loaded on user switch.
if (getCurrentUserId() == userId) {
@@ -5116,6 +5161,35 @@ public class AudioService extends IAudioService.Stub {
}
} // end class AudioServiceBroadcastReceiver
+ private class AudioServiceUserRestrictionsListener implements UserRestrictionsListener {
+
+ @Override
+ public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
+ Bundle prevRestrictions) {
+ // Update mic mute state.
+ {
+ final boolean wasRestricted =
+ prevRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ final boolean isRestricted =
+ newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ if (wasRestricted != isRestricted) {
+ setMicrophoneMuteNoCallerCheck(isRestricted, userId);
+ }
+ }
+
+ // Update speaker mute state.
+ {
+ final boolean wasRestricted =
+ prevRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+ final boolean isRestricted =
+ newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+ if (wasRestricted != isRestricted) {
+ setMasterMuteInternalNoCallerCheck(isRestricted, /* flags =*/ 0, userId);
+ }
+ }
+ }
+ } // end class AudioServiceUserRestrictionsListener
+
private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) {
PackageManager pm = mContext.getPackageManager();
// Find the home activity of the user. It should not be killed to avoid expensive restart,
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 80fd82330fca..e4dbf65764d1 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.app.DownloadManager;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal.PackagesProvider;
@@ -696,7 +697,12 @@ final class DefaultPermissionGrantPolicy {
Intent intent, int userId) {
ResolveInfo handler = mService.resolveIntent(intent,
intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
- if (handler == null) {
+ if (handler == null || handler.activityInfo == null) {
+ return null;
+ }
+ ActivityInfo activityInfo = handler.activityInfo;
+ if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
+ && activityInfo.name.equals(mService.mResolveActivity.name)) {
return null;
}
return getSystemPackageLPr(handler.activityInfo.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c259ac2b2fec..d7176fd0776b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1,28 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.pm;
+import android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.internal.util.SizedInputStream;
+
+import libcore.io.IoUtils;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
class PackageManagerShellCommand extends ShellCommand {
final IPackageManager mInterface;
@@ -42,8 +82,21 @@ class PackageManagerShellCommand extends ShellCommand {
final PrintWriter pw = getOutPrintWriter();
try {
switch(cmd) {
+ case "install":
+ return runInstall();
+ case "install-abandon":
+ case "install-destroy":
+ return runInstallAbandon();
+ case "install-commit":
+ return runInstallCommit();
+ case "install-create":
+ return runInstallCreate();
+ case "install-write":
+ return runInstallWrite();
case "list":
return runList();
+ case "uninstall":
+ return runUninstall();
default:
return handleDefaultCommands(cmd);
}
@@ -53,6 +106,65 @@ class PackageManagerShellCommand extends ShellCommand {
return -1;
}
+ private int runInstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams params = makeInstallParams();
+ final int sessionId = doCreateSession(params.sessionParams,
+ params.installerPackageName, params.userId);
+
+ final String inPath = getNextArg();
+ if (inPath == null && params.sessionParams.sizeBytes == 0) {
+ pw.println("Error: must either specify a package size or an APK file");
+ return 1;
+ }
+ if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk") != 0) {
+ return 1;
+ }
+ if (doCommitSession(sessionId) != 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runInstallAbandon() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doAbandonSession(sessionId);
+ }
+
+ private int runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doCommitSession(sessionId);
+ }
+
+ private int runInstallCreate() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams installParams = makeInstallParams();
+ final int sessionId = doCreateSession(installParams.sessionParams,
+ installParams.installerPackageName, installParams.userId);
+
+ // NOTE: adb depends on parsing this string
+ pw.println("Success: created install session [" + sessionId + "]");
+ return 0;
+ }
+
+ private int runInstallWrite() throws RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(getNextArg());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+
+ final int sessionId = Integer.parseInt(getNextArg());
+ final String splitName = getNextArg();
+ final String path = getNextArg();
+ return doWriteSession(sessionId, path, sizeBytes, splitName);
+ }
+
private int runList() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String type = getNextArg();
@@ -363,6 +475,279 @@ class PackageManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runUninstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int flags = 0;
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-k":
+ flags |= PackageManager.DELETE_KEEP_DATA;
+ break;
+ case "--user":
+ userId = Integer.parseInt(getNextArg());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+
+ userId = translateUserId(userId, "runUninstall");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ flags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId);
+ if (info == null) {
+ pw.println("Failure - not installed for " + userId);
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags,
+ receiver.getIntentSender(), userId);
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return 1;
+ }
+ }
+
+ private static class InstallParams {
+ SessionParams sessionParams;
+ String installerPackageName;
+ int userId = UserHandle.USER_ALL;
+ }
+
+ private InstallParams makeInstallParams() {
+ final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ final InstallParams params = new InstallParams();
+ params.sessionParams = sessionParams;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-l":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ break;
+ case "-r":
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ break;
+ case "-i":
+ params.installerPackageName = getNextArg();
+ if (params.installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ break;
+ case "-t":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ break;
+ case "-s":
+ sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ break;
+ case "-f":
+ sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
+ break;
+ case "-d":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ break;
+ case "-g":
+ sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ break;
+ case "--originating-uri":
+ sessionParams.originatingUri = Uri.parse(getNextArg());
+ break;
+ case "--referrer":
+ sessionParams.referrerUri = Uri.parse(getNextArg());
+ break;
+ case "-p":
+ sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
+ sessionParams.appPackageName = getNextArg();
+ if (sessionParams.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
+ break;
+ case "-S":
+ sessionParams.setSize(Long.parseLong(getNextArg()));
+ break;
+ case "--abi":
+ sessionParams.abiOverride = checkAbiArgument(getNextArg());
+ break;
+ case "--user":
+ params.userId = Integer.parseInt(getNextArg());
+ break;
+ case "--install-location":
+ sessionParams.installLocation = Integer.parseInt(getNextArg());
+ break;
+ case "--force-uuid":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+ sessionParams.volumeUuid = getNextArg();
+ if ("internal".equals(sessionParams.volumeUuid)) {
+ sessionParams.volumeUuid = null;
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+ return params;
+ }
+
+ private static String checkAbiArgument(String abi) {
+ if (TextUtils.isEmpty(abi)) {
+ throw new IllegalArgumentException("Missing ABI argument");
+ }
+
+ if ("-".equals(abi)) {
+ return abi;
+ }
+
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ return abi;
+ }
+ }
+
+ throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+ }
+
+ private int translateUserId(int userId, String logContext) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, logContext, "pm command");
+ }
+
+ private int doCreateSession(SessionParams params, String installerPackageName, int userId)
+ throws RemoteException {
+ userId = translateUserId(userId, "runInstallCreate");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ final int sessionId = mInterface.getPackageInstaller()
+ .createSession(params, installerPackageName, userId);
+ return sessionId;
+ }
+
+ private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName)
+ throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ if ("-".equals(inPath)) {
+ inPath = null;
+ } else if (inPath != null) {
+ final File file = new File(inPath);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
+ }
+
+ final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
+
+ PackageInstaller.Session session = null;
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
+ if (inPath != null) {
+ in = new FileInputStream(inPath);
+ } else {
+ in = new SizedInputStream(getInputStream(), sizeBytes);
+ }
+ out = session.openWrite(splitName, 0, sizeBytes);
+
+ int total = 0;
+ byte[] buffer = new byte[65536];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+
+ if (info.sizeBytes > 0) {
+ final float fraction = ((float) c / (float) info.sizeBytes);
+ session.addProgress(fraction);
+ }
+ }
+ session.fsync(out);
+
+ pw.println("Success: streamed " + total + " bytes");
+ return 0;
+ } catch (IOException e) {
+ pw.println("Error: failed to write; " + e.getMessage());
+ return 1;
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int doCommitSession(int sessionId) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ pw.println("Failure details: " + result.getExtras());
+ }
+ return status;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int doAbandonSession(int sessionId) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+ session.abandon();
+ pw.println("Success");
+ return 0;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
boolean summary, int startProtectionLevel, int endProtectionLevel)
throws RemoteException {
@@ -525,5 +910,34 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" -u: list only the permissions users will see");
pw.println("");
}
+
+ private static class LocalIntentReceiver {
+ private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public int send(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 621ae2d56e7c..4929960bad93 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -52,6 +52,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
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;
@@ -232,6 +233,10 @@ public class UserManagerService extends IUserManager.Stub {
private final LocalService mLocalService;
+ @GuardedBy("mUserRestrictionsListeners")
+ private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners =
+ new ArrayList<>();
+
private static UserManagerService sInstance;
public static UserManagerService getInstance() {
@@ -630,7 +635,7 @@ public class UserManagerService extends IUserManager.Stub {
}
if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
info.flags |= UserInfo.FLAG_INITIALIZED;
- scheduleWriteUserLP(info);
+ scheduleWriteUser(info);
}
}
}
@@ -770,6 +775,7 @@ public class UserManagerService extends IUserManager.Stub {
Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId)
!= newRestrictions);
mBaseUserRestrictions.put(userId, newRestrictions);
+ scheduleWriteUser(mUsers.get(userId));
}
final Bundle effective = computeEffectiveUserRestrictionsLR(userId);
@@ -781,7 +787,14 @@ public class UserManagerService extends IUserManager.Stub {
@GuardedBy("mRestrictionsLock")
private void applyUserRestrictionsLR(int userId, Bundle newRestrictions) {
- final Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+ if (newRestrictions == null) {
+ newRestrictions = Bundle.EMPTY;
+ }
+
+ Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+ if (prevRestrictions == null) {
+ prevRestrictions = Bundle.EMPTY;
+ }
if (DBG) {
Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId
@@ -797,12 +810,36 @@ public class UserManagerService extends IUserManager.Stub {
Binder.restoreCallingIdentity(token);
}
- UserRestrictionsUtils.applyUserRestrictions(
+ UserRestrictionsUtils.applyUserRestrictionsLR(
mContext, userId, newRestrictions, prevRestrictions);
+ notifyUserRestrictionsListeners(userId, newRestrictions, prevRestrictions);
+
mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions));
}
+ private void notifyUserRestrictionsListeners(final int userId,
+ Bundle newRestrictions, Bundle prevRestrictions) {
+
+ final Bundle newRestrictionsFinal = new Bundle(newRestrictions);
+ final Bundle prevRestrictionsFinal = new Bundle(prevRestrictions);
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final UserRestrictionsListener[] listeners;
+ synchronized (mUserRestrictionsListeners) {
+ listeners = new UserRestrictionsListener[mUserRestrictionsListeners.size()];
+ mUserRestrictionsListeners.toArray(listeners);
+ }
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onUserRestrictionsChanged(userId,
+ newRestrictionsFinal, prevRestrictionsFinal);
+ }
+ }
+ });
+ }
+
@GuardedBy("mRestrictionsLock")
private void updateEffectiveUserRestrictionsLR(int userId) {
updateUserRestrictionsInternalLR(null, userId);
@@ -1046,7 +1083,7 @@ public class UserManagerService extends IUserManager.Stub {
UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM);
if ("Primary".equals(user.name)) {
user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
- scheduleWriteUserLP(user);
+ scheduleWriteUser(user);
}
userVersion = 1;
}
@@ -1056,7 +1093,7 @@ public class UserManagerService extends IUserManager.Stub {
UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM);
if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
user.flags |= UserInfo.FLAG_INITIALIZED;
- scheduleWriteUserLP(user);
+ scheduleWriteUser(user);
}
userVersion = 2;
}
@@ -1080,7 +1117,7 @@ public class UserManagerService extends IUserManager.Stub {
if (!splitSystemUser && user.isRestricted()
&& (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) {
user.restrictedProfileParentId = UserHandle.USER_SYSTEM;
- scheduleWriteUserLP(user);
+ scheduleWriteUser(user);
}
}
}
@@ -1125,7 +1162,9 @@ public class UserManagerService extends IUserManager.Stub {
writeUserLP(system);
}
- private void scheduleWriteUserLP(UserInfo userInfo) {
+ private void scheduleWriteUser(UserInfo userInfo) {
+ // No need to wrap it within a lock -- worst case, we'll just post the same message
+ // twice.
if (!mHandler.hasMessages(WRITE_USER_MSG, userInfo)) {
Message msg = mHandler.obtainMessage(WRITE_USER_MSG, userInfo);
mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
@@ -1507,7 +1546,7 @@ public class UserManagerService extends IUserManager.Stub {
if (isManagedProfile) {
if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
parent.profileGroupId = parent.id;
- scheduleWriteUserLP(parent);
+ scheduleWriteUser(parent);
}
userInfo.profileGroupId = parent.profileGroupId;
} else if (isRestricted) {
@@ -1516,7 +1555,7 @@ public class UserManagerService extends IUserManager.Stub {
}
if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
parent.restrictedProfileParentId = parent.id;
- scheduleWriteUserLP(parent);
+ scheduleWriteUser(parent);
}
userInfo.restrictedProfileParentId = parent.restrictedProfileParentId;
}
@@ -1535,7 +1574,7 @@ public class UserManagerService extends IUserManager.Stub {
}
mPm.createNewUserLILPw(userId);
userInfo.partial = false;
- scheduleWriteUserLP(userInfo);
+ scheduleWriteUser(userInfo);
updateUserIds();
Bundle restrictions = new Bundle();
synchronized (mRestrictionsLock) {
@@ -2112,7 +2151,7 @@ public class UserManagerService extends IUserManager.Stub {
}
if (now > EPOCH_PLUS_30_YEARS) {
user.lastLoggedInTime = now;
- scheduleWriteUserLP(user);
+ scheduleWriteUser(user);
}
}
}
@@ -2388,6 +2427,25 @@ public class UserManagerService extends IUserManager.Stub {
}
}
}
+
+ @Override
+ public boolean getUserRestriction(int userId, String key) {
+ return getUserRestrictions(userId).getBoolean(key);
+ }
+
+ @Override
+ public void addUserRestrictionsListener(UserRestrictionsListener listener) {
+ synchronized (mUserRestrictionsListeners) {
+ mUserRestrictionsListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void removeUserRestrictionsListener(UserRestrictionsListener listener) {
+ synchronized (mUserRestrictionsListeners) {
+ mUserRestrictionsListeners.remove(listener);
+ }
+ }
}
private class Shell extends ShellCommand {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 28df9f673ed0..56e8b3eaea2c 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -18,19 +18,14 @@ package com.android.server.pm;
import com.google.android.collect.Sets;
-import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
-import android.media.IAudioService;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.Slog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -39,6 +34,11 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
+/**
+ * Utility methods for uesr restrictions.
+ *
+ * <p>See {@link UserManagerService} for the method suffixes.
+ */
public class UserRestrictionsUtils {
private static final String TAG = "UserRestrictionsUtils";
@@ -129,26 +129,31 @@ public class UserRestrictionsUtils {
/**
* Takes a new use restriction set and the previous set, and apply the restrictions that have
* changed.
+ *
+ * <p>Note this method is called by {@link UserManagerService} while holding
+ * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
+ * a deadlock.
*/
- public static void applyUserRestrictions(Context context, int userId,
- @Nullable Bundle newRestrictions, @Nullable Bundle prevRestrictions) {
- if (newRestrictions == null) {
- newRestrictions = Bundle.EMPTY;
- }
- if (prevRestrictions == null) {
- prevRestrictions = Bundle.EMPTY;
- }
+ public static void applyUserRestrictionsLR(Context context, int userId,
+ Bundle newRestrictions, Bundle prevRestrictions) {
for (String key : USER_RESTRICTIONS) {
final boolean newValue = newRestrictions.getBoolean(key);
final boolean prevValue = prevRestrictions.getBoolean(key);
if (newValue != prevValue) {
- applyUserRestriction(context, userId, key, newValue);
+ applyUserRestrictionLR(context, userId, key, newValue);
}
}
}
- private static void applyUserRestriction(Context context, int userId, String key,
+ /**
+ * Apply each user restriction.
+ *
+ * <p>Note this method is called by {@link UserManagerService} while holding
+ * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
+ * a deadlock.
+ */
+ private static void applyUserRestrictionLR(Context context, int userId, String key,
boolean newValue) {
// When certain restrictions are cleared, we don't update the system settings,
// because these settings are changeable on the Settings UI and we don't know the original
@@ -161,14 +166,6 @@ public class UserRestrictionsUtils {
final long id = Binder.clearCallingIdentity();
try {
switch (key) {
- case UserManager.DISALLOW_UNMUTE_MICROPHONE:
- IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE))
- .setMicrophoneMute(newValue, context.getPackageName(), userId);
- break;
- case UserManager.DISALLOW_ADJUST_VOLUME:
- IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE))
- .setMasterMute(newValue, 0, context.getPackageName(), userId);
- break;
case UserManager.DISALLOW_CONFIG_WIFI:
if (newValue) {
android.provider.Settings.Secure.putIntForUser(cr,
@@ -231,8 +228,6 @@ public class UserRestrictionsUtils {
}
break;
}
- } catch (RemoteException re) {
- Slog.e(TAG, "Failed to talk to AudioService.", re);
} finally {
Binder.restoreCallingIdentity(id);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 25a23fd4c72f..68034c8c4f79 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -49,9 +49,11 @@ import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -71,6 +73,8 @@ import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
// State management of app transitions. When we are preparing for a
// transition, mNextAppTransition will be the kind of transition to
@@ -168,6 +172,8 @@ public class AppTransition implements Dump {
// Keyed by task id.
private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
= new SparseArray<>();
+ private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture;
+ private boolean mNextAppTransitionAnimationsSpecsPending;
private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
private Rect mNextAppTransitionInsets = new Rect();
@@ -200,10 +206,16 @@ public class AppTransition implements Dump {
private int mCurrentUserId = 0;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+ private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
+ private final Object mServiceLock;
+ private final WindowSurfacePlacer mWindowSurfacePlacer;
- AppTransition(Context context, Handler h) {
+ AppTransition(Context context, Handler h, Object serviceLock,
+ WindowSurfacePlacer windowSurfacePlacer) {
mContext = context;
mH = h;
+ mServiceLock = serviceLock;
+ mWindowSurfacePlacer = windowSurfacePlacer;
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -262,6 +274,7 @@ public class AppTransition implements Dump {
void setReady() {
mAppTransitionState = APP_STATE_READY;
+ fetchAppTransitionSpecsFromFuture();
}
boolean isRunning() {
@@ -296,6 +309,14 @@ public class AppTransition implements Dump {
return mNextAppTransitionScaleUp;
}
+ /**
+ * @return true if and only if we are currently fetching app transition specs from the future
+ * passed into {@link #overridePendingAppTransitionMultiThumbFuture}
+ */
+ boolean isFetchingAppTransitionsSpecs() {
+ return mNextAppTransitionAnimationsSpecsPending;
+ }
+
private boolean prepare() {
if (!isRunning()) {
mAppTransitionState = APP_STATE_IDLE;
@@ -1408,6 +1429,24 @@ public class AppTransition implements Dump {
}
}
+ void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
+ boolean scaleUp) {
+ if (isTransitionSet()) {
+ mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
+ : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
+ mNextAppTransitionPackage = null;
+ mDefaultNextAppTransitionAnimationSpec = null;
+ mNextAppTransitionAnimationsSpecs.clear();
+ mNextAppTransitionAnimationsSpecsFuture = specsFuture;
+ mNextAppTransitionScaleUp = scaleUp;
+ postAnimationCallback();
+ mNextAppTransitionCallback = callback;
+ } else {
+ postAnimationCallback();
+ }
+ }
+
void overrideInPlaceAppTransition(String packageName, int anim) {
if (isTransitionSet()) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
@@ -1419,6 +1458,35 @@ public class AppTransition implements Dump {
}
}
+ /**
+ * If a future is set for the app transition specs, fetch it in another thread.
+ */
+ private void fetchAppTransitionSpecsFromFuture() {
+ if (mNextAppTransitionAnimationsSpecsFuture != null) {
+ mNextAppTransitionAnimationsSpecsPending = true;
+ final IAppTransitionAnimationSpecsFuture future
+ = mNextAppTransitionAnimationsSpecsFuture;
+ mNextAppTransitionAnimationsSpecsFuture = null;
+ mDefaultExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ AppTransitionAnimationSpec[] specs = null;
+ try {
+ specs = future.get();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to fetch app transition specs: " + e);
+ }
+ synchronized (mServiceLock) {
+ mNextAppTransitionAnimationsSpecsPending = false;
+ overridePendingAppTransitionMultiThumb(specs, mNextAppTransitionCallback,
+ null /* finishedCallback */, mNextAppTransitionScaleUp);
+ mWindowSurfacePlacer.requestTraversal();
+ }
+ }
+ });
+ }
+ }
+
@Override
public String toString() {
return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 67576baa7ef1..921d27cebe60 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -270,7 +270,14 @@ class DimLayerController {
}
void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- mState.remove(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
+ if (state != null) {
+ // Destroy the surface, unless it's the shared fullscreen dim.
+ if (state.dimLayer != mSharedFullScreenDimLayer) {
+ state.dimLayer.destroySurface();
+ }
+ mState.remove(dimLayerUser);
+ }
}
void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 53f8bbd9084b..8c00a570d837 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -154,6 +154,10 @@ class DisplayContent {
return mDisplayMetrics;
}
+ DockedStackDividerController getDockedDividerController() {
+ return mDividerControllerLocked;
+ }
+
/**
* Returns true if the specified UID has access to this display.
*/
@@ -192,7 +196,6 @@ class DisplayContent {
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
- mDividerControllerLocked.updateDisplayInfo();
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
@@ -373,6 +376,12 @@ class DisplayContent {
*/
if (isFreeformed) {
mTmpRect.inset(-delta, -delta);
+ // Intersect with display content rect. If we have system decor (status bar/
+ // navigation bar), we want to exclude that from the tap detection.
+ // Otherwise, if the app is partially placed under some system button (eg.
+ // Recents, Home), pressing that button would cause a full series of
+ // unwanted transfer focus/resume/pause, before we could go home.
+ mTmpRect.intersect(mContentRect);
}
mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 77ae35a0d21a..63b58b8499c0 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,124 +16,64 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
-import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
-import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP;
-import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
-import static com.android.server.wm.TaskStack.DOCKED_LEFT;
-import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
-import static com.android.server.wm.TaskStack.DOCKED_TOP;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-
import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
/**
- * Controls showing and hiding of a docked stack divider on the display.
+ * Keeps information about the docked stack divider.
*/
-public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser {
- private static final String TAG = "DockedStackDivider";
- private final Context mContext;
- private final int mDividerWidth;
+public class DockedStackDividerController {
+
+ private static final String TAG = "DockedStackDividerController";
+
private final DisplayContent mDisplayContent;
- private final int mSideMargin;
- private final DimLayer mDimLayer;
- private int mDisplayWidth;
- private int mDisplayHeight;
- private View mView;
- private Rect mTmpRect = new Rect();
- private Rect mLastResizeRect = new Rect();
- private int mStartX;
- private int mStartY;
- private TaskStack mTaskStack;
- private Rect mOriginalRect = new Rect();
- private int mDockSide;
- private boolean mDimLayerVisible;
+ private final int mDividerWindowWidth;
+ private final int mDividerInsets;
+ private boolean mResizing;
+ private WindowState mWindow;
+ private final Rect mTmpRect = new Rect();
DockedStackDividerController(Context context, DisplayContent displayContent) {
- mContext = context;
mDisplayContent = displayContent;
- updateDisplayInfo();
- mDividerWidth = context.getResources().getDimensionPixelSize(
+ mDividerWindowWidth = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
- mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics());
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
+ mDividerInsets = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
}
- private void addDivider(Configuration configuration) {
- View view = LayoutInflater.from(mContext).inflate(
- com.android.internal.R.layout.docked_stack_divider, null);
- view.setOnTouchListener(this);
- WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
- final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? mDividerWidth : MATCH_PARENT;
- final int height = landscape ? MATCH_PARENT : mDividerWidth;
- view.setPointerShape(
- landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
- WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- width, height, TYPE_DOCK_DIVIDER,
- FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
- | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
- PixelFormat.OPAQUE);
- params.setTitle(TAG);
- manager.addView(view, params, mDisplayContent.getDisplay(), null);
- mView = view;
+ boolean isResizing() {
+ return mResizing;
}
- private void removeDivider() {
- mView.setOnTouchListener(null);
- WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
- manager.removeView(mView, true /* immediate */);
- mView = null;
+ int getContentWidth() {
+ return mDividerWindowWidth - 2 * mDividerInsets;
}
- boolean hasDivider() {
- return mView != null;
+ void setResizing(boolean resizing) {
+ mResizing = resizing;
}
- void updateDisplayInfo() {
- final DisplayInfo info = mDisplayContent.getDisplayInfo();
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
+ void setWindow(WindowState window) {
+ mWindow = window;
+ reevaluateVisibility();
}
- void update(Configuration configuration, boolean forceUpdate) {
- if (forceUpdate && mView != null) {
- removeDivider();
- }
+ void reevaluateVisibility() {
+ if (mWindow == null) return;
TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
- if (stack != null && stack.hasWindowWithFinalVisibility() && mView == null) {
- addDivider(configuration);
- } else if ((stack == null || !stack.isVisibleLocked()) && mView != null) {
- removeDivider();
+ if (stack != null && stack.isVisibleLocked()) {
+ mWindow.showLw(true /* doAnimation */);
+ } else {
+ mWindow.hideLw(true /* doAnimation */);
}
}
- int getWidth() {
- return mDividerWidth;
- }
-
void positionDockedStackedDivider(Rect frame) {
TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack == null) {
@@ -142,265 +82,25 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
// divider was deferred to WM thread and hasn't happened yet.
return;
}
- final @TaskStack.DockSide int side = stack.getDockSide();
+ int side = stack.getDockSide();
stack.getBounds(mTmpRect);
switch (side) {
case DOCKED_LEFT:
- frame.set(mTmpRect.right, frame.top, mTmpRect.right + frame.width(), frame.bottom);
- break;
- case DOCKED_TOP:
- frame.set(frame.left, mTmpRect.bottom, mTmpRect.right,
- mTmpRect.bottom + frame.height());
- break;
- case DOCKED_RIGHT:
- frame.set(mTmpRect.left - frame.width(), frame.top, mTmpRect.left, frame.bottom);
- break;
- case DOCKED_BOTTOM:
- frame.set(frame.left, mTmpRect.top - frame.height(), frame.right, mTmpRect.top);
- break;
- }
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- final int action = event.getAction() & MotionEvent.ACTION_MASK;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- // We use raw values, because getX/Y() would give us results relative to the
- // dock divider bounds.
- mStartX = (int) event.getRawX();
- mStartY = (int) event.getRawY();
- synchronized (mDisplayContent.mService.mWindowMap) {
- mTaskStack = mDisplayContent.getDockedStackLocked();
- if (mTaskStack != null) {
- mTaskStack.getBounds(mOriginalRect);
- mDockSide = mTaskStack.getDockSide();
- }
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (mTaskStack != null) {
- final int x = (int) event.getRawX();
- final int y = (int) event.getRawY();
- resizeStack(x, y);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mTaskStack != null) {
- final int x = (int) event.getRawX();
- final int y = (int) event.getRawY();
- // At most one of these will be executed, the other one will exit early.
- maybeDismissTaskStack(x, y);
- maybeMaximizeTaskStack(x, y);
- mTaskStack = null;
- }
- setDimLayerVisible(false);
- mDockSide = TaskStack.DOCKED_INVALID;
- break;
- }
- return true;
- }
-
- private void maybeMaximizeTaskStack(int x, int y) {
- final int distance = distanceFromFullScreen(mDockSide, x, y);
- if (distance == -1) {
- Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide);
- return;
- }
- if (distance <= mSideMargin) {
- try {
- mDisplayContent.mService.mActivityManager.resizeStack(
- mTaskStack.mStackId, null, true);
- } catch (RemoteException e) {
- // This can't happen because we are in the same process.
- }
- }
- }
-
- private void maybeDismissTaskStack(int x, int y) {
- final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
- if (distance == -1) {
- Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide);
- return;
- }
- if (distance <= mSideMargin) {
- try {
- mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId);
- } catch (RemoteException e) {
- // This can't happen because we are in the same process.
- }
- }
- }
-
- private void updateDimLayer(int x, int y) {
- final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
- final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y);
- if (dismissDistance == -1 || maximizeDistance == -1) {
- Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide);
- return;
- }
- if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) {
- Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer.");
- return;
- }
- if (dismissDistance <= mSideMargin) {
- setDismissDimLayerVisible(x, y);
- } else if (maximizeDistance <= mSideMargin) {
- setMaximizeDimLayerVisible(x, y);
- } else {
- setDimLayerVisible(false);
- }
- }
-
- /**
- * Provides the distance from the point to the docked side of a rectangle.
- *
- * @return non negative distance or -1 on error
- */
- private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) {
- switch (dockSide) {
- case DOCKED_LEFT:
- return x - bounds.left;
- case DOCKED_TOP:
- return y - bounds.top;
- case DOCKED_RIGHT:
- return bounds.right - x;
- case DOCKED_BOTTOM:
- return bounds.bottom - y;
- }
- return -1;
- }
-
- private int distanceFromFullScreen(int dockSide, int x, int y) {
- switch (dockSide) {
- case DOCKED_LEFT:
- return mDisplayWidth - x;
- case DOCKED_TOP:
- return mDisplayHeight - y;
- case DOCKED_RIGHT:
- return x;
- case DOCKED_BOTTOM:
- return y;
- }
- return -1;
- }
-
- private void setDismissDimLayerVisible(int x, int y) {
- mTmpRect.set(mOriginalRect);
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.right = x;
- break;
- case DOCKED_TOP:
- mTmpRect.bottom = y;
- break;
- case DOCKED_RIGHT:
- mTmpRect.left = x;
- break;
- case DOCKED_BOTTOM:
- mTmpRect.top = y;
- break;
- default:
- Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim "
- + "layer=" + mDockSide);
- return;
- }
- mDimLayer.setBounds(mTmpRect);
- setDimLayerVisible(true);
- }
-
- private void setMaximizeDimLayerVisible(int x, int y) {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.left = x;
- break;
- case DOCKED_TOP:
- mTmpRect.top = y;
- break;
- case DOCKED_RIGHT:
- mTmpRect.right = x;
- break;
- case DOCKED_BOTTOM:
- mTmpRect.top = y;
- break;
- default:
- Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim "
- + "layer=" + mDockSide);
- }
- mDimLayer.setBounds(mTmpRect);
- setDimLayerVisible(true);
- }
-
- private void setDimLayerVisible(boolean visible) {
- if (mDimLayerVisible == visible) {
- return;
- }
- mDimLayerVisible = visible;
- if (mDimLayerVisible) {
- mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
- RESIZING_HINT_DURATION_MS);
- } else {
- mDimLayer.hide();
- }
- }
-
- private void resizeStack(int x, int y) {
- mTmpRect.set(mOriginalRect);
- final int deltaX = x - mStartX;
- final int deltaY = y - mStartY;
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.right += deltaX;
+ frame.set(mTmpRect.right - mDividerInsets, frame.top,
+ mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
break;
case DOCKED_TOP:
- mTmpRect.bottom += deltaY;
+ frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
+ mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
break;
case DOCKED_RIGHT:
- mTmpRect.left += deltaX;
+ frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
+ mTmpRect.left + mDividerInsets, frame.bottom);
break;
case DOCKED_BOTTOM:
- mTmpRect.top += deltaY;
+ frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
+ frame.right, mTmpRect.top + mDividerInsets);
break;
}
- if (mTmpRect.equals(mLastResizeRect)) {
- return;
- }
- mLastResizeRect.set(mTmpRect);
- try {
- mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect, true);
- } catch (RemoteException e) {
- // This can't happen because we are in the same process.
- }
- updateDimLayer(x, y);
- }
-
- boolean isResizing() {
- return mTaskStack != null;
- }
-
- int getWidthAdjustment() {
- return getWidth() / 2;
- }
-
- @Override
- public boolean isFullscreen() {
- return false;
- }
-
- @Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public void getBounds(Rect outBounds) {
- // This dim layer user doesn't need this.
- }
-
- @Override
- public String toShortString() {
- return TAG;
}
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index f35ea669a71d..400cc5e3d9b5 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -27,7 +27,6 @@ import android.content.ClipDescription;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
@@ -52,6 +51,7 @@ class DragState {
SurfaceControl mSurfaceControl;
int mFlags;
IBinder mLocalWin;
+ int mPid;
int mUid;
ClipData mData;
ClipDescription mDataDescription;
@@ -270,8 +270,15 @@ class DragState {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
}
for (WindowState ws : mNotifiedWindows) {
+ float x = 0;
+ float y = 0;
+ if (!mDragResult && (ws.mSession.mPid == mPid)) {
+ // Report unconsumed drop location back to the app that started the drag.
+ x = mCurrentX;
+ y = mCurrentY;
+ }
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
- 0, 0, null, null, null, null, mDragResult);
+ x, y, null, null, null, null, mDragResult);
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f9994020f3c5..3c3123ff0ba3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -193,20 +193,6 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
- if (child.mAttrs.type == TYPE_DOCK_DIVIDER) {
- // We need to determine if the divider is horizontal or vertical and adjust its handle
- // frame accordingly.
- int adjustment = displayContent.mDividerControllerLocked.getWidthAdjustment();
- if (inputWindowHandle.frameRight - inputWindowHandle.frameLeft >
- inputWindowHandle.frameTop - inputWindowHandle.frameBottom) {
- // Horizontal divider.
- inputWindowHandle.frameTop -= adjustment;
- inputWindowHandle.frameBottom += adjustment;
- } else {
- inputWindowHandle.frameLeft -= adjustment;
- inputWindowHandle.frameRight += adjustment;
- }
- }
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5864b25781b0..6aaf3c72d4fc 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -328,6 +328,15 @@ class Task implements DimLayer.DimLayerUser {
}
}
+ /**
+ * Cancels any running thumbnail transitions associated with the task.
+ */
+ void cancelTaskThumbnailTransition() {
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ mAppTokens.get(activityNdx).mAppAnimator.clearThumbnail();
+ }
+ }
+
boolean showForAllUsers() {
final int tokensCount = mAppTokens.size();
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bb3d6f77fc08..7c02b43604be 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,16 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
-import static com.android.server.wm.WindowManagerService.TAG;
-
-import android.annotation.IntDef;
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -39,10 +29,20 @@ import android.view.Surface;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+import static com.android.server.wm.WindowManagerService.TAG;
+
public class TaskStack implements DimLayer.DimLayerUser {
// If the stack should be resized to fullscreen.
@@ -86,21 +86,6 @@ public class TaskStack implements DimLayer.DimLayerUser {
/** Detach this stack from its display when animation completes. */
boolean mDeferDetach;
- static final int DOCKED_INVALID = -1;
- static final int DOCKED_LEFT = 1;
- static final int DOCKED_TOP = 2;
- static final int DOCKED_RIGHT = 3;
- static final int DOCKED_BOTTOM = 4;
-
- @IntDef({
- DOCKED_INVALID,
- DOCKED_LEFT,
- DOCKED_TOP,
- DOCKED_RIGHT,
- DOCKED_BOTTOM})
- @Retention(RetentionPolicy.SOURCE)
- @interface DockSide {}
-
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -401,7 +386,7 @@ public class TaskStack implements DimLayer.DimLayerUser {
final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getWidth(),
+ mDisplayContent.mDividerControllerLocked.getContentWidth(),
dockedOnTopOrLeft);
}
@@ -435,7 +420,6 @@ public class TaskStack implements DimLayer.DimLayerUser {
return;
}
- @DockSide
final int dockedSide = dockedStack.getDockSide();
if (dockedSide == DOCKED_INVALID) {
// Not sure how you got here...Only thing we can do is return current bounds.
@@ -446,9 +430,10 @@ public class TaskStack implements DimLayer.DimLayerUser {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
dockedStack.getRawBounds(mTmpRect2);
- final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
+ final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP
+ || dockedSide == DOCKED_LEFT;
getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
@@ -519,7 +504,7 @@ public class TaskStack implements DimLayer.DimLayerUser {
final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
final int count = mService.mStackIdToStack.size();
@@ -660,7 +645,6 @@ public class TaskStack implements DimLayer.DimLayerUser {
/**
* For docked workspace provides information which side of the screen was the dock anchored.
*/
- @DockSide
int getDockSide() {
if (mStackId != DOCKED_STACK_ID) {
return DOCKED_INVALID;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 4a9d8cbdf006..8b984f03ebd0 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -149,11 +149,11 @@ class WallpaperController {
}
void updateWallpaperVisibility() {
- final boolean visible = isWallpaperVisible(mWallpaperTarget);
final DisplayContent displayContent = mWallpaperTarget.getDisplayContent();
if (displayContent == null) {
return;
}
+ final boolean visible = isWallpaperVisible(mWallpaperTarget);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
@@ -205,7 +205,7 @@ class WallpaperController {
final WindowState wallpaper = token.windows.get(j);
final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
if (!winAnimator.mLastHidden || wasDeferred) {
- winAnimator.hide();
+ winAnimator.hide("hideWallpapers");
dispatchWallpaperVisibility(wallpaper, false);
final DisplayContent displayContent = wallpaper.getDisplayContent();
if (displayContent != null) {
@@ -470,13 +470,13 @@ class WallpaperController {
}
private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) {
-
final WindowAnimator winAnimator = mService.mAnimator;
result.reset();
WindowState w = null;
int windowDetachedI = -1;
boolean resetTopWallpaper = false;
boolean inFreeformSpace = false;
+ boolean replacing = false;
for (int i = windows.size() - 1; i >= 0; i--) {
w = windows.get(i);
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
@@ -504,12 +504,13 @@ class WallpaperController {
inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
+ replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow);
+
// If the app is executing an animation because the keyguard is going away,
// keep the wallpaper during the animation so it doesn't flicker out.
final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
|| (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayAnimation);
- if (hasWallpaper && w.isOnScreen()
- && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+ if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w);
result.setWallpaperTarget(w, i);
if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) {
@@ -530,9 +531,12 @@ class WallpaperController {
"Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w);
result.setWallpaperTarget(w, windowDetachedI);
}
- if (result.wallpaperTarget == null && inFreeformSpace) {
+ if (result.wallpaperTarget == null
+ && (inFreeformSpace || (replacing && mWallpaperTarget != null))) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
- // additional window to make it visible.
+ // additional window to make it visible. When we are replacing a window and there was
+ // wallpaper before replacement, we want to keep the window until the new windows fully
+ // appear and can determine the visibility, to avoid flickering.
result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7fe523e90c7c..3205c6cca53f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -22,6 +22,7 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -117,6 +118,7 @@ import android.view.DisplayInfo;
import android.view.DropPermissionHolder;
import android.view.Gravity;
import android.view.IApplicationToken;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -701,7 +703,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at "
+ newX + "," + newY);
synchronized (mWindowMap) {
- endDrag = completeDrop(newX, newY);
+ endDrag = completeDropLw(newX, newY);
}
} else {
synchronized (mWindowMap) {
@@ -715,7 +717,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+ newX + "," + newY);
synchronized (mWindowMap) {
- endDrag = completeDrop(newX, newY);
+ endDrag = completeDropLw(newX, newY);
}
} break;
@@ -745,11 +747,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private boolean completeDrop(float x, float y) {
+ private boolean completeDropLw(float x, float y) {
WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
+ mDragState.mCurrentX = x;
+ mDragState.mCurrentY = y;
+
DropPermissionHolder dropPermissionHolder = null;
- if ((mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
+ if (dropTargetWin != null &&
+ (mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
(mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
dropPermissionHolder = new DropPermissionHolder(
mDragState.mData,
@@ -904,7 +910,7 @@ public class WindowManagerService extends IWindowManager.Stub
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
- mAppTransition = new AppTransition(context, mH);
+ mAppTransition = new AppTransition(context, mH, mWindowMap, mWindowPlacerLocked);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
mActivityManager = ActivityManagerNative.getDefault();
@@ -1519,7 +1525,7 @@ public class WindowManagerService extends IWindowManager.Stub
// TODO(multidisplay): IMEs are only supported on the default display.
getDefaultWindowListLocked().add(pos, win);
mWindowsChanged = true;
- moveInputMethodDialogsLocked(pos+1);
+ moveInputMethodDialogsLocked(pos + 1);
return;
}
win.mTargetAppToken = null;
@@ -1898,11 +1904,6 @@ public class WindowManagerService extends IWindowManager.Stub
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
- } else if (type == TYPE_DOCK_DIVIDER) {
- if (displayContent.mDividerControllerLocked.hasDivider()) {
- Slog.w(TAG, "Attempted to add docked stack divider twice. Aborting.");
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
} else if (token.appWindowToken != null) {
Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
// It is not valid to use an app token with other system types; we will
@@ -2001,6 +2002,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ if (type == TYPE_DOCK_DIVIDER) {
+ getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win);
+ }
+
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
@@ -2079,7 +2084,9 @@ public class WindowManagerService extends IWindowManager.Stub
// happen, let's just simply add a window.
return;
}
- Rect frame = replacedWindow.mFrame;
+ // We use the visible frame, because we want the animation to morph the window from what
+ // was visible to the user to the final destination of the new window.
+ Rect frame = replacedWindow.mVisibleFrame;
// We treat this as if this activity was opening, so we can trigger the app transition
// animation and piggy-back on existing transition animation infrastructure.
mOpeningApps.add(atoken);
@@ -3502,13 +3509,6 @@ public class WindowManagerService extends IWindowManager.Stub
mLastFinishedFreezeSource = "new-config";
}
mWindowPlacerLocked.performSurfacePlacement();
- if (orientationChanged) {
- for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
- DisplayContent content = mDisplayContents.valueAt(i);
- Message.obtain(mH, H.UPDATE_DOCKED_STACK_DIVIDER, H.DOCK_DIVIDER_FORCE_UPDATE,
- H.UNUSED, content).sendToTarget();
- }
- }
}
}
@@ -3655,8 +3655,8 @@ public class WindowManagerService extends IWindowManager.Stub
int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
boolean scaleUp) {
synchronized(mWindowMap) {
- mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
- startY, targetWidth, targetHeight, startedCallback, scaleUp);
+ mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, startY,
+ targetWidth, targetHeight, startedCallback, scaleUp);
}
}
@@ -3696,6 +3696,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
+ boolean scaleUp) {
+ synchronized(mWindowMap) {
+ mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, callback,
+ scaleUp);
+ }
+ }
+
+ @Override
public void endProlongedAnimations() {
synchronized (mWindowMap) {
for (final WindowState win : mWindowMap.values()) {
@@ -4680,6 +4690,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public void cancelTaskThumbnailTransition(int taskId) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task != null) {
+ task.cancelTaskThumbnailTransition();
+ }
+ }
+ }
+
public void addTask(int taskId, int stackId, boolean toTop) {
synchronized (mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG, "addTask: adding taskId=" + taskId
@@ -7102,6 +7122,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " asbinder=" + window.asBinder());
}
+ final int callerPid = Binder.getCallingPid();
final int callerUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
IBinder token = null;
@@ -7127,6 +7148,7 @@ public class WindowManagerService extends IWindowManager.Stub
final IBinder winBinder = window.asBinder();
token = new Binder();
mDragState = new DragState(this, token, surface, flags, winBinder);
+ mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
token = mDragState.mToken = new Binder();
@@ -7352,16 +7374,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int RESIZE_TASK = 44;
/**
- * Used to indicate in the message that the dock divider needs to be updated only if it's
- * necessary.
- */
- static final int DOCK_DIVIDER_NO_FORCE_UPDATE = 0;
- /**
- * Used to indicate in the message that the dock divider should be force-removed before
- * updating, so new configuration can be applied.
- */
- static final int DOCK_DIVIDER_FORCE_UPDATE = 1;
- /**
* Used to denote that an integer field in a message will not be used.
*/
public static final int UNUSED = 0;
@@ -7904,12 +7916,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
- break;
case UPDATE_DOCKED_STACK_DIVIDER: {
- DisplayContent content = (DisplayContent) msg.obj;
- final boolean forceUpdate = msg.arg1 == DOCK_DIVIDER_FORCE_UPDATE;
synchronized (mWindowMap) {
- content.mDividerControllerLocked.update(mCurConfiguration, forceUpdate);
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .reevaluateVisibility();
}
}
break;
@@ -8527,8 +8537,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowStateAnimator winAnimator = w.mWinAnimator;
boolean layerChanged = false;
int oldLayer = w.mLayer;
- if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
- || (i > 0 && w.mIsWallpaper)) {
+ if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
@@ -8852,7 +8861,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " pid=" + ws.mSession.mPid
+ " uid=" + ws.mSession.mUid);
wsa.destroySurface();
- ws.mHasSurface = false;
+ ws.setHasSurface(false);
mForceRemoves.add(ws);
leakedSurface = true;
} else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
@@ -8862,7 +8871,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " saved=" + ws.mAppToken.mHasSavedSurface);
if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
wsa.destroySurface();
- ws.mHasSurface = false;
+ ws.setHasSurface(false);
ws.mAppToken.mHasSavedSurface = false;
leakedSurface = true;
}
@@ -8909,7 +8918,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
"RECOVER DESTROY", null);
surfaceController.destroyInTransaction();
- winAnimator.mWin.mHasSurface = false;
+ winAnimator.mWin.setHasSurface(false);
scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken);
}
@@ -10102,6 +10111,26 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public int getDockedStackSide() {
+ synchronized (mWindowMap) {
+ TaskStack dockedStack = getDefaultDisplayContentLocked().getDockedStackLocked();
+ return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
+ }
+ }
+
+ @Override
+ public void setDockedStackResizing(boolean resizing) {
+ synchronized (mWindowMap) {
+ getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
+ requestTraversal();
+ }
+ }
+
+ boolean isDockedStackResizingLocked() {
+ return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
+ }
+
static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 307bd4f5df78..6d05921c386c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -108,6 +108,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
static final boolean BOUNDS_FOR_TOUCH = true;
+ static final int DRAG_RESIZE_MODE_FREEFORM = 0;
+ static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
+
final WindowManagerService mService;
final WindowManagerPolicy mPolicy;
final Context mContext;
@@ -145,6 +148,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
boolean mAttachedHidden; // is our parent window hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
boolean mDragResizing;
+ int mResizeMode;
RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -725,7 +729,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
+ mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
mContentFrame.set(mFrame);
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
@@ -1291,6 +1295,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mConfigHasChanged = false;
}
+ void setHasSurface(boolean hasSurface) {
+ mHasSurface = hasSurface;
+ }
+
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
super(inputChannel, mService.mH.getLooper());
@@ -1429,9 +1437,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mWinAnimator + ": " + mPolicyVisibilityAfterAnim);
}
mPolicyVisibility = mPolicyVisibilityAfterAnim;
- if (mDisplayContent != null) {
- mDisplayContent.layoutNeeded = true;
- }
+ setDisplayLayoutNeeded();
if (!mPolicyVisibility) {
if (mService.mCurrentFocus == this) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
@@ -1838,15 +1844,15 @@ final class WindowState implements WindowManagerPolicy.WindowState {
@Override
public void run() {
try {
- mClient.resized(frame, overscanInsets, contentInsets,
- visibleInsets, stableInsets, outsets, reportDraw, newConfig);
+ dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
+ stableInsets, outsets, reportDraw, newConfig);
} catch (RemoteException e) {
// Not a remote call, RemoteException won't be raised.
}
}
});
} else {
- mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+ dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
outsets, reportDraw, newConfig);
}
@@ -1875,6 +1881,17 @@ final class WindowState implements WindowManagerPolicy.WindowState {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
+ Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
+ Configuration newConfig) throws RemoteException {
+ DisplayInfo displayInfo = getDisplayInfo();
+ mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ boolean resizing = computeDragResizing();
+ mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+ outsets, reportDraw, newConfig, inFreeformWorkspace() || !resizing
+ ? frame : mTmpRect);
+ }
+
public void registerFocusObserver(IWindowFocusObserver observer) {
synchronized(mService.mWindowMap) {
if (mFocusCallbacks == null) {
@@ -1907,6 +1924,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mDragResizing != computeDragResizing();
}
+ int getResizeMode() {
+ return mResizeMode;
+ }
+
private boolean computeDragResizing() {
final Task task = getTask();
if (task == null) {
@@ -1921,6 +1942,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
void setDragResizing() {
mDragResizing = computeDragResizing();
+ mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
+ ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
+ : DRAG_RESIZE_MODE_FREEFORM;
}
boolean isDragResizing() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index dbbd4ab35638..91755ac879b0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS
import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
+import static com.android.server.wm.WindowState.*;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -101,7 +102,7 @@ class WindowStateAnimator {
*/
boolean mSurfaceResized;
WindowSurfaceController mSurfaceController;
- WindowSurfaceController mPendingDestroySurface;
+ private WindowSurfaceController mPendingDestroySurface;
/**
* Set if the client has asked that the destroy of its surface be delayed
@@ -109,7 +110,7 @@ class WindowStateAnimator {
*/
boolean mSurfaceDestroyDeferred;
- boolean mDestroyPreservedSurfaceUponRedraw;
+ private boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -460,9 +461,7 @@ class WindowStateAnimator {
if (mSurfaceController != null && mSurfaceController.hasSurface()) {
mService.mDestroySurface.add(mWin);
mWin.mDestroying = true;
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(
- mWin, "HIDE (finishExit)", null);
- hide();
+ hide("finishExit");
}
mWin.mExiting = false;
if (mWin.mRemoveOnExit) {
@@ -472,11 +471,11 @@ class WindowStateAnimator {
mWallpaperControllerLocked.hideWallpapers(mWin);
}
- void hide() {
+ void hide(String reason) {
if (!mLastHidden) {
//dump();
mLastHidden = true;
- mSurfaceController.hideInTransaction();
+ mSurfaceController.hideInTransaction(reason);
}
}
@@ -537,8 +536,8 @@ class WindowStateAnimator {
if (mDestroyPreservedSurfaceUponRedraw) {
return;
}
- mSurfaceController.setLayer(WINDOW_FREEZE_LAYER);
-
+ if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", null);
+ mSurfaceController.setLayer(mAnimLayer + 1);
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
destroySurfaceLocked();
@@ -655,7 +654,7 @@ class WindowStateAnimator {
attrs.getTitle().toString(),
width, height, format, flags, this);
- w.mHasSurface = true;
+ w.setHasSurface(true);
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
Slog.i(TAG, " CREATE SURFACE "
@@ -667,13 +666,13 @@ class WindowStateAnimator {
+ " / " + this);
}
} catch (OutOfResourcesException e) {
- w.mHasSurface = false;
+ w.setHasSurface(false);
Slog.w(TAG, "OutOfResourcesException creating surface");
mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
mDrawState = NO_SURFACE;
return null;
} catch (Exception e) {
- w.mHasSurface = false;
+ w.setHasSurface(false);
Slog.e(TAG, "Exception creating surface", e);
mDrawState = NO_SURFACE;
return null;
@@ -760,7 +759,14 @@ class WindowStateAnimator {
+ ": " + e.toString());
}
- mWin.mHasSurface = false;
+ // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
+ // needs to be cleared to match the WindowState.mHasSurface state. It is also necessary
+ // so it can be recreated successfully in mPendingDestroySurface case.
+ mWin.setHasSurface(false);
+ if (mSurfaceController != null) {
+ mSurfaceController.setShown(false);
+ }
+ mSurfaceController = null;
mDrawState = NO_SURFACE;
}
}
@@ -1063,8 +1069,10 @@ class WindowStateAnimator {
}
final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
+ final boolean isFreeformResizing =
+ w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
final Rect clipRect = mTmpClipRect;
- if (w.isDragResizing()) {
+ if (isFreeformResizing) {
// When we're doing a drag-resizing, the surface is set up to cover full screen.
// Set the clip rect to be the same size so that we don't get any scaling.
clipRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
@@ -1093,7 +1101,7 @@ class WindowStateAnimator {
// animation for docked window, otherwise the animating window will be suddenly cut off.
if (!(mAnimator.mAnimating && w.inDockedWorkspace())) {
- adjustCropToStackBounds(w, clipRect);
+ adjustCropToStackBounds(w, clipRect, isFreeformResizing);
}
w.transformFromScreenToSurfaceSpace(clipRect);
@@ -1104,7 +1112,7 @@ class WindowStateAnimator {
}
}
- private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
+ private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) {
final AppWindowToken appToken = w.mAppToken;
final Task task = w.getTask();
// We don't apply the the stack bounds to the window that is being replaced, because it was
@@ -1117,10 +1125,9 @@ class WindowStateAnimator {
// When we resize we use the big surface approach, which means we can't trust the
// window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
// hardcoding it, we use surface coordinates.
- final boolean isResizing = w.isDragResizing();
- final int frameX = isResizing ? (int) mSurfaceController.getX() :
+ final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() :
w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
- final int frameY = isResizing ? (int) mSurfaceController.getY() :
+ final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
// We need to do some acrobatics with surface position, because their clip region is
// relative to the inside of the surface, but the stack bounds aren't.
@@ -1153,9 +1160,13 @@ class WindowStateAnimator {
// so that we don't need to reallocate during the process. This also prevents
// buffer drops due to size mismatch.
final DisplayInfo displayInfo = w.getDisplayInfo();
- if (displayInfo != null && w.isDragResizing()) {
+
+ // In freeform resize mode, put surface at 0/0.
+ if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
left = 0;
top = 0;
+ }
+ if (displayInfo != null && w.isDragResizing()) {
width = displayInfo.logicalWidth;
height = displayInfo.logicalHeight;
} else {
@@ -1194,9 +1205,9 @@ class WindowStateAnimator {
mSurfaceController.setPositionInTransaction(left, top, recoveringMemory);
mSurfaceResized = mSurfaceController.setSizeInTransaction(width, height,
mDsDx * w.mHScale, mDtDx * w.mVScale,
- mDsDy * w.mHScale, mDtDy * w.mVScale,
+ mDsDy * w.mHScale, mDtDy * w.mVScale,
recoveringMemory);
-
+
if (mSurfaceResized) {
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
@@ -1226,9 +1237,9 @@ class WindowStateAnimator {
if (mIsWallpaper && !mWin.mWallpaperVisible) {
// Wallpaper is no longer visible and there is no wp target => hide it.
- hide();
+ hide("prepareSurfaceLocked");
} else if (w.mAttachedHidden || !w.isOnScreen()) {
- hide();
+ hide("prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
// If we are waiting for this window to handle an
@@ -1260,7 +1271,6 @@ class WindowStateAnimator {
mLastDtDy = mDtDy;
w.mLastHScale = w.mHScale;
w.mLastVScale = w.mVScale;
-
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"controller=" + mSurfaceController +
"alpha=" + mShownAlpha + " layer=" + mAnimLayer
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index d09e00d2df9f..2e2be5c48972 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -95,9 +95,8 @@ class WindowSurfaceController {
}
}
- void hideInTransaction() {
- if (SHOW_TRANSACTIONS) logSurface(
- "HIDE (performLayout)", null);
+ void hideInTransaction(String reason) {
+ if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
if (mSurfaceControl != null) {
mSurfaceShown = false;
try {
@@ -157,7 +156,7 @@ class WindowSurfaceController {
mSurfaceControl.setWindowCrop(clipRect);
mHiddenForCrop = false;
} else {
- hideInTransaction();
+ hideInTransaction("setCrop");
mHiddenForCrop = true;
}
} catch (RuntimeException e) {
@@ -365,6 +364,10 @@ class WindowSurfaceController {
return mSurfaceShown;
}
+ void setShown(boolean surfaceShown) {
+ mSurfaceShown = surfaceShown;
+ }
+
float getX() {
return mSurfaceX;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a8fb77e322bb..1f49e97294cd 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -993,8 +993,7 @@ class WindowSurfacePlacer {
}
mService.mPolicy.finishLayoutLw();
- mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER,
- DOCK_DIVIDER_NO_FORCE_UPDATE, UNUSED, displayContent).sendToTarget();
+ mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
/**
@@ -1260,6 +1259,12 @@ class WindowSurfacePlacer {
}
}
+ // We also need to wait for the specs to be fetched, if needed.
+ if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
+ return false;
+ }
+
// If the wallpaper is visible, we need to check it's ready too.
return !mWallpaperControllerLocked.isWallpaperVisible() ||
mWallpaperControllerLocked.wallpaperTransitionReady();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bf7c745a546e..37e46fbe8a95 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -212,8 +212,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_UNMUTE_MICROPHONE);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADJUST_VOLUME);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SMS);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FUN);
DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SAFE_BOOT);
@@ -4648,7 +4646,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mIPackageManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userHandle.getIdentifier());
- // TODO This will not revert audio mute restrictions if they were set. b/24981972
synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle.getIdentifier());
}
@@ -4799,10 +4796,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (info.isGuest()) {
throw new IllegalStateException("Cannot set a profile owner on a guest");
}
- if (getProfileOwner(userHandle) != null) {
+ if (mOwners.hasProfileOwner(userHandle)) {
throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+ "is already set.");
}
+ if (mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userHandle) {
+ throw new IllegalStateException("Trying to set the profile owner, but the user "
+ + "already has a device owner.");
+ }
int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (hasUserSetupCompleted(userHandle) &&
@@ -4832,6 +4833,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new IllegalStateException("Trying to set the device owner, but device owner "
+ "is already set.");
}
+ if (mOwners.hasProfileOwner(userId)) {
+ throw new IllegalStateException("Trying to set the device owner, but the user already "
+ + "has a profile owner.");
+ }
if (!mUserManager.isUserRunning(new UserHandle(userId))) {
throw new IllegalStateException("User not running: " + userId);
}
@@ -5599,7 +5604,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
Preconditions.checkNotNull(who, "ComponentName is null");
final int userHandle = mInjector.userHandleGetCallingUserId();
- final UserHandle user = new UserHandle(userHandle);
synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
synchronized (this) {
ActiveAdmin activeAdmin =
@@ -5641,6 +5645,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public Bundle getUserRestrictions(ComponentName who) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ synchronized (this) {
+ final ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return activeAdmin.userRestrictions;
+ }
+ }
+
+ @Override
public boolean setApplicationHidden(ComponentName who, String packageName,
boolean hidden) {
Preconditions.checkNotNull(who, "ComponentName is null");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 12b3775d3071..ded44220c995 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -121,17 +121,20 @@ class Owners {
if (!legacy.delete()) {
Slog.e(TAG, "Failed to remove the legacy setting file");
}
- return;
- }
+ } else {
+ // No legacy file, read from the new format files.
+ new DeviceOwnerReadWriter().readFromFileLocked();
- // No legacy file, read from the new format files.
- new DeviceOwnerReadWriter().readFromFileLocked();
-
- final List<UserInfo> users = mUserManager.getUsers();
- for (UserInfo ui : users) {
- new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo ui : users) {
+ new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
+ }
}
}
+ if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) {
+ Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported",
+ getDeviceOwnerUserId()));
+ }
}
String getDeviceOwnerPackageName() {
@@ -218,6 +221,10 @@ class Owners {
return mDeviceOwner != null;
}
+ boolean hasProfileOwner(int userId) {
+ return getProfileOwnerComponent(userId) != null;
+ }
+
/**
* @return true if user restrictions need to be migrated for DO.
*/
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 727858b77f41..36980e3c7d49 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -490,6 +490,17 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ // Try to set a profile owner on the same user, which should fail.
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
+ try {
+ dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM);
+ fail("IllegalStateException not thrown");
+ } catch (IllegalStateException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("already has a device owner"));
+ }
+
// TODO Test getDeviceOwnerName() too. To do so, we need to change
// DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
@@ -509,6 +520,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"));
fail("Didn't throw IllegalArgumentException");
} catch (IllegalArgumentException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("Invalid component"));
}
}
@@ -597,6 +610,17 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testSetProfileOwner() throws Exception {
setAsProfileOwner(admin1);
+
+ // Try setting DO on the same user, which should fail.
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+ dpm.setActiveAdmin(admin2, /* refreshing= */ true, DpmMockContext.CALLER_USER_HANDLE);
+ try {
+ dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE);
+ fail("IllegalStateException not thrown");
+ } catch (IllegalStateException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("already has a profile owner"));
+ }
}
public void testSetProfileOwner_failures() throws Exception {
@@ -739,6 +763,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
DpmTestUtils.newRestrictions(),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -748,6 +776,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS);
@@ -755,6 +788,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -762,6 +799,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
DpmTestUtils.newRestrictions(),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpm.getUserRestrictions(admin1)
+ );
// TODO Check inner calls.
// TODO Make sure restrictions are written to the file.
@@ -787,6 +828,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
.ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ ),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
@@ -798,6 +846,12 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
.ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_OUTGOING_CALLS
+ ),
+ dpm.getUserRestrictions(admin1)
+ );
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -806,6 +860,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
.ensureUserRestrictions()
);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpm.getUserRestrictions(admin1)
+ );
// TODO Check inner calls.
// TODO Make sure restrictions are written to the file.
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
index 9a1fe151a2dc..11d8136b9f5d 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -100,16 +100,14 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
* SSLHandshakeException when used for a connection.
*/
private NetworkSecurityConfig getEmptyConfig() {
- return new NetworkSecurityConfig(true, false,
- new PinSet(new ArraySet<Pin>(), -1),
- new ArrayList<CertificatesEntryRef>());
+ return new NetworkSecurityConfig.Builder().build();
}
private NetworkSecurityConfig getSystemStoreConfig() {
- ArrayList<CertificatesEntryRef> defaultSource = new ArrayList<CertificatesEntryRef>();
- defaultSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
- return new NetworkSecurityConfig(true, false, new PinSet(new ArraySet<Pin>(),
- -1), defaultSource);
+ return new NetworkSecurityConfig.Builder()
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
}
public void testEmptyConfig() throws Exception {
@@ -126,24 +124,20 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
new Domain("android.com", true), getEmptyConfig()));
- ArrayList<CertificatesEntryRef> defaultSource = new ArrayList<CertificatesEntryRef>();
- defaultSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
- NetworkSecurityConfig defaultConfig = new NetworkSecurityConfig(true, false,
- new PinSet(new ArraySet<Pin>(), -1),
- defaultSource);
+ NetworkSecurityConfig defaultConfig = getSystemStoreConfig();
SSLContext context = getSSLContext(new TestConfigSource(domainMap, defaultConfig));
assertConnectionFails(context, "android.com", 443);
assertConnectionSucceeds(context, "google.com", 443);
}
public void testBadPin() throws Exception {
- ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
- systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
ArraySet<Pin> pins = new ArraySet<Pin>();
pins.add(new Pin("SHA-256", new byte[0]));
- NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
- new PinSet(pins, Long.MAX_VALUE),
- systemSource);
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
@@ -155,13 +149,13 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
}
public void testGoodPin() throws Exception {
- ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
- systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
ArraySet<Pin> pins = new ArraySet<Pin>();
pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
- NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
- new PinSet(pins, Long.MAX_VALUE),
- systemSource);
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
@@ -174,13 +168,13 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
public void testOverridePins() throws Exception {
// Use a bad pin + granting the system CA store the ability to override pins.
- ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
- systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), true));
ArraySet<Pin> pins = new ArraySet<Pin>();
pins.add(new Pin("SHA-256", new byte[0]));
- NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
- new PinSet(pins, Long.MAX_VALUE),
- systemSource);
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
+ .build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
@@ -220,14 +214,33 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
assertConnectionFails(context, "developer.android.com", 443);
}
+ public void testConfigBuilderUsesParents() throws Exception {
+ // Check that a builder with a parent uses the parent's values when non is set.
+ NetworkSecurityConfig config = new NetworkSecurityConfig.Builder()
+ .setParent(NetworkSecurityConfig.getDefaultBuilder())
+ .build();
+ assert(!config.getTrustAnchors().isEmpty());
+ }
+
+ public void testConfigBuilderParentLoop() throws Exception {
+ NetworkSecurityConfig.Builder config1 = new NetworkSecurityConfig.Builder();
+ NetworkSecurityConfig.Builder config2 = new NetworkSecurityConfig.Builder();
+ config1.setParent(config2);
+ try {
+ config2.setParent(config1);
+ fail("Loop in NetworkSecurityConfig parents");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
public void testWithUrlConnection() throws Exception {
- ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
- systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
ArraySet<Pin> pins = new ArraySet<Pin>();
pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
- NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
- new PinSet(pins, Long.MAX_VALUE),
- systemSource);
+ NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+ .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+ .addCertificatesEntryRef(
+ new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ .build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 7ee06f3a428b..3c260a83ecde 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -241,6 +241,13 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
+ boolean scaleUp) throws RemoteException {
+
+ }
+
+ @Override
public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
IRemoteCallback callback0, IRemoteCallback callback1, boolean scaleUp) {
// TODO Auto-generated method stub
@@ -515,10 +522,23 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public int getDockedStackSide() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void setDockedStackResizing(boolean resizing) throws RemoteException {
+ }
+
+ @Override
public void cancelTaskWindowTransition(int taskId) {
}
@Override
+ public void cancelTaskThumbnailTransition(int taskId) {
+ }
+
+ @Override
public void endProlongedAnimations() {
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index c2d8d0c3c77b..3662573849a9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -48,7 +48,7 @@ public final class BridgeWindow implements IWindow {
@Override
public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6,
- boolean b, Configuration configuration) throws RemoteException {
+ boolean b, Configuration configuration, Rect rect7) throws RemoteException {
// pass for now.
}