summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/ActivityThread.java19
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl4
-rw-r--r--core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java94
-rw-r--r--core/java/android/hardware/display/DisplayManager.java14
-rw-r--r--core/java/android/os/Trace.java24
-rw-r--r--core/java/android/os/UserHandle.java2
-rw-r--r--core/java/android/os/storage/OWNERS1
-rw-r--r--core/java/android/provider/Telephony.java7
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java5
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java5
-rw-r--r--core/java/android/view/IWindowManager.aidl15
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java4
-rw-r--r--core/java/android/view/autofill/AutofillManager.java8
-rw-r--r--core/java/android/window/WindowContainerTransaction.java31
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java17
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java6
-rw-r--r--core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java92
-rw-r--r--core/java/com/android/internal/app/SuggestedLocaleAdapter.java97
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java10
-rw-r--r--core/java/com/android/internal/view/inline/InlineTooltipUi.java154
-rw-r--r--core/java/com/android/internal/widget/MessagingImageMessage.java7
-rw-r--r--core/jni/android_os_Trace.cpp18
-rw-r--r--core/proto/android/server/windowmanagertransitiontrace.proto68
-rw-r--r--core/res/res/layout/chooser_grid.xml2
-rw-r--r--core/res/res/layout/language_picker_section_header.xml1
-rw-r--r--core/res/res/layout/resolver_list.xml2
-rw-r--r--core/res/res/layout/resolver_profile_tab_button.xml2
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/dimens.xml1
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--data/etc/platform.xml4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java355
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java3
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java11
-rw-r--r--packages/SystemUI/res/values/dimens.xml45
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt340
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt735
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt139
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java15
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java66
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java34
-rw-r--r--services/companion/java/com/android/server/companion/CompanionApplicationController.java6
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java15
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java26
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java17
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java18
-rw-r--r--services/core/java/com/android/server/display/RampAnimator.java35
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java21
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/ApexPackageInfo.java9
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java149
-rw-r--r--services/core/java/com/android/server/pm/InitAppsHelper.java1
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java128
-rw-r--r--services/core/java/com/android/server/pm/PackageInstalledInfo.java4
-rw-r--r--services/core/java/com/android/server/pm/PreferredActivityHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/Settings.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java102
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java64
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java15
-rw-r--r--services/core/java/com/android/server/wm/Transition.java43
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java45
-rw-r--r--services/core/java/com/android/server/wm/TransitionTracer.java234
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java43
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowOrientationListener.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java7
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd10
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt4
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java2
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java48
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java127
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java73
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java47
-rw-r--r--telephony/java/android/telephony/data/DataProfile.java7
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java2
106 files changed, 3330 insertions, 853 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index d37f80671a6b..31b0f057d4ea 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7673,7 +7673,7 @@ package android.app.admin {
field public static final String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
field public static final String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
field public static final String DELEGATION_SECURITY_LOGGING = "delegation-security-logging";
- field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
+ field @Deprecated public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2c605151ee63..2fff53791f52 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3367,7 +3367,7 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
- method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean);
+ method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 69868ab76c3c..57bfc64adaff 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -207,7 +207,6 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
import com.android.server.am.MemInfoDumpProto;
@@ -1404,7 +1403,6 @@ public final class ActivityThread extends ClientTransactionHandler
ContextImpl.class,
Activity.class,
WebView.class,
- OpenSSLSocketImpl.class,
View.class,
ViewRootImpl.class
};
@@ -1412,9 +1410,8 @@ public final class ActivityThread extends ClientTransactionHandler
long appContextInstanceCount = instanceCounts[0];
long activityInstanceCount = instanceCounts[1];
long webviewInstanceCount = instanceCounts[2];
- long openSslSocketCount = instanceCounts[3];
- long viewInstanceCount = instanceCounts[4];
- long viewRootInstanceCount = instanceCounts[5];
+ long viewInstanceCount = instanceCounts[3];
+ long viewRootInstanceCount = instanceCounts[4];
int globalAssetCount = AssetManager.getGlobalAssetCount();
int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
@@ -1447,7 +1444,6 @@ public final class ActivityThread extends ClientTransactionHandler
pw.print(binderProxyObjectCount); pw.print(',');
pw.print(binderDeathObjectCount); pw.print(',');
- pw.print(openSslSocketCount); pw.print(',');
// SQL
pw.print(stats.memoryUsed / 1024); pw.print(',');
@@ -1484,8 +1480,7 @@ public final class ActivityThread extends ClientTransactionHandler
printRow(pw, TWO_COUNT_COLUMNS, "Parcel memory:", parcelSize/1024,
"Parcel count:", parcelCount);
printRow(pw, TWO_COUNT_COLUMNS, "Death Recipients:", binderDeathObjectCount,
- "OpenSSL Sockets:", openSslSocketCount);
- printRow(pw, ONE_COUNT_COLUMN, "WebViews:", webviewInstanceCount);
+ "WebViews:", webviewInstanceCount);
// SQLite mem info
pw.println(" ");
@@ -1558,7 +1553,6 @@ public final class ActivityThread extends ClientTransactionHandler
ContextImpl.class,
Activity.class,
WebView.class,
- OpenSSLSocketImpl.class,
View.class,
ViewRootImpl.class
};
@@ -1566,9 +1560,8 @@ public final class ActivityThread extends ClientTransactionHandler
long appContextInstanceCount = instanceCounts[0];
long activityInstanceCount = instanceCounts[1];
long webviewInstanceCount = instanceCounts[2];
- long openSslSocketCount = instanceCounts[3];
- long viewInstanceCount = instanceCounts[4];
- long viewRootInstanceCount = instanceCounts[5];
+ long viewInstanceCount = instanceCounts[3];
+ long viewRootInstanceCount = instanceCounts[4];
int globalAssetCount = AssetManager.getGlobalAssetCount();
int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
@@ -1610,8 +1603,6 @@ public final class ActivityThread extends ClientTransactionHandler
proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
binderDeathObjectCount);
- proto.write(MemInfoDumpProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT,
- openSslSocketCount);
proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
webviewInstanceCount);
proto.end(oToken);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 578e29090d13..939763ac811d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6490,8 +6490,10 @@ public class DevicePolicyManager {
* Result code for {@link #getStorageEncryptionStatus}: indicating that encryption is not
* currently active, but is currently being activated.
* <p>
- * This result code has never actually been used.
+ * @deprecated This result code has never actually been used, so there is no reason for apps to
+ * check for it.
*/
+ @Deprecated
public static final int ENCRYPTION_STATUS_ACTIVATING = 2;
/**
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
index a61bb3304c12..34d016adbc06 100644
--- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
+++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
@@ -15,8 +15,8 @@
*/
package android.hardware.camera2.extension;
-import android.hardware.camera2.extension.Size;
import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.Size;
import android.view.Surface;
/** @hide */
@@ -35,5 +35,5 @@ parcelable CameraOutputConfig
OutputConfigId outputId;
int surfaceGroupId;
String physicalCameraId;
- List<OutputConfigId> surfaceSharingOutputConfigs;
+ List<CameraOutputConfig> sharedSurfaceConfigs;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 5503e2834d98..c8dc2d0b0b91 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -217,60 +217,30 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
previewSurface, captureSurface);
List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
- // map camera output ids to output configurations
- HashMap<Integer, OutputConfiguration> cameraOutputs = new HashMap<>();
- for (CameraOutputConfig output : outputConfigs) {
- OutputConfiguration cameraOutput = null;
- switch(output.type) {
- case CameraOutputConfig.TYPE_SURFACE:
- if (output.surface == null) {
- Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
- ", skipping!");
- continue;
- }
- cameraOutput = new OutputConfiguration(output.surfaceGroupId,
- output.surface);
- break;
- case CameraOutputConfig.TYPE_IMAGEREADER:
- if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
- (output.size.height <= 0)) {
- Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
- ", skipping!");
- continue;
- }
- ImageReader reader = ImageReader.newInstance(output.size.width,
- output.size.height, output.imageFormat, output.capacity);
- mReaderMap.put(output.outputId.id, reader);
- cameraOutput = new OutputConfiguration(output.surfaceGroupId,
- reader.getSurface());
- break;
- case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
- // Support for multi-resolution outputs to be added in future releases
- default:
- throw new IllegalArgumentException("Unsupported output config type: " +
- output.type);
- }
- cameraOutput.setPhysicalCameraId(output.physicalCameraId);
- cameraOutputs.put(output.outputId.id, cameraOutput);
- }
-
ArrayList<OutputConfiguration> outputList = new ArrayList<>();
for (CameraOutputConfig output : outputConfigs) {
- if (!cameraOutputs.containsKey(output.outputId.id)) {
- // Shared surface already removed by a previous iteration
+ Surface outputSurface = initializeSurfrace(output);
+ if (outputSurface == null) {
continue;
}
- OutputConfiguration outConfig = cameraOutputs.get(output.outputId.id);
- if ((output.surfaceSharingOutputConfigs != null) &&
- !output.surfaceSharingOutputConfigs.isEmpty()) {
- outConfig.enableSurfaceSharing();
- for (OutputConfigId outputId : output.surfaceSharingOutputConfigs) {
- outConfig.addSurface(cameraOutputs.get(outputId.id).getSurface());
- cameraOutputs.remove(outputId.id);
+ OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+ outputSurface);
+
+ if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) {
+ cameraOutput.enableSurfaceSharing();
+ for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) {
+ Surface sharedSurface = initializeSurfrace(sharedOutputConfig);
+ if (sharedSurface == null) {
+ continue;
+ }
+ cameraOutput.addSurface(sharedSurface);
+ mCameraConfigMap.put(sharedSurface, sharedOutputConfig);
}
}
- outputList.add(outConfig);
- mCameraConfigMap.put(outConfig.getSurface(), output);
+
+ cameraOutput.setPhysicalCameraId(output.physicalCameraId);
+ outputList.add(cameraOutput);
+ mCameraConfigMap.put(cameraOutput.getSurface(), output);
}
SessionConfiguration sessionConfiguration = new SessionConfiguration(
@@ -995,4 +965,32 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters);
return ret;
}
+
+ private Surface initializeSurfrace(CameraOutputConfig output) {
+ switch(output.type) {
+ case CameraOutputConfig.TYPE_SURFACE:
+ if (output.surface == null) {
+ Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+ ", skipping!");
+ return null;
+ }
+ return output.surface;
+ case CameraOutputConfig.TYPE_IMAGEREADER:
+ if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
+ (output.size.height <= 0)) {
+ Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+ ", skipping!");
+ return null;
+ }
+ ImageReader reader = ImageReader.newInstance(output.size.width,
+ output.size.height, output.imageFormat, output.capacity);
+ mReaderMap.put(output.outputId.id, reader);
+ return reader.getSurface();
+ case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
+ // Support for multi-resolution outputs to be added in future releases
+ default:
+ throw new IllegalArgumentException("Unsupported output config type: " +
+ output.type);
+ }
+ }
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 731ea9207283..eadcac91dcd7 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -108,6 +108,17 @@ public final class DisplayManager {
public static final String DISPLAY_CATEGORY_PRESENTATION =
"android.hardware.display.category.PRESENTATION";
+ /**
+ * Display category: All displays, including disabled displays.
+ * <p>
+ * This returns all displays, including currently disabled and inaccessible displays.
+ *
+ * @see #getDisplays(String)
+ * @hide
+ */
+ public static final String DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED =
+ "android.hardware.display.category.ALL_INCLUDING_DISABLED";
+
/** @hide **/
@IntDef(prefix = "VIRTUAL_DISPLAY_FLAG_", flag = true, value = {
VIRTUAL_DISPLAY_FLAG_PUBLIC,
@@ -552,7 +563,8 @@ public final class DisplayManager {
final int[] displayIds = mGlobal.getDisplayIds();
synchronized (mLock) {
try {
- if (category == null) {
+ if (category == null
+ || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
addAllDisplaysLocked(mTempDisplays, displayIds);
} else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index ac2156e9e46e..4965057a7fdb 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -140,7 +140,7 @@ public final class Trace {
String trackName, String name, int cookie);
@FastNative
private static native void nativeAsyncTraceForTrackEnd(long tag,
- String trackName, String name, int cookie);
+ String trackName, int cookie);
@FastNative
private static native void nativeInstant(long tag, String name);
@FastNative
@@ -281,8 +281,10 @@ public final class Trace {
/**
* Writes a trace message to indicate that a given section of code has
* begun. Must be followed by a call to {@link #asyncTraceForTrackEnd} using the same
- * tag. This function operates exactly like {@link #asyncTraceBegin(long, String, int)},
+ * track name and cookie.
+ * This function operates exactly like {@link #asyncTraceBegin(long, String, int)},
* except with the inclusion of a track name argument for where this method should appear.
+ * The cookie must be unique on the trackName level, not the methodName level
*
* @param traceTag The trace tag.
* @param trackName The track where the event should appear in the trace.
@@ -302,19 +304,31 @@ public final class Trace {
* Writes a trace message to indicate that the current method has ended.
* Must be called exactly once for each call to
* {@link #asyncTraceForTrackBegin(long, String, String, int)}
- * using the same tag, track name, name and cookie.
+ * using the same tag, track name, and cookie.
*
* @param traceTag The trace tag.
* @param trackName The track where the event should appear in the trace.
- * @param methodName The method name to appear in the trace.
* @param cookie Unique identifier for distinguishing simultaneous events
*
* @hide
*/
public static void asyncTraceForTrackEnd(long traceTag,
+ @NonNull String trackName, int cookie) {
+ if (isTagEnabled(traceTag)) {
+ nativeAsyncTraceForTrackEnd(traceTag, trackName, cookie);
+ }
+ }
+
+ /**
+ * @deprecated use asyncTraceForTrackEnd without methodName argument
+ *
+ * @hide
+ */
+ @Deprecated
+ public static void asyncTraceForTrackEnd(long traceTag,
@NonNull String trackName, @NonNull String methodName, int cookie) {
if (isTagEnabled(traceTag)) {
- nativeAsyncTraceForTrackEnd(traceTag, trackName, methodName, cookie);
+ nativeAsyncTraceForTrackEnd(traceTag, trackName, cookie);
}
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 3d5abb3b8a2f..d831ecb5e816 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -368,7 +368,7 @@ public final class UserHandle implements Parcelable {
@UnsupportedAppUsage
@TestApi
public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
- if (MU_ENABLED) {
+ if (MU_ENABLED && appId >= 0) {
return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
} else {
return appId;
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index ff126e12cf61..1f686e5c449c 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -8,3 +8,4 @@ sahanas@google.com
abkaur@google.com
chiangi@google.com
narayan@google.com
+dipankarb@google.com
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index cbfd805d2363..154fcab8827d 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -931,9 +931,10 @@ public final class Telephony {
* Set as a "result" extra in the {@link #SMS_REJECTED_ACTION} intent to indicate an sms
* was received while the phone was in encrypted state.
* <p>
- * This result is never used on devices that launched with Android 10 (API level 29) or
- * higher, since Android's storage encryption implementation has changed and it no
- * longer can cause the rejection of incoming SMS messages.
+ * This result code is only used on devices that use Full Disk Encryption. Support for
+ * Full Disk Encryption was entirely removed in API level 33, having been replaced by
+ * File Based Encryption. Devices that use File Based Encryption never reject incoming
+ * SMS messages due to the encryption state.
*/
public static final int RESULT_SMS_RECEIVED_WHILE_ENCRYPTED = 9;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index acefebc232ae..65f0824a9b78 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -258,7 +258,10 @@ public abstract class NotificationListenerService extends Service {
public static final int REASON_CLEAR_DATA = 21;
/** Notification was canceled due to an assistant adjustment update. */
public static final int REASON_ASSISTANT_CANCEL = 22;
- /** Notification was canceled when lockdown mode is enabled. */
+ /**
+ * Notification was canceled when entering lockdown mode, which turns off
+ * Smart Lock, fingerprint unlocking, and notifications on the lock screen.
+ */
public static final int REASON_LOCKDOWN = 23;
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 1e22856c1bde..9679a6ab3acb 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1539,8 +1539,9 @@ public abstract class WallpaperService extends Service {
// may have been destroyed so now we need to make
// sure it is re-created.
doOffsetsChanged(false);
- // force relayout to get new surface
- updateSurface(true, false, false);
+ // It will check mSurfaceCreated so no need to force relayout.
+ updateSurface(false /* forceRelayout */, false /* forceReport */,
+ false /* redrawNeeded */);
}
onVisibilityChanged(visible);
if (mReportedVisible && mFrozenRequested) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f246cd9fa107..acdff4fb52fd 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -543,6 +543,21 @@ interface IWindowManager
boolean isWindowTraceEnabled();
/**
+ * Starts a transition trace.
+ */
+ void startTransitionTrace();
+
+ /**
+ * Stops a transition trace.
+ */
+ void stopTransitionTrace();
+
+ /**
+ * Returns true if transition trace is enabled.
+ */
+ boolean isTransitionTraceEnabled();
+
+ /**
* Gets the windowing mode of the display.
*
* @param displayId The id of the display.
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 4d07171d3086..43d427db2c75 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -237,10 +237,6 @@ public interface WindowManagerPolicyConstants {
*/
int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
- // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
- // make app pair split only have single root then we can just attach the
- // divider to the single root task in shell.
- int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 62e0a521c735..c18d089f6375 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -521,6 +521,14 @@ public final class AutofillManager {
public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS =
"autofill_dialog_hints";
+ /**
+ * Sets a value of delay time to show up the inline tooltip view.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY =
+ "autofill_inline_tooltip_first_show_delay";
+
private static final String DIALOG_HINTS_DELIMITER = ":";
/** @hide */
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 7dc039d44f95..633d87937049 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -384,12 +384,10 @@ public final class WindowContainerTransaction implements Parcelable {
*/
@NonNull
public WindowContainerTransaction setAdjacentRoots(
- @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2,
- boolean moveTogether) {
+ @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
root1.asBinder(),
- root2.asBinder(),
- moveTogether));
+ root2.asBinder()));
return this;
}
@@ -1106,9 +1104,6 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mReparentTopOnly;
- // TODO(b/207185041): Remove this once having a single-top root for split screen.
- private boolean mMoveAdjacentTogether;
-
@Nullable
private int[] mWindowingModes;
@@ -1171,12 +1166,10 @@ public final class WindowContainerTransaction implements Parcelable {
}
/** Create a hierarchy op for setting adjacent root tasks. */
- public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2,
- boolean moveTogether) {
+ public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
.setContainer(root1)
.setReparentContainer(root2)
- .setMoveAdjacentTogether(moveTogether)
.build();
}
@@ -1223,7 +1216,6 @@ public final class WindowContainerTransaction implements Parcelable {
mInsetsProviderFrame = copy.mInsetsProviderFrame;
mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
- mMoveAdjacentTogether = copy.mMoveAdjacentTogether;
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
@@ -1245,7 +1237,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
- mMoveAdjacentTogether = in.readBoolean();
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
@@ -1300,10 +1291,6 @@ public final class WindowContainerTransaction implements Parcelable {
return mReparentTopOnly;
}
- public boolean getMoveAdjacentTogether() {
- return mMoveAdjacentTogether;
- }
-
public int[] getWindowingModes() {
return mWindowingModes;
}
@@ -1356,8 +1343,7 @@ public final class WindowContainerTransaction implements Parcelable {
return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
return "{SetAdjacentRoot: container=" + mContainer
- + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether="
- + mMoveAdjacentTogether + "}";
+ + " adjacentRoot=" + mReparent + "}";
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
return "{LaunchTask: " + mLaunchOptions + "}";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
@@ -1413,7 +1399,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
- dest.writeBoolean(mMoveAdjacentTogether);
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
@@ -1458,8 +1443,6 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mReparentTopOnly;
- private boolean mMoveAdjacentTogether;
-
@Nullable
private int[] mWindowingModes;
@@ -1515,11 +1498,6 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
- Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) {
- mMoveAdjacentTogether = moveAdjacentTogether;
- return this;
- }
-
Builder setWindowingModes(@Nullable int[] windowingModes) {
mWindowingModes = windowingModes;
return this;
@@ -1570,7 +1548,6 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mInsetsProviderFrame = mInsetsProviderFrame;
hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
- hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether;
hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 9eec2128fd9d..33325d382e94 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1002,6 +1002,7 @@ public class ChooserActivity extends ResolverActivity implements
mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
adjustPreviewWidth(newConfig.orientation, null);
updateStickyContentPreview();
+ updateTabPadding();
}
private boolean shouldDisplayLandscape(int orientation) {
@@ -1024,6 +1025,20 @@ public class ChooserActivity extends ResolverActivity implements
updateLayoutWidth(R.id.content_preview_file_layout, width, parent);
}
+ private void updateTabPadding() {
+ if (shouldShowTabs()) {
+ View tabs = findViewById(R.id.tabs);
+ float iconSize = getResources().getDimension(R.dimen.chooser_icon_size);
+ // The entire width consists of icons or padding. Divide the item padding in half to get
+ // paddingHorizontal.
+ float padding = (tabs.getWidth() - mMaxTargetsPerRow * iconSize)
+ / mMaxTargetsPerRow / 2;
+ // Subtract the margin the buttons already have.
+ padding -= getResources().getDimension(R.dimen.resolver_profile_tab_margin);
+ tabs.setPadding((int) padding, 0, (int) padding, 0);
+ }
+ }
+
private void updateLayoutWidth(int layoutResourceId, int width, View parent) {
View view = parent.findViewById(layoutResourceId);
if (view != null && view.getLayoutParams() != null) {
@@ -2493,6 +2508,8 @@ public class ChooserActivity extends ResolverActivity implements
recyclerView.setAdapter(gridAdapter);
((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
mMaxTargetsPerRow);
+
+ updateTabPadding();
}
UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index d35d5ca6fca7..e6cc62472f5c 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -86,7 +86,11 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
final LayoutInflater inflater = LayoutInflater.from(getContext());
final ViewGroup rootView =
(ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false);
- return new ChooserProfileDescriptor(rootView, adapter);
+ ChooserProfileDescriptor profileDescriptor =
+ new ChooserProfileDescriptor(rootView, adapter);
+ profileDescriptor.recyclerView.setAccessibilityDelegateCompat(
+ new ChooserRecyclerViewAccessibilityDelegate(profileDescriptor.recyclerView));
+ return profileDescriptor;
}
RecyclerView getListViewForIndex(int index) {
diff --git a/core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java b/core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java
new file mode 100644
index 000000000000..66c9838bf71b
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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.internal.app;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerViewAccessibilityDelegate;
+
+class ChooserRecyclerViewAccessibilityDelegate extends RecyclerViewAccessibilityDelegate {
+ private final Rect mTempRect = new Rect();
+ private final int[] mConsumed = new int[2];
+
+ ChooserRecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
+ super(recyclerView);
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(
+ @NonNull ViewGroup host,
+ @NonNull View view,
+ @NonNull AccessibilityEvent event) {
+ boolean result = super.onRequestSendAccessibilityEvent(host, view, event);
+ if (result && event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+ ensureViewOnScreenVisibility((RecyclerView) host, view);
+ }
+ return result;
+ }
+
+ /**
+ * Bring the view that received accessibility focus on the screen.
+ * The method's logic is based on a model where RecyclerView is a child of another scrollable
+ * component (ResolverDrawerLayout) and can be partially scrolled off the screen. In that case,
+ * RecyclerView's children that are positioned fully within RecyclerView bounds but scrolled
+ * out of the screen by the outer component, when selected by the accessibility navigation will
+ * remain off the screen (as neither components detect such specific case).
+ * If the view that receiving accessibility focus is scrolled of the screen, perform the nested
+ * scrolling to make in visible.
+ */
+ private void ensureViewOnScreenVisibility(RecyclerView recyclerView, View view) {
+ View child = recyclerView.findContainingItemView(view);
+ if (child == null) {
+ return;
+ }
+ recyclerView.getBoundsOnScreen(mTempRect, true);
+ int recyclerOnScreenTop = mTempRect.top;
+ int recyclerOnScreenBottom = mTempRect.bottom;
+ child.getBoundsOnScreen(mTempRect);
+ int dy = 0;
+ // if needed, do the page-length scroll instead of just a row-length scroll as
+ // ResolverDrawerLayout snaps to the compact view and the row-length scroll can be snapped
+ // back right away.
+ if (mTempRect.top < recyclerOnScreenTop) {
+ // snap to the bottom
+ dy = mTempRect.bottom - recyclerOnScreenBottom;
+ } else if (mTempRect.bottom > recyclerOnScreenBottom) {
+ // snap to the top
+ dy = mTempRect.top - recyclerOnScreenTop;
+ }
+ nestedVerticalScrollBy(recyclerView, dy);
+ }
+
+ private void nestedVerticalScrollBy(RecyclerView recyclerView, int dy) {
+ if (dy == 0) {
+ return;
+ }
+ recyclerView.startNestedScroll(View.SCROLL_AXIS_VERTICAL);
+ if (recyclerView.dispatchNestedPreScroll(0, dy, mConsumed, null)) {
+ dy -= mConsumed[1];
+ }
+ recyclerView.scrollBy(0, dy);
+ recyclerView.stopNestedScroll();
+ }
+}
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 5fe111148c91..ff188dc6f2b9 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -27,6 +27,7 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.R;
@@ -104,6 +105,13 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
@Override
public int getItemViewType(int position) {
if (!showHeaders()) {
+ LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position);
+ if (item.isSystemLocale()) {
+ return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER;
+ }
+ if (item.isAppCurrentLocale()) {
+ return TYPE_CURRENT_LOCALE;
+ }
return TYPE_LOCALE;
} else {
if (position == 0) {
@@ -193,15 +201,11 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
}
int itemType = getItemViewType(position);
+ View itemView = getNewViewIfNeeded(convertView, parent, itemType, position);
switch (itemType) {
case TYPE_HEADER_SUGGESTED: // intentional fallthrough
case TYPE_HEADER_ALL_OTHERS:
- // Covers both null, and "reusing" a wrong kind of view
- if (!(convertView instanceof TextView)) {
- convertView = mInflater.inflate(R.layout.language_picker_section_header,
- parent, false);
- }
- TextView textView = (TextView) convertView;
+ TextView textView = (TextView) itemView;
if (itemType == TYPE_HEADER_SUGGESTED) {
setTextTo(textView, R.string.language_picker_section_suggested);
} else {
@@ -215,38 +219,77 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
mDisplayLocale != null ? mDisplayLocale : Locale.getDefault());
break;
case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
- if (!(convertView instanceof ViewGroup)) {
- TextView title;
- if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) {
- convertView = mInflater.inflate(
- R.layout.app_language_picker_current_locale_item, parent, false);
- title = convertView.findViewById(R.id.language_picker_item);
- addStateDescriptionIntoCurrentLocaleItem(convertView);
- } else {
- convertView = mInflater.inflate(
+ TextView title;
+ if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) {
+ title = itemView.findViewById(R.id.language_picker_item);
+ } else {
+ title = itemView.findViewById(R.id.locale);
+ }
+ title.setText(R.string.system_locale_title);
+ break;
+ case TYPE_CURRENT_LOCALE:
+ updateTextView(itemView,
+ itemView.findViewById(R.id.language_picker_item), position);
+ break;
+ default:
+ updateTextView(itemView, itemView.findViewById(R.id.locale), position);
+ break;
+ }
+ return itemView;
+ }
+
+ /** Check if the old view can be reused, otherwise create a new one. */
+ private View getNewViewIfNeeded(
+ View convertView, ViewGroup parent, int itemType, int position) {
+ View updatedView = convertView;
+ boolean shouldReuseView;
+ switch (itemType) {
+ case TYPE_HEADER_SUGGESTED: // intentional fallthrough
+ case TYPE_HEADER_ALL_OTHERS:
+ shouldReuseView = convertView instanceof TextView
+ && convertView.findViewById(R.id.language_picker_header) != null;
+ if (!shouldReuseView) {
+ updatedView = mInflater.inflate(
+ R.layout.language_picker_section_header, parent, false);
+ }
+ break;
+ case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
+ if (((LocaleStore.LocaleInfo) getItem(position)).isAppCurrentLocale()) {
+ shouldReuseView = convertView instanceof LinearLayout
+ && convertView.findViewById(R.id.language_picker_item) != null;
+ if (!shouldReuseView) {
+ updatedView = mInflater.inflate(
+ R.layout.app_language_picker_current_locale_item,
+ parent, false);
+ addStateDescriptionIntoCurrentLocaleItem(updatedView);
+ }
+ } else {
+ shouldReuseView = convertView instanceof TextView
+ && convertView.findViewById(R.id.locale) != null;
+ if (!shouldReuseView) {
+ updatedView = mInflater.inflate(
R.layout.language_picker_item, parent, false);
- title = convertView.findViewById(R.id.locale);
}
- title.setText(R.string.system_locale_title);
}
break;
case TYPE_CURRENT_LOCALE:
- if (!(convertView instanceof ViewGroup)) {
- convertView = mInflater.inflate(
+ shouldReuseView = convertView instanceof LinearLayout
+ && convertView.findViewById(R.id.language_picker_item) != null;
+ if (!shouldReuseView) {
+ updatedView = mInflater.inflate(
R.layout.app_language_picker_current_locale_item, parent, false);
- addStateDescriptionIntoCurrentLocaleItem(convertView);
+ addStateDescriptionIntoCurrentLocaleItem(updatedView);
}
- updateTextView(
- convertView, convertView.findViewById(R.id.language_picker_item), position);
break;
default:
- // Covers both null, and "reusing" a wrong kind of view
- if (!(convertView instanceof ViewGroup)) {
- convertView = mInflater.inflate(R.layout.language_picker_item, parent, false);
+ shouldReuseView = convertView instanceof TextView
+ && convertView.findViewById(R.id.locale) != null;
+ if (!shouldReuseView) {
+ updatedView = mInflater.inflate(R.layout.language_picker_item, parent, false);
}
- updateTextView(convertView, convertView.findViewById(R.id.locale), position);
+ break;
}
- return convertView;
+ return updatedView;
}
private boolean showHeaders() {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 1fd04109ae45..6eae34bf1999 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -331,6 +331,16 @@ public class ArrayUtils {
return array;
}
+ @NonNull
+ public static int[] convertToIntArray(@NonNull ArraySet<Integer> set) {
+ final int size = set.size();
+ int[] array = new int[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = set.valueAt(i);
+ }
+ return array;
+ }
+
public static @Nullable long[] convertToLongArray(@Nullable int[] intArray) {
if (intArray == null) return null;
long[] array = new long[intArray.length];
diff --git a/core/java/com/android/internal/view/inline/InlineTooltipUi.java b/core/java/com/android/internal/view/inline/InlineTooltipUi.java
index 25fa678d0507..3eae89e350a0 100644
--- a/core/java/com/android/internal/view/inline/InlineTooltipUi.java
+++ b/core/java/com/android/internal/view/inline/InlineTooltipUi.java
@@ -15,24 +15,30 @@
*/
package com.android.internal.view.inline;
+import static android.view.autofill.AutofillManager.DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY;
import static android.view.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.ContextWrapper;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.transition.Transition;
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.inline.InlineContentView;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
/**
* UI container for the inline suggestion tooltip.
@@ -40,6 +46,8 @@ import java.io.PrintWriter;
public final class InlineTooltipUi extends PopupWindow implements AutoCloseable {
private static final String TAG = "InlineTooltipUi";
+ private static final int FIRST_TIME_SHOW_DEFAULT_DELAY_MS = 250;
+
private final WindowManager mWm;
private final ViewGroup mContentContainer;
@@ -47,6 +55,16 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
private WindowManager.LayoutParams mWindowLayoutParams;
+ private DelayShowRunnable mDelayShowTooltip;
+
+ private boolean mHasEverDetached;
+
+ private boolean mDelayShowAtStart = true;
+ private boolean mDelaying = false;
+ private int mShowDelayConfigMs;
+
+ private final Rect mTmpRect = new Rect();
+
private final View.OnAttachStateChangeListener mAnchorOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@Override
@@ -56,6 +74,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
@Override
public void onViewDetachedFromWindow(View v) {
+ mHasEverDetached = true;
dismiss();
}
};
@@ -66,6 +85,13 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mHasEverDetached) {
+ // If the tooltip is ever detached, skip adjusting the position,
+ // because it only accepts to attach once and does not show again
+ // after detaching.
+ return;
+ }
+
if (mHeight != bottom - top) {
mHeight = bottom - top;
adjustPosition();
@@ -77,6 +103,13 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
mContentContainer = new LinearLayout(new ContextWrapper(context));
mWm = context.getSystemService(WindowManager.class);
+ // That's a default delay time, and it will scale via the value of
+ // Settings.Global.ANIMATOR_DURATION_SCALE
+ mShowDelayConfigMs = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY,
+ FIRST_TIME_SHOW_DEFAULT_DELAY_MS);
+
setTouchModal(false);
setOutsideTouchable(true);
setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
@@ -95,7 +128,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
@Override
public void close() {
- hide();
+ dismiss();
}
@Override
@@ -117,14 +150,57 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
* The effective {@code update} method that should be called by its clients.
*/
public void update(View anchor) {
+ if (anchor == null) {
+ final View oldAnchor = getAnchor();
+ if (oldAnchor != null) {
+ removeDelayShowTooltip(oldAnchor);
+ }
+ return;
+ }
+
+ if (mDelayShowAtStart) {
+ // To avoid showing when the anchor is doing the fade in animation. That will
+ // cause the tooltip to show in the wrong position and jump at the start.
+ mDelayShowAtStart = false;
+ mDelaying = true;
+
+ if (mDelayShowTooltip == null) {
+ mDelayShowTooltip = new DelayShowRunnable(anchor);
+ }
+
+ int delayTimeMs = mShowDelayConfigMs;
+ try {
+ final float scale = Settings.Global.getFloat(
+ anchor.getContext().getContentResolver(),
+ Settings.Global.ANIMATOR_DURATION_SCALE);
+ delayTimeMs *= scale;
+ } catch (Settings.SettingNotFoundException e) {
+ // do nothing
+ }
+ anchor.postDelayed(mDelayShowTooltip, delayTimeMs);
+ } else if (!mDelaying) {
+ // Note: If we are going to reuse the tooltip, we need to take care the delay in
+ // the case that update for the new anchor.
+ updateInner(anchor);
+ }
+ }
+
+ private void removeDelayShowTooltip(View anchor) {
+ if (mDelayShowTooltip != null) {
+ anchor.removeCallbacks(mDelayShowTooltip);
+ mDelayShowTooltip = null;
+ }
+ }
+
+ private void updateInner(View anchor) {
+ if (mHasEverDetached) {
+ return;
+ }
// set to the application type with the highest z-order
setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
- // The first time to show up, the height of tooltip is zero,
- // so set the offset Y to 2 * anchor height.
- final int achoredHeight = mContentContainer.getHeight();
- final int offsetY = (achoredHeight == 0)
- ? -anchor.getHeight() << 1 : -anchor.getHeight() - achoredHeight;
+ final int offsetY = -anchor.getHeight() - getPreferHeight(anchor);
+
if (!isShowing()) {
setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
@@ -135,6 +211,34 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
}
+ private int getPreferHeight(View anchor) {
+ // The first time to show up, the height of tooltip is zero, so make its height
+ // the same as anchor.
+ final int achoredHeight = mContentContainer.getHeight();
+ return (achoredHeight == 0) ? anchor.getHeight() : achoredHeight;
+ }
+
+ @Override
+ protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+ int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
+ boolean isAbove = super.findDropDownPosition(anchor, outParams, xOffset, yOffset, width,
+ height, gravity, allowScroll);
+ // Make the tooltips y fo position is above or under the parent of the anchor,
+ // otherwise suggestions doesn't clickable.
+ ViewParent parent = anchor.getParent();
+ if (parent instanceof View) {
+ final Rect r = mTmpRect;
+ ((View) parent).getGlobalVisibleRect(r);
+ if (isAbove) {
+ outParams.y = r.top - getPreferHeight(anchor);
+ } else {
+ outParams.y = r.bottom + 1;
+ }
+ }
+
+ return isAbove;
+ }
+
@Override
protected void update(View anchor, WindowManager.LayoutParams params) {
// update content view for the anchor is scrolling
@@ -175,7 +279,9 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
final View anchor = getAnchor();
if (anchor != null) {
anchor.removeOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener);
+ removeDelayShowTooltip(anchor);
}
+ mHasEverDetached = true;
super.detachFromAnchor();
}
@@ -185,7 +291,6 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
return;
}
- setShowing(false);
setTransitioningToDismiss(true);
hide();
@@ -193,6 +298,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
if (getOnDismissListener() != null) {
getOnDismissListener().onDismiss();
}
+ super.dismiss();
}
private void adjustPosition() {
@@ -202,15 +308,15 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
private void show(WindowManager.LayoutParams params) {
- if (sVerbose) {
- Slog.v(TAG, "show()");
- }
mWindowLayoutParams = params;
try {
params.packageName = "android";
params.setTitle("Autofill Inline Tooltip"); // Title is set for debugging purposes
if (!mShowing) {
+ if (sVerbose) {
+ Slog.v(TAG, "show()");
+ }
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.privateFlags |=
@@ -232,11 +338,11 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
private void hide() {
- if (sVerbose) {
- Slog.v(TAG, "hide()");
- }
try {
if (mShowing) {
+ if (sVerbose) {
+ Slog.v(TAG, "hide()");
+ }
mContentContainer.removeOnLayoutChangeListener(mAnchoredOnLayoutChangeListener);
mWm.removeView(mContentContainer);
mShowing = false;
@@ -336,4 +442,26 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
}
}
+
+ private class DelayShowRunnable implements Runnable {
+ WeakReference<View> mAnchor;
+
+ DelayShowRunnable(View anchor) {
+ mAnchor = new WeakReference<>(anchor);
+ }
+
+ @Override
+ public void run() {
+ mDelaying = false;
+ final View anchor = mAnchor.get();
+ if (anchor != null) {
+ updateInner(anchor);
+ }
+ }
+
+ public void setAnchor(View anchor) {
+ mAnchor.clear();
+ mAnchor = new WeakReference<>(anchor);
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index f7955c3f72da..8e7fe18b222b 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -226,6 +226,13 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mDrawable == null) {
+ Log.e(TAG, "onMeasure() after recycle()!");
+ setMeasuredDimension(0, 0);
+ return;
+ }
+
if (mIsIsolated) {
// When isolated we have a fixed size, let's use that sizing.
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 734b6ca47660..1c61c7be4414 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -82,9 +82,8 @@ static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass,
});
}
-static void android_os_Trace_nativeAsyncTraceForTrackBegin(JNIEnv* env, jclass, jlong tag,
- jstring trackStr, jstring nameStr,
- jint cookie) {
+static void android_os_Trace_nativeAsyncTraceForTrackBegin(JNIEnv* env, jclass,
+ jlong tag, jstring trackStr, jstring nameStr, jint cookie) {
withString(env, trackStr, [env, tag, nameStr, cookie](char* track) {
withString(env, nameStr, [tag, track, cookie](char* name) {
atrace_async_for_track_begin(tag, track, name, cookie);
@@ -92,13 +91,10 @@ static void android_os_Trace_nativeAsyncTraceForTrackBegin(JNIEnv* env, jclass,
});
}
-static void android_os_Trace_nativeAsyncTraceForTrackEnd(JNIEnv* env, jclass, jlong tag,
- jstring trackStr, jstring nameStr,
- jint cookie) {
- withString(env, trackStr, [env, tag, nameStr, cookie](char* track) {
- withString(env, nameStr, [tag, track, cookie](char* name) {
- atrace_async_for_track_end(tag, track, name, cookie);
- });
+static void android_os_Trace_nativeAsyncTraceForTrackEnd(JNIEnv* env, jclass,
+ jlong tag, jstring trackStr, jint cookie) {
+ withString(env, trackStr, [tag, cookie](char* track) {
+ atrace_async_for_track_end(tag, track, cookie);
});
}
@@ -156,7 +152,7 @@ static const JNINativeMethod gTraceMethods[] = {
"(JLjava/lang/String;Ljava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceForTrackBegin },
{ "nativeAsyncTraceForTrackEnd",
- "(JLjava/lang/String;Ljava/lang/String;I)V",
+ "(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceForTrackEnd },
{ "nativeInstant",
"(JLjava/lang/String;)V",
diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto
new file mode 100644
index 000000000000..9429127b2f6e
--- /dev/null
+++ b/core/proto/android/server/windowmanagertransitiontrace.proto
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+syntax = "proto3";
+
+package com.android.server.wm.shell;
+
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
+
+option java_multiple_files = true;
+
+/* Represents a file full of transition entries.
+ Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.TRNTRACE), such
+ that it can be easily identified. */
+message TransitionTraceProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x544e5254; /* TRNT (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+ }
+
+ fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ int64 timestamp = 2; /* The timestamp of when the trace was started. */
+ repeated Transition transition = 3;
+}
+
+message Transition {
+
+ enum State {
+ COLLECTING = 0;
+ PENDING = -1;
+ STARTED = 1;
+ PLAYING = 2;
+ ABORT = 3;
+ FINISHED = 4;
+ }
+
+ int32 id = 1;
+ int32 transition_type = 2;
+ int64 timestamp = 3;
+ State state = 5;
+ int32 flags = 6;
+ repeated ChangeInfo change = 7;
+}
+
+message ChangeInfo {
+ com.android.server.wm.IdentifierProto window_identifier = 1;
+ int32 transit_mode = 2;
+ bool has_changed = 3;
+ int32 change_flags = 4;
+}
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 6e3a11af27a7..b102b4bb2124 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -41,7 +41,7 @@
android:src="@drawable/ic_drag_handle"
android:layout_marginTop="@dimen/chooser_edge_margin_thin"
android:layout_marginBottom="@dimen/chooser_edge_margin_thin"
- android:tint="@color/lighter_gray"
+ android:tint="?attr/colorSurfaceVariant"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true" />
diff --git a/core/res/res/layout/language_picker_section_header.xml b/core/res/res/layout/language_picker_section_header.xml
index 4fa4d9b043c6..58042f9a42f8 100644
--- a/core/res/res/layout/language_picker_section_header.xml
+++ b/core/res/res/layout/language_picker_section_header.xml
@@ -24,4 +24,5 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:textColor="?android:attr/colorAccent"
android:textStyle="bold"
+ android:id="@+id/language_picker_header"
tools:text="@string/language_picker_section_all"/>
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 8480ec37a79e..6a200d05c2d7 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -90,11 +90,13 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <!-- horizontal padding = 8dp content padding - 4dp margin that tab buttons have. -->
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tabStripEnabled="false"
+ android:paddingHorizontal="4dp"
android:visibility="gone" />
<FrameLayout
android:id="@android:id/tabcontent"
diff --git a/core/res/res/layout/resolver_profile_tab_button.xml b/core/res/res/layout/resolver_profile_tab_button.xml
index 936c8e23b87a..fd168e6414f1 100644
--- a/core/res/res/layout/resolver_profile_tab_button.xml
+++ b/core/res/res/layout/resolver_profile_tab_button.xml
@@ -21,7 +21,7 @@
android:layout_height="36dp"
android:layout_weight="1"
android:layout_marginVertical="6dp"
- android:layout_marginHorizontal="4dp"
+ android:layout_marginHorizontal="@dimen/resolver_profile_tab_margin"
android:background="@drawable/resolver_profile_tab_bg"
android:textColor="@color/resolver_profile_tab_text"
android:textSize="@dimen/resolver_tab_text_size"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ab6798282cb4..ddaf2e84ad5d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3004,6 +3004,10 @@
</string-array>
+ <!-- Whether to show a notification informing users about notification permission settings
+ upon upgrade to T from a pre-T version -->
+ <bool name="config_notificationReviewPermissions">false</bool>
+
<!-- Default Gravity setting for the system Toast view. Equivalent to: Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM -->
<integer name="config_toastDefaultGravity">0x00000051</integer>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index ff8477a62665..09571213e81f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -962,6 +962,7 @@
<dimen name="resolver_title_padding_bottom">0dp</dimen>
<dimen name="resolver_empty_state_container_padding_top">48dp</dimen>
<dimen name="resolver_empty_state_container_padding_bottom">8dp</dimen>
+ <dimen name="resolver_profile_tab_margin">4dp</dimen>
<dimen name="chooser_action_button_icon_size">18dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5c851eb35ca7..900dcb9da54c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2268,6 +2268,7 @@
<java-symbol type="integer" name="config_screenshotChordKeyTimeout" />
<java-symbol type="integer" name="config_maxResolverActivityColumns" />
<java-symbol type="array" name="config_notificationSignalExtractors" />
+ <java-symbol type="bool" name="config_notificationReviewPermissions" />
<java-symbol type="layout" name="notification_material_action" />
<java-symbol type="layout" name="notification_material_action_list" />
@@ -4323,6 +4324,7 @@
<java-symbol type="dimen" name="resolver_title_padding_bottom" />
<java-symbol type="dimen" name="resolver_empty_state_container_padding_top" />
<java-symbol type="dimen" name="resolver_empty_state_container_padding_bottom" />
+ <java-symbol type="dimen" name="resolver_profile_tab_margin" />
<java-symbol type="string" name="config_deviceSpecificDisplayAreaPolicyProvider" />
@@ -4800,6 +4802,7 @@
<java-symbol type="layout" name="app_language_picker_current_locale_item" />
<java-symbol type="id" name="system_locale_subtitle" />
<java-symbol type="id" name="language_picker_item" />
+ <java-symbol type="id" name="language_picker_header" />
<java-symbol type="dimen" name="status_bar_height_default" />
</resources>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 61bb1ee2561f..75e188ff37bb 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -120,6 +120,10 @@
<group gid="reserved_disk" />
</permission>
+ <permission name="android.permission.WRITE_SECURITY_LOG">
+ <group gid="security_log" />
+ </permission>
+
<!-- These are permissions that were mapped to gids but we need
to keep them here until an upgrade from L to the current
version is to be supported. These permissions are built-in
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 23cf1d9654c4..3380a23f3c09 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -47,6 +47,7 @@ import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
+import androidx.annotation.GuardedBy;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import com.android.internal.annotations.VisibleForTesting;
@@ -65,9 +66,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private static final String TAG = "SplitController";
@VisibleForTesting
+ @GuardedBy("mLock")
final SplitPresenter mPresenter;
// Currently applied split configuration.
+ @GuardedBy("mLock")
private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
/**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
@@ -76,6 +79,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* organizer.
*/
@VisibleForTesting
+ @GuardedBy("mLock")
final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
// Callback to Jetpack to notify about changes to split states.
@@ -83,6 +87,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
private final Handler mHandler;
+ private final Object mLock = new Object();
public SplitController() {
final MainThreadExecutor executor = new MainThreadExecutor();
@@ -100,180 +105,183 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/** Updates the embedding rules applied to future activity launches. */
@Override
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
- mSplitRules.clear();
- mSplitRules.addAll(rules);
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- updateAnimationOverride(mTaskContainers.valueAt(i));
+ synchronized (mLock) {
+ mSplitRules.clear();
+ mSplitRules.addAll(rules);
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ updateAnimationOverride(mTaskContainers.valueAt(i));
+ }
}
}
@NonNull
- public List<EmbeddingRule> getSplitRules() {
+ List<EmbeddingRule> getSplitRules() {
return mSplitRules;
}
/**
- * Starts an activity to side of the launchingActivity with the provided split config.
- */
- public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
- @Nullable Bundle options, @NonNull SplitRule sideRule,
- @Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) {
- try {
- mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule,
- isPlaceholder);
- } catch (Exception e) {
- if (failureCallback != null) {
- failureCallback.accept(e);
- }
- }
- }
-
- /**
* Registers the split organizer callback to notify about changes to active splits.
*/
@Override
public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
- mEmbeddingCallback = callback;
- updateCallbackIfNecessary();
+ synchronized (mLock) {
+ mEmbeddingCallback = callback;
+ updateCallbackIfNecessary();
+ }
}
@Override
public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
- TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
- if (container == null) {
- return;
- }
+ synchronized (mLock) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
+ if (container == null) {
+ return;
+ }
- container.setInfo(taskFragmentInfo);
- if (container.isFinished()) {
- mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+ container.setInfo(taskFragmentInfo);
+ if (container.isFinished()) {
+ mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+ }
+ updateCallbackIfNecessary();
}
- updateCallbackIfNecessary();
}
@Override
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
- TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
- if (container == null) {
- return;
- }
+ synchronized (mLock) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
+ if (container == null) {
+ return;
+ }
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- final boolean wasInPip = isInPictureInPicture(container);
- container.setInfo(taskFragmentInfo);
- final boolean isInPip = isInPictureInPicture(container);
- // Check if there are no running activities - consider the container empty if there are no
- // non-finishing activities left.
- if (!taskFragmentInfo.hasRunningActivity()) {
- if (taskFragmentInfo.isTaskFragmentClearedForPip()) {
- // Do not finish the dependents if the last activity is reparented to PiP.
- // Instead, the original split should be cleanup, and the dependent may be expanded
- // to fullscreen.
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final boolean wasInPip = isInPictureInPicture(container);
+ container.setInfo(taskFragmentInfo);
+ final boolean isInPip = isInPictureInPicture(container);
+ // Check if there are no running activities - consider the container empty if there are
+ // no non-finishing activities left.
+ if (!taskFragmentInfo.hasRunningActivity()) {
+ if (taskFragmentInfo.isTaskFragmentClearedForPip()) {
+ // Do not finish the dependents if the last activity is reparented to PiP.
+ // Instead, the original split should be cleanup, and the dependent may be
+ // expanded to fullscreen.
+ cleanupForEnterPip(wct, container);
+ mPresenter.cleanupContainer(container, false /* shouldFinishDependent */, wct);
+ } else if (taskFragmentInfo.isTaskClearedForReuse()) {
+ // Do not finish the dependents if this TaskFragment was cleared due to
+ // launching activity in the Task.
+ mPresenter.cleanupContainer(container, false /* shouldFinishDependent */, wct);
+ } else if (!container.isWaitingActivityAppear()) {
+ // Do not finish the container before the expected activity appear until
+ // timeout.
+ mPresenter.cleanupContainer(container, true /* shouldFinishDependent */, wct);
+ }
+ } else if (wasInPip && isInPip) {
+ // No update until exit PIP.
+ return;
+ } else if (isInPip) {
+ // Enter PIP.
+ // All overrides will be cleanup.
+ container.setLastRequestedBounds(null /* bounds */);
+ container.setLastRequestedWindowingMode(WINDOWING_MODE_UNDEFINED);
cleanupForEnterPip(wct, container);
- mPresenter.cleanupContainer(container, false /* shouldFinishDependent */, wct);
- } else if (taskFragmentInfo.isTaskClearedForReuse()) {
- // Do not finish the dependents if this TaskFragment was cleared due to launching
- // activity in the Task.
- mPresenter.cleanupContainer(container, false /* shouldFinishDependent */, wct);
- } else if (!container.isWaitingActivityAppear()) {
- // Do not finish the container before the expected activity appear until timeout.
- mPresenter.cleanupContainer(container, true /* shouldFinishDependent */, wct);
+ } else if (wasInPip) {
+ // Exit PIP.
+ // Updates the presentation of the container. Expand or launch placeholder if
+ // needed.
+ updateContainer(wct, container);
}
- } else if (wasInPip && isInPip) {
- // No update until exit PIP.
- return;
- } else if (isInPip) {
- // Enter PIP.
- // All overrides will be cleanup.
- container.setLastRequestedBounds(null /* bounds */);
- container.setLastRequestedWindowingMode(WINDOWING_MODE_UNDEFINED);
- cleanupForEnterPip(wct, container);
- } else if (wasInPip) {
- // Exit PIP.
- // Updates the presentation of the container. Expand or launch placeholder if needed.
- updateContainer(wct, container);
+ mPresenter.applyTransaction(wct);
+ updateCallbackIfNecessary();
}
- mPresenter.applyTransaction(wct);
- updateCallbackIfNecessary();
}
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
- final TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
- if (container != null) {
- // Cleanup if the TaskFragment vanished is not requested by the organizer.
- removeContainer(container);
- // Make sure the top container is updated.
- final TaskFragmentContainer newTopContainer = getTopActiveContainer(
- container.getTaskId());
- if (newTopContainer != null) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- updateContainer(wct, newTopContainer);
- mPresenter.applyTransaction(wct);
+ synchronized (mLock) {
+ final TaskFragmentContainer container = getContainer(
+ taskFragmentInfo.getFragmentToken());
+ if (container != null) {
+ // Cleanup if the TaskFragment vanished is not requested by the organizer.
+ removeContainer(container);
+ // Make sure the top container is updated.
+ final TaskFragmentContainer newTopContainer = getTopActiveContainer(
+ container.getTaskId());
+ if (newTopContainer != null) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ updateContainer(wct, newTopContainer);
+ mPresenter.applyTransaction(wct);
+ }
+ updateCallbackIfNecessary();
}
- updateCallbackIfNecessary();
+ cleanupTaskFragment(taskFragmentInfo.getFragmentToken());
}
- cleanupTaskFragment(taskFragmentInfo.getFragmentToken());
}
@Override
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@NonNull Configuration parentConfig) {
- final TaskFragmentContainer container = getContainer(fragmentToken);
- if (container != null) {
- onTaskConfigurationChanged(container.getTaskId(), parentConfig);
- if (isInPictureInPicture(parentConfig)) {
- // No need to update presentation in PIP until the Task exit PIP.
- return;
+ synchronized (mLock) {
+ final TaskFragmentContainer container = getContainer(fragmentToken);
+ if (container != null) {
+ onTaskConfigurationChanged(container.getTaskId(), parentConfig);
+ if (isInPictureInPicture(parentConfig)) {
+ // No need to update presentation in PIP until the Task exit PIP.
+ return;
+ }
+ mPresenter.updateContainer(container);
+ updateCallbackIfNecessary();
}
- mPresenter.updateContainer(container);
- updateCallbackIfNecessary();
}
}
@Override
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {
- // If the activity belongs to the current app process, we treat it as a new activity launch.
- final Activity activity = getActivity(activityToken);
- if (activity != null) {
- // We don't allow split as primary for new launch because we currently only support
- // launching to top. We allow split as primary for activity reparent because the
- // activity may be split as primary before it is reparented out. In that case, we want
- // to show it as primary again when it is reparented back.
- if (!resolveActivityToContainer(activity, true /* isOnReparent */)) {
- // When there is no embedding rule matched, try to place it in the top container
- // like a normal launch.
- placeActivityInTopContainer(activity);
+ synchronized (mLock) {
+ // If the activity belongs to the current app process, we treat it as a new activity
+ // launch.
+ final Activity activity = getActivity(activityToken);
+ if (activity != null) {
+ // We don't allow split as primary for new launch because we currently only support
+ // launching to top. We allow split as primary for activity reparent because the
+ // activity may be split as primary before it is reparented out. In that case, we
+ // want to show it as primary again when it is reparented back.
+ if (!resolveActivityToContainer(activity, true /* isOnReparent */)) {
+ // When there is no embedding rule matched, try to place it in the top container
+ // like a normal launch.
+ placeActivityInTopContainer(activity);
+ }
+ updateCallbackIfNecessary();
+ return;
}
- updateCallbackIfNecessary();
- return;
- }
- final TaskContainer taskContainer = getTaskContainer(taskId);
- if (taskContainer == null || taskContainer.isInPictureInPicture()) {
- // We don't embed activity when it is in PIP.
- return;
- }
+ final TaskContainer taskContainer = getTaskContainer(taskId);
+ if (taskContainer == null || taskContainer.isInPictureInPicture()) {
+ // We don't embed activity when it is in PIP.
+ return;
+ }
- // If the activity belongs to a different app process, we treat it as starting new intent,
- // since both actions might result in a new activity that should appear in an organized
- // TaskFragment.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- TaskFragmentContainer targetContainer = resolveStartActivityIntent(wct, taskId,
- activityIntent, null /* launchingActivity */);
- if (targetContainer == null) {
- // When there is no embedding rule matched, try to place it in the top container like a
- // normal launch.
- targetContainer = taskContainer.getTopTaskFragmentContainer();
- }
- if (targetContainer == null) {
- return;
+ // If the activity belongs to a different app process, we treat it as starting new
+ // intent, since both actions might result in a new activity that should appear in an
+ // organized TaskFragment.
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ TaskFragmentContainer targetContainer = resolveStartActivityIntent(wct, taskId,
+ activityIntent, null /* launchingActivity */);
+ if (targetContainer == null) {
+ // When there is no embedding rule matched, try to place it in the top container
+ // like a normal launch.
+ targetContainer = taskContainer.getTopTaskFragmentContainer();
+ }
+ if (targetContainer == null) {
+ return;
+ }
+ wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(),
+ activityToken);
+ mPresenter.applyTransaction(wct);
+ // Because the activity does not belong to the organizer process, we wait until
+ // onTaskFragmentAppeared to trigger updateCallbackIfNecessary().
}
- wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(), activityToken);
- mPresenter.applyTransaction(wct);
- // Because the activity does not belong to the organizer process, we wait until
- // onTaskFragmentAppeared to trigger updateCallbackIfNecessary().
}
/** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */
@@ -481,6 +489,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/**
+ * Starts an activity to side of the launchingActivity with the provided split config.
+ */
+ private void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
+ @Nullable Bundle options, @NonNull SplitRule sideRule,
+ @Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) {
+ try {
+ mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule,
+ isPlaceholder);
+ } catch (Exception e) {
+ if (failureCallback != null) {
+ failureCallback.accept(e);
+ }
+ }
+ }
+
+ /**
* Expands the given activity by either expanding the TaskFragment it is currently in or putting
* it into a new expanded TaskFragment.
*/
@@ -1382,25 +1406,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) {
- final IBinder activityToken = activity.getActivityToken();
- final IBinder initialTaskFragmentToken = getInitialTaskFragmentToken(activity);
- // If the activity is not embedded, then it will not have an initial task fragment token
- // so no further action is needed.
- if (initialTaskFragmentToken == null) {
- return;
- }
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .mContainers;
- for (int j = containers.size() - 1; j >= 0; j--) {
- final TaskFragmentContainer container = containers.get(j);
- if (!container.hasActivity(activityToken)
- && container.getTaskFragmentToken().equals(initialTaskFragmentToken)) {
- // The onTaskFragmentInfoChanged callback containing this activity has not
- // reached the client yet, so add the activity to the pending appeared
- // activities.
- container.addPendingAppearedActivity(activity);
- return;
+ synchronized (mLock) {
+ final IBinder activityToken = activity.getActivityToken();
+ final IBinder initialTaskFragmentToken = getInitialTaskFragmentToken(activity);
+ // If the activity is not embedded, then it will not have an initial task fragment
+ // token so no further action is needed.
+ if (initialTaskFragmentToken == null) {
+ return;
+ }
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .mContainers;
+ for (int j = containers.size() - 1; j >= 0; j--) {
+ final TaskFragmentContainer container = containers.get(j);
+ if (!container.hasActivity(activityToken)
+ && container.getTaskFragmentToken()
+ .equals(initialTaskFragmentToken)) {
+ // The onTaskFragmentInfoChanged callback containing this activity has
+ // not reached the client yet, so add the activity to the pending
+ // appeared activities.
+ container.addPendingAppearedActivity(activity);
+ return;
+ }
}
}
}
@@ -1412,17 +1439,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// first. In case of a configured placeholder activity we want to make sure
// that we don't launch it if an activity itself already requested something to be
// launched to side.
- SplitController.this.onActivityCreated(activity);
+ synchronized (mLock) {
+ SplitController.this.onActivityCreated(activity);
+ }
}
@Override
public void onActivityConfigurationChanged(Activity activity) {
- SplitController.this.onActivityConfigurationChanged(activity);
+ synchronized (mLock) {
+ SplitController.this.onActivityConfigurationChanged(activity);
+ }
}
@Override
public void onActivityPostDestroyed(Activity activity) {
- SplitController.this.onActivityDestroyed(activity);
+ synchronized (mLock) {
+ SplitController.this.onActivityDestroyed(activity);
+ }
}
}
@@ -1457,16 +1490,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return super.onStartActivity(who, intent, options);
}
- final int taskId = getTaskId(launchingActivity);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- final TaskFragmentContainer launchedInTaskFragment = resolveStartActivityIntent(wct,
- taskId, intent, launchingActivity);
- if (launchedInTaskFragment != null) {
- mPresenter.applyTransaction(wct);
- // Amend the request to let the WM know that the activity should be placed in the
- // dedicated container.
- options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
- launchedInTaskFragment.getTaskFragmentToken());
+ synchronized (mLock) {
+ final int taskId = getTaskId(launchingActivity);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final TaskFragmentContainer launchedInTaskFragment = resolveStartActivityIntent(wct,
+ taskId, intent, launchingActivity);
+ if (launchedInTaskFragment != null) {
+ mPresenter.applyTransaction(wct);
+ // Amend the request to let the WM know that the activity should be placed in
+ // the dedicated container.
+ options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+ launchedInTaskFragment.getTaskFragmentToken());
+ }
}
return super.onStartActivity(who, intent, options);
@@ -1479,7 +1514,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@Override
public boolean isActivityEmbedded(@NonNull Activity activity) {
- return mPresenter.isActivityEmbedded(activity.getActivityToken());
+ synchronized (mLock) {
+ return mPresenter.isActivityEmbedded(activity.getActivityToken());
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index eb7aa1f7ff50..6f990081de9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -434,7 +434,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
}
- final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
+ final Rect destinationBounds = getExitDestinationBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
@@ -489,6 +489,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
});
}
+ /** Returns the bounds to restore to when exiting PIP mode. */
+ public Rect getExitDestinationBounds() {
+ return mPipBoundsState.getDisplayBounds();
+ }
+
private void exitLaunchIntoPipTask(WindowContainerTransaction wct) {
wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */);
mTaskOrganizer.applyTransaction(wct);
@@ -974,6 +979,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (!isInPip()) {
return;
}
+ if (mLeash == null || !mLeash.isValid()) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Invalid leash on setPipVisibility: %s", TAG, mLeash);
+ return;
+ }
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.alpha(tx, mLeash, visible ? 1f : 0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index f969caac3196..3cf8a45310ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1025,8 +1025,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
// Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
- true /* moveTogether */);
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct);
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7c99ce5a8c4d..64c080fd938a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1134,6 +1134,8 @@
<string name="battery_info_status_charging_slow">Charging slowly</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging wirelessly. -->
<string name="battery_info_status_charging_wireless">Charging wirelessly</string>
+ <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when the device is dock charging. -->
+ <string name="battery_info_status_charging_dock">Charging Dock</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_discharging">Not charging</string>
<!-- Battery Info screen. Value for a status item. A state which device is connected with any charger(e.g. USB, Adapter or Wireless) but not charging yet. Used for diagnostic info screens, precise translation isn't needed -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index feb4212035bc..b9c4030d9d0e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -239,6 +239,8 @@ public class Utils {
statusString = res.getString(R.string.battery_info_status_charging);
break;
}
+ } else if (batteryStatus.isPluggedInDock()) {
+ statusString = res.getString(R.string.battery_info_status_charging_dock);
} else {
statusString = res.getString(R.string.battery_info_status_charging_wireless);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 09b2a2e73c5b..336cdd3f259f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -365,6 +365,17 @@ public class UtilsTest {
}
@Test
+ public void getBatteryStatus_chargingDock_returnDockChargingString() {
+ final Intent intent = new Intent();
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_DOCK);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
+ resources.getString(R.string.battery_info_status_charging_dock));
+ }
+
+ @Test
public void getBatteryStatus_chargingWireless_returnWirelessChargingString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 92812cf64db7..bfd76c5e29f9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -48,7 +48,50 @@
<!-- The minimum display position of the arrow on the screen -->
<dimen name="navigation_edge_arrow_min_y">64dp</dimen>
<!-- The amount by which the arrow is shifted to avoid the finger-->
- <dimen name="navigation_edge_finger_offset">48dp</dimen>
+ <dimen name="navigation_edge_finger_offset">64dp</dimen>
+
+ <!-- The thickness of the arrow -->
+ <dimen name="navigation_edge_arrow_thickness">4dp</dimen>
+ <!-- The minimum delta needed to change direction / stop triggering back -->
+ <dimen name="navigation_edge_minimum_x_delta_for_switch">32dp</dimen>
+
+ <!-- entry state -->
+ <dimen name="navigation_edge_entry_margin">4dp</dimen>
+ <dimen name="navigation_edge_entry_background_width">8dp</dimen>
+ <dimen name="navigation_edge_entry_background_height">60dp</dimen>
+ <dimen name="navigation_edge_entry_edge_corners">6dp</dimen>
+ <dimen name="navigation_edge_entry_far_corners">6dp</dimen>
+ <dimen name="navigation_edge_entry_arrow_length">10dp</dimen>
+ <dimen name="navigation_edge_entry_arrow_height">7dp</dimen>
+
+ <!-- pre-threshold -->
+ <dimen name="navigation_edge_pre_threshold_margin">4dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_background_width">64dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_background_height">60dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_edge_corners">22dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_far_corners">26dp</dimen>
+
+ <!-- post-threshold / active -->
+ <dimen name="navigation_edge_active_margin">14dp</dimen>
+ <dimen name="navigation_edge_active_background_width">60dp</dimen>
+ <dimen name="navigation_edge_active_background_height">60dp</dimen>
+ <dimen name="navigation_edge_active_edge_corners">30dp</dimen>
+ <dimen name="navigation_edge_active_far_corners">30dp</dimen>
+ <dimen name="navigation_edge_active_arrow_length">8dp</dimen>
+ <dimen name="navigation_edge_active_arrow_height">9dp</dimen>
+
+ <!-- stretch @412 dp -->
+ <dimen name="navigation_edge_stretch_threshold">412dp</dimen>
+ <dimen name="navigation_edge_stretch_margin">18dp</dimen>
+ <dimen name="navigation_edge_stretch_background_width">74dp</dimen>
+ <dimen name="navigation_edge_stretch_background_height">60dp</dimen>
+ <dimen name="navigation_edge_stretch_left_corners">30dp</dimen>
+ <dimen name="navigation_edge_stretch_right_corners">30dp</dimen>
+ <dimen name="navigation_edge_stretched_arrow_length">7dp</dimen>
+ <dimen name="navigation_edge_stretched_arrow_height">10dp</dimen>
+
+ <dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen>
+ <dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
new file mode 100644
index 000000000000..56ad19ae89ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -0,0 +1,340 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.RectF
+import android.view.View
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.util.LatencyTracker
+import com.android.settingslib.Utils
+import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
+
+private const val TAG = "BackPanel"
+private const val DEBUG = false
+
+class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) {
+
+ var arrowsPointLeft = false
+ set(value) {
+ if (field != value) {
+ invalidate()
+ field = value
+ }
+ }
+
+ // Arrow color and shape
+ private val arrowPath = Path()
+ private val arrowPaint = Paint()
+
+ // Arrow background color and shape
+ private var arrowBackgroundRect = RectF()
+ private var arrowBackgroundPaint = Paint()
+
+ // True if the panel is currently on the left of the screen
+ var isLeftPanel = false
+
+ /**
+ * Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw]
+ */
+ private var trackingBackArrowLatency = false
+
+ /**
+ * The length of the arrow measured horizontally. Used for animating [arrowPath]
+ */
+ private var arrowLength = AnimatedFloat("arrowLength", SpringForce())
+
+ /**
+ * The height of the arrow measured vertically from its center to its top (i.e. half the total
+ * height). Used for animating [arrowPath]
+ */
+ private var arrowHeight = AnimatedFloat("arrowHeight", SpringForce())
+
+ private val backgroundWidth = AnimatedFloat(
+ name = "backgroundWidth",
+ SpringForce().apply {
+ stiffness = 600f
+ dampingRatio = 0.65f
+ })
+
+ private val backgroundHeight = AnimatedFloat(
+ name = "backgroundHeight",
+ SpringForce().apply {
+ stiffness = 600f
+ dampingRatio = 0.65f
+ })
+
+ /**
+ * Corners of the background closer to the edge of the screen (where the arrow appeared from).
+ * Used for animating [arrowBackgroundRect]
+ */
+ private val backgroundEdgeCornerRadius = AnimatedFloat(
+ name = "backgroundEdgeCornerRadius",
+ SpringForce().apply {
+ stiffness = 400f
+ dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
+ })
+
+ /**
+ * Corners of the background further from the edge of the screens (toward the direction the
+ * arrow is being dragged). Used for animating [arrowBackgroundRect]
+ */
+ private val backgroundDragCornerRadius = AnimatedFloat(
+ name = "backgroundDragCornerRadius",
+ SpringForce().apply {
+ stiffness = 2200f
+ dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ })
+
+ /**
+ * Left/right position of the background relative to the canvas. Also corresponds with the
+ * background's margin relative to the screen edge. The arrow will be centered within the
+ * background.
+ */
+ private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce())
+
+ /**
+ * Canvas vertical translation. How far up/down the arrow and background appear relative to the
+ * canvas.
+ */
+ private var verticalTranslation: AnimatedFloat =
+ AnimatedFloat("verticalTranslation", SpringForce().apply {
+ stiffness = SpringForce.STIFFNESS_MEDIUM
+ })
+
+ /**
+ * Use for drawing debug info. Can only be set if [DEBUG]=true
+ */
+ var drawDebugInfo: ((canvas: Canvas) -> Unit)? = null
+ set(value) {
+ if (DEBUG) field = value
+ }
+
+ internal fun updateArrowPaint(arrowThickness: Float) {
+ // Arrow constants
+ arrowPaint.strokeWidth = arrowThickness
+
+ arrowPaint.color =
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ arrowBackgroundPaint.color = Utils.getColorAccentDefaultColor(context)
+ }
+
+ private inner class AnimatedFloat(name: String, springForce: SpringForce) {
+ // The resting position when not stretched by a touch drag
+ private var restingPosition = 0f
+
+ // The current position as updated by the SpringAnimation
+ var pos = 0f
+ set(v) {
+ if (field != v) {
+ field = v
+ invalidate()
+ }
+ }
+
+ val animation: SpringAnimation
+
+ init {
+ val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) {
+ override fun setValue(animatedFloat: AnimatedFloat, value: Float) {
+ animatedFloat.pos = value
+ }
+
+ override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
+ }
+ animation = SpringAnimation(this, floatProp)
+ animation.spring = springForce
+ }
+
+ fun snapTo(newPosition: Float) {
+ animation.cancel()
+ restingPosition = newPosition
+ animation.spring.finalPosition = newPosition
+ pos = newPosition
+ }
+
+ fun stretchTo(stretchAmount: Float) {
+ animation.animateToFinalPosition(restingPosition + stretchAmount)
+ }
+
+ fun updateRestingPosition(pos: Float, animated: Boolean) {
+ restingPosition = pos
+ if (animated)
+ animation.animateToFinalPosition(restingPosition)
+ else
+ snapTo(restingPosition)
+ }
+ }
+
+ init {
+ visibility = GONE
+ arrowPaint.apply {
+ style = Paint.Style.STROKE
+ strokeCap = Paint.Cap.SQUARE
+ }
+ arrowBackgroundPaint.apply {
+ style = Paint.Style.FILL
+ strokeJoin = Paint.Join.ROUND
+ strokeCap = Paint.Cap.ROUND
+ }
+ }
+
+ private fun calculateArrowPath(dx: Float, dy: Float): Path {
+ arrowPath.reset()
+ arrowPath.moveTo(dx, -dy)
+ arrowPath.lineTo(0f, 0f)
+ arrowPath.lineTo(dx, dy)
+ arrowPath.moveTo(dx, -dy)
+ return arrowPath
+ }
+
+ fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean {
+ return if (horizontalTranslation.animation.isRunning) {
+ horizontalTranslation.animation.addEndListener(endListener)
+ true
+ } else {
+ endListener.runNow()
+ false
+ }
+ }
+
+ fun setStretch(
+ arrowLengthStretch: Float,
+ arrowHeightStretch: Float,
+ backgroundWidthStretch: Float,
+ backgroundHeightStretch: Float,
+ backgroundEdgeCornerRadiusStretch: Float,
+ backgroundDragCornerRadiusStretch: Float,
+ horizontalTranslationStretch: Float
+ ) {
+ arrowLength.stretchTo(arrowLengthStretch)
+ arrowHeight.stretchTo(arrowHeightStretch)
+ backgroundWidth.stretchTo(backgroundWidthStretch)
+ backgroundHeight.stretchTo(backgroundHeightStretch)
+ backgroundEdgeCornerRadius.stretchTo(backgroundEdgeCornerRadiusStretch)
+ backgroundDragCornerRadius.stretchTo(backgroundDragCornerRadiusStretch)
+ horizontalTranslation.stretchTo(horizontalTranslationStretch)
+ }
+
+ fun resetStretch() {
+ setStretch(0f, 0f, 0f, 0f, 0f, 0f, 0f)
+ }
+
+ /**
+ * Updates resting arrow and background size not accounting for stretch
+ */
+ internal fun updateRestingArrowDimens(
+ backgroundWidth: Float,
+ backgroundHeight: Float,
+ backgroundEdgeCornerRadius: Float,
+ backgroundDragCornerRadius: Float,
+ arrowLength: Float,
+ arrowHeight: Float,
+ horizontalTranslation: Float,
+ animate: Boolean
+ ) {
+ this.arrowLength.updateRestingPosition(arrowLength, animate)
+ this.arrowHeight.updateRestingPosition(arrowHeight, animate)
+ this.backgroundWidth.updateRestingPosition(backgroundWidth, animate)
+ this.backgroundHeight.updateRestingPosition(backgroundHeight, animate)
+ this.backgroundEdgeCornerRadius.updateRestingPosition(backgroundEdgeCornerRadius, animate)
+ this.backgroundDragCornerRadius.updateRestingPosition(backgroundDragCornerRadius, animate)
+ this.horizontalTranslation.updateRestingPosition(horizontalTranslation, animate)
+ }
+
+ fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
+
+ fun setArrowStiffness(arrowStiffness: Float, arrowDampingRatio: Float) {
+ arrowLength.animation.spring.apply {
+ stiffness = arrowStiffness
+ dampingRatio = arrowDampingRatio
+ }
+ arrowHeight.animation.spring.apply {
+ stiffness = arrowStiffness
+ dampingRatio = arrowDampingRatio
+ }
+ }
+
+ override fun hasOverlappingRendering() = false
+
+ override fun onDraw(canvas: Canvas) {
+ var edgeCorner = backgroundEdgeCornerRadius.pos
+ val farCorner = backgroundDragCornerRadius.pos
+ val halfHeight = backgroundHeight.pos / 2
+
+ canvas.save()
+
+ if (!isLeftPanel) canvas.scale(-1f, 1f, width / 2.0f, 0f)
+
+ canvas.translate(
+ horizontalTranslation.pos,
+ height * 0.5f + verticalTranslation.pos
+ )
+
+ val arrowBackground = arrowBackgroundRect.apply {
+ left = 0f
+ top = -halfHeight
+ right = backgroundWidth.pos
+ bottom = halfHeight
+ }.toPathWithRoundCorners(
+ topLeft = edgeCorner,
+ bottomLeft = edgeCorner,
+ topRight = farCorner,
+ bottomRight = farCorner
+ )
+ canvas.drawPath(arrowBackground, arrowBackgroundPaint)
+
+ val dx = arrowLength.pos
+ val dy = arrowHeight.pos
+
+ // How far the arrow bounding box should be from the edge of the screen. Measured from
+ // either the tip or the back of the arrow, whichever is closer
+ var arrowOffset = (backgroundWidth.pos - dx) / 2
+ canvas.translate(
+ /* dx= */ arrowOffset,
+ /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
+ )
+
+ val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel)
+ if (arrowPointsAwayFromEdge) {
+ canvas.apply {
+ scale(-1f, 1f, 0f, 0f)
+ translate(-dx, 0f)
+ }
+ }
+
+ val arrowPath = calculateArrowPath(dx = dx, dy = dy)
+ canvas.drawPath(arrowPath, arrowPaint)
+ canvas.restore()
+
+ if (trackingBackArrowLatency) {
+ latencyTracker.onActionEnd(LatencyTracker.ACTION_SHOW_BACK_ARROW)
+ trackingBackArrowLatency = false
+ }
+
+ if (DEBUG) drawDebugInfo?.invoke(canvas)
+ }
+
+ fun startTrackingShowBackArrowLatency() {
+ latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_BACK_ARROW)
+ trackingBackArrowLatency = true
+ }
+
+ private fun RectF.toPathWithRoundCorners(
+ topLeft: Float = 0f,
+ topRight: Float = 0f,
+ bottomRight: Float = 0f,
+ bottomLeft: Float = 0f
+ ): Path = Path().apply {
+ val corners = floatArrayOf(
+ topLeft, topLeft,
+ topRight, topRight,
+ bottomRight, bottomRight,
+ bottomLeft, bottomLeft
+ )
+ addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
new file mode 100644
index 000000000000..100411b1cb93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2022 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.navigationbar.gestural
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Point
+import android.os.Handler
+import android.os.SystemClock
+import android.os.VibrationEffect
+import android.util.Log
+import android.util.MathUtils.constrain
+import android.util.MathUtils.saturate
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.WindowManager
+import android.view.animation.AccelerateInterpolator
+import android.view.animation.PathInterpolator
+import android.window.BackEvent
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.util.LatencyTracker
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.NavigationEdgeBackPlugin
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.ViewController
+import com.android.wm.shell.back.BackAnimation
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.sign
+
+private const val TAG = "BackPanelController"
+private const val DEBUG = false
+
+private const val ENABLE_FAILSAFE = true
+
+private const val FAILSAFE_DELAY_MS: Long = 350
+
+/**
+ * The time required between the arrow-appears vibration effect and the back-committed vibration
+ * effect. If the arrow is flung quickly, the phone only vibrates once. However, if the arrow is
+ * held on the screen for a long time, it will vibrate a second time when the back gesture is
+ * committed.
+ */
+private const val GESTURE_DURATION_FOR_CLICK_MS = 400
+
+/**
+ * The min duration arrow remains on screen during a fling event.
+ */
+private const val FLING_PAUSE_DURATION_MS = 50L
+
+/**
+ * The min duration arrow remains on screen during a fling event.
+ */
+private const val MIN_FLING_VELOCITY = 3000
+
+/**
+ * The amount of rubber banding we do for the vertical translation
+ */
+private const val RUBBER_BAND_AMOUNT = 15
+
+private const val ARROW_APPEAR_STIFFNESS = 600f
+private const val ARROW_APPEAR_DAMPING_RATIO = 0.4f
+private const val ARROW_DISAPPEAR_STIFFNESS = 1200f
+private const val ARROW_DISAPPEAR_DAMPING_RATIO = SpringForce.DAMPING_RATIO_NO_BOUNCY
+
+/**
+ * The interpolator used to rubber band
+ */
+private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f)
+
+private val ACCELERATE_INTERPOLATOR = AccelerateInterpolator(0.7f)
+
+class BackPanelController private constructor(
+ context: Context,
+ private var backAnimation: BackAnimation?,
+ private val windowManager: WindowManager,
+ @Main private val mainHandler: Handler,
+ private val vibratorHelper: VibratorHelper,
+ private val configurationController: ConfigurationController,
+ latencyTracker: LatencyTracker
+) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
+
+ /**
+ * Injectable instance to create a new BackPanelController.
+ *
+ * Necessary because EdgeBackGestureHandler sometimes needs to create new instances of
+ * BackPanelController, and we need to match EdgeBackGestureHandler's context.
+ */
+ class Factory @Inject constructor(
+ private val windowManager: WindowManager,
+ @Main private val mainHandler: Handler,
+ private val vibratorHelper: VibratorHelper,
+ private val configurationController: ConfigurationController,
+ private val latencyTracker: LatencyTracker
+ ) {
+ /** Construct a [BackPanelController]. */
+ fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+ val backPanelController = BackPanelController(
+ context,
+ backAnimation,
+ windowManager,
+ mainHandler,
+ vibratorHelper,
+ configurationController,
+ latencyTracker
+ )
+ backPanelController.init()
+ return backPanelController
+ }
+ }
+
+ private var params: EdgePanelParams = EdgePanelParams(resources)
+ private var currentState: GestureState = GestureState.GONE
+ private var previousState: GestureState = GestureState.GONE
+
+ // Phone should only vibrate the first time the arrow is activated
+ private var hasHapticPlayed = false
+
+ // Screen attributes
+ private lateinit var layoutParams: WindowManager.LayoutParams
+ private val displaySize = Point()
+
+ private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
+
+ private var previousXTranslation = 0f
+ private var totalTouchDelta = 0f
+ private var velocityTracker: VelocityTracker? = null
+ set(value) {
+ if (field != value) field?.recycle()
+ field = value
+ }
+ get() {
+ if (field == null) field = VelocityTracker.obtain()
+ return field
+ }
+
+ // The x,y position of the first touch event
+ private var startX = 0f
+ private var startY = 0f
+
+ private val failsafeRunnable = Runnable { onFailsafe() }
+
+ private enum class GestureState {
+ /* Arrow is off the screen and invisible */
+ GONE,
+
+ /* Arrow is animating in */
+ ENTRY,
+
+ /* could be entry, neutral, or stretched, releasing will commit back */
+ ACTIVE,
+
+ /* releasing will cancel back */
+ INACTIVE,
+
+ /* like committed, but animation takes longer */
+ FLUNG,
+
+ /* back action currently occurring, arrow soon to be GONE */
+ COMMITTED,
+
+ /* back action currently cancelling, arrow soon to be GONE */
+ CANCELLED
+ }
+
+ /**
+ * Wrapper around OnAnimationEndListener which runs the given runnable after a delay. The
+ * runnable is not called if the animation is cancelled
+ */
+ class DelayedOnAnimationEndListener(
+ private val handler: Handler,
+ private val runnable: Runnable,
+ private val delay: Long
+ ) : DynamicAnimation.OnAnimationEndListener {
+
+ override fun onAnimationEnd(
+ animation: DynamicAnimation<*>,
+ canceled: Boolean,
+ value: Float,
+ velocity: Float
+ ) {
+ animation.removeEndListener(this)
+ if (!canceled) {
+ handler.postDelayed(runnable, delay)
+ }
+ }
+
+ fun runNow() {
+ runnable.run()
+ }
+ }
+
+ private val setCommittedEndListener =
+ DelayedOnAnimationEndListener(
+ mainHandler,
+ { updateArrowState(GestureState.COMMITTED) },
+ delay = FLING_PAUSE_DURATION_MS
+ )
+
+ private val setGoneEndListener =
+ DelayedOnAnimationEndListener(
+ mainHandler,
+ {
+ cancelFailsafe()
+ updateArrowState(GestureState.GONE)
+ },
+ delay = 0
+ )
+
+ // Vibration
+ private var vibrationTime: Long = 0
+
+ // Minimum size of the screen's width or height
+ private var screenSize = 0
+
+ /**
+ * Used for initialization and configuration changes
+ */
+ private fun updateConfiguration() {
+ params.update(resources)
+ initializeBackAnimation()
+ mView.updateArrowPaint(params.arrowThickness)
+ }
+
+ private val configurationListener = object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateConfiguration()
+ }
+
+ override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
+ updateArrowDirection(isLayoutRtl)
+ }
+ }
+
+ override fun onViewAttached() {
+ updateConfiguration()
+ updateArrowDirection(configurationController.isLayoutRtl)
+ updateArrowState(GestureState.GONE, force = true)
+ updateRestingArrowDimens(animated = false, currentState)
+ configurationController.addCallback(configurationListener)
+ }
+
+ /** Update the arrow direction. The arrow should point the same way for both panels. */
+ private fun updateArrowDirection(isLayoutRtl: Boolean) {
+ mView.arrowsPointLeft = isLayoutRtl
+ }
+
+ override fun onViewDetached() {
+ configurationController.removeCallback(configurationListener)
+ }
+
+ override fun onMotionEvent(event: MotionEvent) {
+ backAnimation?.onBackMotion(
+ event,
+ event.actionMasked,
+ if (mView.isLeftPanel) BackEvent.EDGE_LEFT else BackEvent.EDGE_RIGHT
+ )
+
+ velocityTracker!!.addMovement(event)
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ resetOnDown()
+ startX = event.x
+ startY = event.y
+
+ // Reset the arrow to the side
+ updateArrowState(GestureState.ENTRY)
+
+ windowManager.updateViewLayout(mView, layoutParams)
+ mView.startTrackingShowBackArrowLatency()
+ }
+ MotionEvent.ACTION_MOVE -> handleMoveEvent(event)
+ MotionEvent.ACTION_UP -> {
+ if (currentState == GestureState.ACTIVE) {
+ updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED)
+ } else {
+ updateArrowState(GestureState.CANCELLED)
+ }
+ velocityTracker = null
+ }
+ MotionEvent.ACTION_CANCEL -> {
+ updateArrowState(GestureState.CANCELLED)
+ velocityTracker = null
+ }
+ }
+ }
+
+ private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
+ when (currentState) {
+ GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, GestureState.CANCELLED ->
+ return
+ }
+
+ updateArrowState(
+ when {
+ // Check if we should transition from ENTRY to ACTIVE
+ currentState == GestureState.ENTRY && xTranslation > params.swipeTriggerThreshold ->
+ GestureState.ACTIVE
+
+ // Abort if we had continuous motion toward the edge for a while, OR the direction
+ // in Y is bigger than X * 2
+ currentState == GestureState.ACTIVE &&
+ ((totalTouchDelta < 0 && -totalTouchDelta > params.minDeltaForSwitch) ||
+ (yTranslation > xTranslation * 2)) ->
+ GestureState.INACTIVE
+
+ // Re-activate if we had continuous motion away from the edge for a while
+ currentState == GestureState.INACTIVE &&
+ (totalTouchDelta > 0 && totalTouchDelta > params.minDeltaForSwitch) ->
+ GestureState.ACTIVE
+
+ // By default assume the current direction is kept
+ else -> currentState
+ }
+ )
+ }
+
+ private fun handleMoveEvent(event: MotionEvent) {
+ when (currentState) {
+ GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED,
+ GestureState.CANCELLED -> return
+ }
+
+ val x = event.x
+ val y = event.y
+
+ val yOffset = y - startY
+
+ // How far in the y direction we are from the original touch
+ val yTranslation = abs(yOffset)
+
+ // How far in the x direction we are from the original touch ignoring motion that
+ // occurs between the screen edge and the touch start.
+ val xTranslation = max(0f, if (mView.isLeftPanel) x - startX else startX - x)
+
+ // Compared to last time, how far we moved in the x direction. If <0, we are moving closer
+ // to the edge. If >0, we are moving further from the edge
+ val xDelta = xTranslation - previousXTranslation
+ previousXTranslation = xTranslation
+
+ if (abs(xDelta) > 0) {
+ if (sign(xDelta) == sign(totalTouchDelta)) {
+ // Direction has NOT changed, so keep counting the delta
+ totalTouchDelta += xDelta
+ } else {
+ // Direction has changed, so reset the delta
+ totalTouchDelta = xDelta
+ }
+ }
+
+ updateArrowStateOnMove(yTranslation, xTranslation)
+ when (currentState) {
+ GestureState.ACTIVE -> setActiveStretch(fullScreenStretchProgress(xTranslation))
+ GestureState.ENTRY ->
+ setEntryStretch(preThresholdStretchProgress(xTranslation))
+ GestureState.INACTIVE -> mView.resetStretch()
+ }
+
+ // set y translation
+ setVerticalTranslation(yOffset)
+ }
+
+ fun setVerticalTranslation(yOffset: Float) {
+ val yTranslation = abs(yOffset)
+ val maxYOffset = (mView.height / 2) - (params.entryBackgroundHeight / 2)
+ val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT))
+ mView.animateVertically(
+ RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset * sign(
+ yOffset
+ )
+ )
+ }
+
+ /**
+ * @return the relative position of the drag from the time after the arrow is activated until
+ * the arrow is fully stretched (between 0.0 - 1.0f)
+ */
+ fun fullScreenStretchProgress(xTranslation: Float): Float {
+ return saturate(
+ (xTranslation - params.swipeTriggerThreshold) /
+ (min(
+ params.fullyStretchedThreshold,
+ screenSize.toFloat()
+ ) - params.swipeTriggerThreshold)
+ )
+ }
+
+ /**
+ * Tracks the relative position of the drag from the entry until the threshold where the arrow
+ * activates (between 0.0 - 1.0f)
+ */
+ fun preThresholdStretchProgress(xTranslation: Float): Float {
+ return saturate(xTranslation / params.swipeTriggerThreshold)
+ }
+
+ fun setActiveStretch(progress: Float) {
+ val stretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+ mView.setStretch(
+ arrowLengthStretch = stretch * (params.stretchedArrowLength - params.activeArrowLength),
+ arrowHeightStretch = stretch * (params.stretchedArrowHeight - params.activeArrowHeight),
+ backgroundWidthStretch =
+ stretch * (params.stretchBackgroundWidth - params.activeBackgroundWidth),
+ backgroundHeightStretch =
+ stretch * (params.stretchBackgroundHeight - params.activeBackgroundHeight),
+ backgroundEdgeCornerRadiusStretch =
+ stretch * (params.stretchEdgeCorners - params.activeEdgeCorners),
+ backgroundDragCornerRadiusStretch =
+ stretch * (params.stretchFarCorners - params.activeFarCorners),
+ horizontalTranslationStretch = stretch * (params.stretchMargin - params.activeMargin)
+ )
+ }
+
+ fun setEntryStretch(progress: Float) {
+ val bgStretch = ACCELERATE_INTERPOLATOR.getInterpolation(progress)
+ val arrowStretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+ mView.setStretch(
+ arrowLengthStretch =
+ arrowStretch * (params.activeArrowLength - params.entryArrowLength),
+ arrowHeightStretch =
+ arrowStretch * (params.activeArrowHeight - params.entryArrowHeight),
+ backgroundWidthStretch =
+ bgStretch * (params.preThresholdBackgroundWidth - params.entryBackgroundWidth),
+ backgroundHeightStretch =
+ bgStretch * (params.preThresholdBackgroundHeight - params.entryBackgroundHeight),
+ backgroundEdgeCornerRadiusStretch =
+ bgStretch * (params.preThresholdEdgeCorners - params.entryEdgeCorners),
+ backgroundDragCornerRadiusStretch =
+ bgStretch * (params.preThresholdFarCorners - params.entryFarCorners),
+ horizontalTranslationStretch =
+ bgStretch * (params.preThresholdMargin - params.entryMargin)
+ )
+ }
+
+ fun setBackAnimation(backAnimation: BackAnimation?) {
+ this.backAnimation = backAnimation
+ initializeBackAnimation()
+ }
+
+ private fun initializeBackAnimation() {
+ backAnimation?.setSwipeThresholds(
+ params.swipeTriggerThreshold,
+ params.swipeProgressThreshold
+ )
+ }
+
+ override fun onDestroy() {
+ cancelFailsafe()
+ windowManager.removeView(mView)
+ }
+
+ override fun setIsLeftPanel(isLeftPanel: Boolean) {
+ mView.isLeftPanel = isLeftPanel
+ layoutParams.gravity = if (isLeftPanel) {
+ Gravity.LEFT or Gravity.TOP
+ } else {
+ Gravity.RIGHT or Gravity.TOP
+ }
+ }
+
+ override fun setInsets(insetLeft: Int, insetRight: Int) {
+ }
+
+ override fun setBackCallback(callback: NavigationEdgeBackPlugin.BackCallback) {
+ backCallback = callback
+ }
+
+ override fun setLayoutParams(layoutParams: WindowManager.LayoutParams) {
+ this.layoutParams = layoutParams
+ windowManager.addView(mView, layoutParams)
+ }
+
+ private fun isFlung() = velocityTracker!!.run {
+ computeCurrentVelocity(1000)
+ abs(xVelocity) > MIN_FLING_VELOCITY
+ }
+
+ private fun playFlingBackAnimation() {
+ playAnimation(setCommittedEndListener)
+ }
+
+ private fun playCommitBackAnimation() {
+ // Check if we should vibrate again
+ if (previousState != GestureState.FLUNG) {
+ backCallback.triggerBack()
+ velocityTracker!!.computeCurrentVelocity(1000)
+ val isSlow = abs(velocityTracker!!.xVelocity) < 500
+ val hasNotVibratedRecently =
+ SystemClock.uptimeMillis() - vibrationTime >= GESTURE_DURATION_FOR_CLICK_MS
+ if (isSlow || hasNotVibratedRecently) {
+ vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
+ }
+ }
+ playAnimation(setGoneEndListener)
+ }
+
+ private fun playCancelBackAnimation() {
+ backCallback.cancelBack()
+ playAnimation(setGoneEndListener)
+ }
+
+ /**
+ * @return true if the animation is running, false otherwise. Some transitions don't animate
+ */
+ private fun playAnimation(endListener: DelayedOnAnimationEndListener) {
+ updateRestingArrowDimens(animated = true, currentState)
+
+ if (!mView.addEndListener(endListener)) {
+ scheduleFailsafe()
+ }
+ }
+
+ private fun resetOnDown() {
+ hasHapticPlayed = false
+ totalTouchDelta = 0f
+ vibrationTime = 0
+ cancelFailsafe()
+ backAnimation?.setTriggerBack(false)
+ }
+
+ private fun updateYPosition(touchY: Float) {
+ var yPosition = touchY - params.fingerOffset
+ yPosition = Math.max(yPosition, params.minArrowYPosition.toFloat())
+ yPosition -= layoutParams.height / 2.0f
+ layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y)
+ }
+
+ override fun setDisplaySize(displaySize: Point) {
+ this.displaySize.set(displaySize.x, displaySize.y)
+ screenSize = Math.min(displaySize.x, displaySize.y)
+ }
+
+ /**
+ * Updates resting arrow and background size not accounting for stretch
+ */
+ private fun updateRestingArrowDimens(animated: Boolean, currentState: GestureState) {
+ mView.updateRestingArrowDimens(
+ backgroundWidth =
+ when (currentState) {
+ GestureState.GONE, GestureState.ENTRY -> params.entryBackgroundWidth
+ else -> params.activeBackgroundWidth
+ },
+ backgroundHeight =
+ when (currentState) {
+ GestureState.GONE, GestureState.ENTRY -> params.entryBackgroundHeight
+ else -> params.activeBackgroundHeight
+ },
+ backgroundEdgeCornerRadius =
+ when (currentState) {
+ GestureState.GONE, GestureState.ENTRY, GestureState.INACTIVE ->
+ params.entryEdgeCorners
+ else ->
+ params.activeEdgeCorners
+ },
+ backgroundDragCornerRadius =
+ when (currentState) {
+ GestureState.GONE, GestureState.ENTRY -> params.entryFarCorners
+ else -> params.activeFarCorners
+ },
+ arrowLength =
+ when (currentState) {
+ GestureState.ACTIVE, GestureState.INACTIVE, GestureState.COMMITTED,
+ GestureState.FLUNG -> params.activeArrowLength
+ GestureState.CANCELLED -> params.cancelledArrowLength
+ GestureState.GONE, GestureState.ENTRY -> params.entryArrowLength
+ },
+ arrowHeight =
+ when (currentState) {
+ GestureState.ACTIVE, GestureState.INACTIVE, GestureState.COMMITTED,
+ GestureState.FLUNG -> params.activeArrowHeight
+ GestureState.CANCELLED -> params.cancelledArrowHeight
+ GestureState.GONE, GestureState.ENTRY -> params.entryArrowHeight
+ },
+ horizontalTranslation =
+ when (currentState) {
+ GestureState.GONE -> -params.activeBackgroundWidth
+ // Position the cancelled/committed arrow slightly further off the screen so we
+ // do not see part of it bouncing
+ GestureState.CANCELLED, GestureState.COMMITTED ->
+ -params.activeBackgroundWidth * 1.5f
+ GestureState.FLUNG -> params.stretchMargin
+ GestureState.ACTIVE -> params.activeMargin
+ GestureState.ENTRY, GestureState.INACTIVE -> params.entryMargin
+ },
+ animate = animated
+ )
+ if (animated) {
+ when (currentState) {
+ GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG ->
+ mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO)
+ else ->
+ mView.setArrowStiffness(
+ ARROW_DISAPPEAR_STIFFNESS, ARROW_DISAPPEAR_DAMPING_RATIO)
+ }
+ }
+ }
+
+ /**
+ * Update arrow state. If state has not changed, this is a no-op.
+ *
+ * Transitioning to active/inactive will indicate whether or not releasing touch will trigger
+ * the back action.
+ */
+ private fun updateArrowState(newState: GestureState, force: Boolean = false) {
+ if (!force && currentState == newState) return
+
+ if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState")
+ previousState = currentState
+ currentState = newState
+ mView.visibility = if (currentState == GestureState.GONE) View.GONE else View.VISIBLE
+
+ when (currentState) {
+ // Transitioning to GONE never animates since the arrow is (presumably) already off the
+ // screen
+ GestureState.GONE -> updateRestingArrowDimens(animated = false, currentState)
+ GestureState.ENTRY -> {
+ updateYPosition(startY)
+ updateRestingArrowDimens(animated = true, currentState)
+ }
+ GestureState.ACTIVE -> {
+ backAnimation?.setTriggerBack(true)
+ updateRestingArrowDimens(animated = true, currentState)
+ // Vibrate the first time we transition to ACTIVE
+ if (!hasHapticPlayed) {
+ hasHapticPlayed = true
+ vibrationTime = SystemClock.uptimeMillis()
+ vibratorHelper.vibrate(VibrationEffect.EFFECT_TICK)
+ }
+ }
+ GestureState.INACTIVE -> {
+ backAnimation?.setTriggerBack(false)
+ updateRestingArrowDimens(animated = true, currentState)
+ }
+ GestureState.FLUNG -> playFlingBackAnimation()
+ GestureState.COMMITTED -> playCommitBackAnimation()
+ GestureState.CANCELLED -> playCancelBackAnimation()
+ }
+ }
+
+ private fun scheduleFailsafe() {
+ if (!ENABLE_FAILSAFE) return
+ cancelFailsafe()
+ if (DEBUG) Log.d(TAG, "scheduleFailsafe")
+ mainHandler.postDelayed(failsafeRunnable, FAILSAFE_DELAY_MS)
+ }
+
+ private fun cancelFailsafe() {
+ if (DEBUG) Log.d(TAG, "cancelFailsafe")
+ mainHandler.removeCallbacks(failsafeRunnable)
+ }
+
+ private fun onFailsafe() {
+ if (DEBUG) Log.d(TAG, "onFailsafe")
+ updateArrowState(GestureState.GONE, force = true)
+ }
+
+ override fun dump(pw: PrintWriter) {
+ pw.println("$TAG:")
+ pw.println(" currentState=$currentState")
+ pw.println(" isLeftPanel=$mView.isLeftPanel")
+ }
+
+ init {
+ if (DEBUG) mView.drawDebugInfo = { canvas ->
+ val debugStrings = listOf(
+ "$currentState",
+ "startX=$startX",
+ "startY=$startY",
+ "xDelta=${"%.1f".format(totalTouchDelta)}",
+ "xTranslation=${"%.1f".format(previousXTranslation)}",
+ "pre=${"%.0f".format(preThresholdStretchProgress(previousXTranslation) * 100)}%",
+ "post=${"%.0f".format(fullScreenStretchProgress(previousXTranslation) * 100)}%"
+ )
+ val debugPaint = Paint().apply {
+ color = Color.WHITE
+ }
+ val debugInfoBottom = debugStrings.size * 32f + 4f
+ canvas.drawRect(
+ 4f,
+ 4f,
+ canvas.width.toFloat(),
+ debugStrings.size * 32f + 4f,
+ debugPaint
+ )
+ debugPaint.apply {
+ color = Color.BLACK
+ textSize = 32f
+ }
+ var offset = 32f
+ for (debugText in debugStrings) {
+ canvas.drawText(debugText, 10f, offset, debugPaint)
+ offset += 32f
+ }
+ debugPaint.apply {
+ color = Color.RED
+ style = Paint.Style.STROKE
+ strokeWidth = 4f
+ }
+ val canvasWidth = canvas.width.toFloat()
+ val canvasHeight = canvas.height.toFloat()
+ canvas.drawRect(0f, 0f, canvasWidth, canvasHeight, debugPaint)
+
+ fun drawVerticalLine(x: Float, color: Int) {
+ debugPaint.color = color
+ val x = if (mView.isLeftPanel) x else canvasWidth - x
+ canvas.drawLine(x, debugInfoBottom, x, canvas.height.toFloat(), debugPaint)
+ }
+
+ drawVerticalLine(x = params.swipeTriggerThreshold, color = Color.BLUE)
+ drawVerticalLine(x = startX, color = Color.GREEN)
+ drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index ea41fe74f798..d41837b7cf4d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -62,6 +62,8 @@ import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -182,6 +184,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final PluginManager mPluginManager;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
+ private final BackPanelController.Factory mBackPanelControllerFactory;
private final ViewConfiguration mViewConfiguration;
private final WindowManager mWindowManager;
private final IWindowManager mWindowManagerService;
@@ -199,6 +202,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final Region mExcludeRegion = new Region();
private final Region mUnrestrictedExcludeRegion = new Region();
private final LatencyTracker mLatencyTracker;
+ private final FeatureFlags mFeatureFlags;
// The left side edge width where touch down is allowed
private int mEdgeWidthLeft;
@@ -230,6 +234,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private boolean mIsBackGestureAllowed;
private boolean mGestureBlockingActivityRunning;
private boolean mIsInPipMode;
+ private boolean mIsPredictiveBackAnimEnabled;
private InputMonitor mInputMonitor;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -298,12 +303,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
};
- EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
- BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
- NavigationModeController navigationModeController, ViewConfiguration viewConfiguration,
- WindowManager windowManager, IWindowManager windowManagerService,
- FalsingManager falsingManager, LatencyTracker latencyTracker) {
+ EdgeBackGestureHandler(
+ Context context,
+ OverviewProxyService overviewProxyService,
+ SysUiState sysUiState,
+ PluginManager pluginManager,
+ @Main Executor executor,
+ BroadcastDispatcher broadcastDispatcher,
+ ProtoTracer protoTracer,
+ NavigationModeController navigationModeController,
+ BackPanelController.Factory backPanelControllerFactory,
+ ViewConfiguration viewConfiguration,
+ WindowManager windowManager,
+ IWindowManager windowManagerService,
+ FalsingManager falsingManager,
+ LatencyTracker latencyTracker,
+ FeatureFlags featureFlags) {
super(broadcastDispatcher);
mContext = context;
mDisplayId = context.getDisplayId();
@@ -313,11 +328,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mPluginManager = pluginManager;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
+ mBackPanelControllerFactory = backPanelControllerFactory;
mViewConfiguration = viewConfiguration;
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
mFalsingManager = falsingManager;
mLatencyTracker = latencyTracker;
+ mFeatureFlags = featureFlags;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
context.getString(com.android.internal.R.string.config_recentsComponentName));
if (recentsComponentName != null) {
@@ -507,8 +524,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
Choreographer.getInstance(), this::onInputEvent);
// Add a nav bar panel window
- setEdgeBackPlugin(
- new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ mIsPredictiveBackAnimEnabled =
+ mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_ANIM);
+ resetEdgeBackPlugin();
mPluginManager.addPluginListener(
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
}
@@ -523,7 +541,17 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
@Override
public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
- setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ resetEdgeBackPlugin();
+ }
+
+ private void resetEdgeBackPlugin() {
+ if (mIsPredictiveBackAnimEnabled) {
+ setEdgeBackPlugin(
+ mBackPanelControllerFactory.create(mContext, mBackAnimation));
+ } else {
+ setEdgeBackPlugin(
+ new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ }
}
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
@@ -948,8 +976,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
public void setBackAnimation(BackAnimation backAnimation) {
mBackAnimation = backAnimation;
- if (mEdgeBackPlugin != null && mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
- ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ if (mEdgeBackPlugin != null) {
+ if (mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
+ ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ } else if (mEdgeBackPlugin instanceof BackPanelController) {
+ ((BackPanelController) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ }
}
}
@@ -967,20 +999,29 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final BroadcastDispatcher mBroadcastDispatcher;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
+ private final BackPanelController.Factory mBackPanelControllerFactory;
private final ViewConfiguration mViewConfiguration;
private final WindowManager mWindowManager;
private final IWindowManager mWindowManagerService;
private final FalsingManager mFalsingManager;
private final LatencyTracker mLatencyTracker;
+ private final FeatureFlags mFeatureFlags;
@Inject
public Factory(OverviewProxyService overviewProxyService,
- SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
- BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
- NavigationModeController navigationModeController,
- ViewConfiguration viewConfiguration, WindowManager windowManager,
- IWindowManager windowManagerService, FalsingManager falsingManager,
- LatencyTracker latencyTracker) {
+ SysUiState sysUiState,
+ PluginManager pluginManager,
+ @Main Executor executor,
+ BroadcastDispatcher broadcastDispatcher,
+ ProtoTracer protoTracer,
+ NavigationModeController navigationModeController,
+ BackPanelController.Factory backPanelControllerFactory,
+ ViewConfiguration viewConfiguration,
+ WindowManager windowManager,
+ IWindowManager windowManagerService,
+ FalsingManager falsingManager,
+ LatencyTracker latencyTracker,
+ FeatureFlags featureFlags) {
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
@@ -988,19 +1029,33 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mBroadcastDispatcher = broadcastDispatcher;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
+ mBackPanelControllerFactory = backPanelControllerFactory;
mViewConfiguration = viewConfiguration;
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
mFalsingManager = falsingManager;
mLatencyTracker = latencyTracker;
+ mFeatureFlags = featureFlags;
}
/** Construct a {@link EdgeBackGestureHandler}. */
public EdgeBackGestureHandler create(Context context) {
- return new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiState,
- mPluginManager, mExecutor, mBroadcastDispatcher, mProtoTracer,
- mNavigationModeController, mViewConfiguration, mWindowManager,
- mWindowManagerService, mFalsingManager, mLatencyTracker);
+ return new EdgeBackGestureHandler(
+ context,
+ mOverviewProxyService,
+ mSysUiState,
+ mPluginManager,
+ mExecutor,
+ mBroadcastDispatcher,
+ mProtoTracer,
+ mNavigationModeController,
+ mBackPanelControllerFactory,
+ mViewConfiguration,
+ mWindowManager,
+ mWindowManagerService,
+ mFalsingManager,
+ mLatencyTracker,
+ mFeatureFlags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
new file mode 100644
index 000000000000..51566b0f8393
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -0,0 +1,139 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.content.res.Resources
+import com.android.systemui.R
+
+data class EdgePanelParams(private var resources: Resources) {
+ var arrowThickness: Float = 0f
+ private set
+ var entryArrowLength: Float = 0f
+ private set
+ var entryArrowHeight: Float = 0f
+ private set
+ var activeArrowLength: Float = 0f
+ private set
+ var activeArrowHeight: Float = 0f
+ private set
+ var stretchedArrowLength: Float = 0f
+ private set
+ var stretchedArrowHeight: Float = 0f
+ private set
+ var cancelledArrowLength: Float = 0f
+ private set
+ var cancelledArrowHeight: Float = 0f
+ private set
+ var entryMargin: Float = 0f
+ private set
+ var entryBackgroundWidth: Float = 0f
+ private set
+ var entryBackgroundHeight: Float = 0f
+ private set
+ var entryEdgeCorners: Float = 0f
+ private set
+ var entryFarCorners: Float = 0f
+ private set
+ var preThresholdMargin: Float = 0f
+ private set
+ var preThresholdBackgroundWidth: Float = 0f
+ private set
+ var preThresholdBackgroundHeight: Float = 0f
+ private set
+ var preThresholdEdgeCorners: Float = 0f
+ private set
+ var preThresholdFarCorners: Float = 0f
+ private set
+ var activeMargin: Float = 0f
+ private set
+ var activeBackgroundWidth: Float = 0f
+ private set
+ var activeBackgroundHeight: Float = 0f
+ private set
+ var activeEdgeCorners: Float = 0f
+ private set
+ var activeFarCorners: Float = 0f
+ private set
+ var fullyStretchedThreshold: Float = 0f
+ private set
+ var stretchMargin: Float = 0f
+ private set
+ var stretchBackgroundWidth: Float = 0f
+ private set
+ var stretchBackgroundHeight: Float = 0f
+ private set
+ var stretchEdgeCorners: Float = 0f
+ private set
+ var stretchFarCorners: Float = 0f
+ private set
+
+ // navigation bar edge constants
+ var arrowPaddingEnd: Int = 0
+ private set
+
+ // The closest to y
+ var minArrowYPosition: Int = 0
+ private set
+ var fingerOffset: Int = 0
+ private set
+ var swipeTriggerThreshold: Float = 0f
+ private set
+ var swipeProgressThreshold: Float = 0f
+ private set
+
+ // The minimum delta needed to change direction / stop triggering back
+ var minDeltaForSwitch: Int = 0
+ private set
+
+ init {
+ update(resources)
+ }
+
+ private fun getDimen(id: Int): Float {
+ return resources.getDimension(id)
+ }
+
+ private fun getPx(id: Int): Int {
+ return resources.getDimensionPixelSize(id)
+ }
+
+ fun update(resources: Resources) {
+ this.resources = resources
+ arrowThickness = getDimen(R.dimen.navigation_edge_arrow_thickness)
+ entryArrowLength = getDimen(R.dimen.navigation_edge_entry_arrow_length)
+ entryArrowHeight = getDimen(R.dimen.navigation_edge_entry_arrow_height)
+ activeArrowLength = getDimen(R.dimen.navigation_edge_active_arrow_length)
+ activeArrowHeight = getDimen(R.dimen.navigation_edge_active_arrow_height)
+ stretchedArrowLength = getDimen(R.dimen.navigation_edge_stretched_arrow_length)
+ stretchedArrowHeight = getDimen(R.dimen.navigation_edge_stretched_arrow_height)
+ cancelledArrowLength = getDimen(R.dimen.navigation_edge_cancelled_arrow_length)
+ cancelledArrowHeight = getDimen(R.dimen.navigation_edge_cancelled_arrow_height)
+ entryMargin = getDimen(R.dimen.navigation_edge_entry_margin)
+ entryBackgroundWidth = getDimen(R.dimen.navigation_edge_entry_background_width)
+ entryBackgroundHeight = getDimen(R.dimen.navigation_edge_entry_background_height)
+ entryEdgeCorners = getDimen(R.dimen.navigation_edge_entry_edge_corners)
+ entryFarCorners = getDimen(R.dimen.navigation_edge_entry_far_corners)
+ preThresholdMargin = getDimen(R.dimen.navigation_edge_pre_threshold_margin)
+ preThresholdBackgroundWidth =
+ getDimen(R.dimen.navigation_edge_pre_threshold_background_width)
+ preThresholdBackgroundHeight =
+ getDimen(R.dimen.navigation_edge_pre_threshold_background_height)
+ preThresholdEdgeCorners = getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners)
+ preThresholdFarCorners = getDimen(R.dimen.navigation_edge_pre_threshold_far_corners)
+ activeMargin = getDimen(R.dimen.navigation_edge_active_margin)
+ activeBackgroundWidth = getDimen(R.dimen.navigation_edge_active_background_width)
+ activeBackgroundHeight = getDimen(R.dimen.navigation_edge_active_background_height)
+ activeEdgeCorners = getDimen(R.dimen.navigation_edge_active_edge_corners)
+ activeFarCorners = getDimen(R.dimen.navigation_edge_active_far_corners)
+ fullyStretchedThreshold = getDimen(R.dimen.navigation_edge_stretch_threshold)
+ stretchMargin = getDimen(R.dimen.navigation_edge_stretch_margin)
+ stretchBackgroundWidth = getDimen(R.dimen.navigation_edge_stretch_background_width)
+ stretchBackgroundHeight = getDimen(R.dimen.navigation_edge_stretch_background_height)
+ stretchEdgeCorners = getDimen(R.dimen.navigation_edge_stretch_left_corners)
+ stretchFarCorners = getDimen(R.dimen.navigation_edge_stretch_right_corners)
+ arrowPaddingEnd = getPx(R.dimen.navigation_edge_panel_padding)
+ minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y)
+ fingerOffset = getPx(R.dimen.navigation_edge_finger_offset)
+ swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
+ swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
+ minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index d05c3385e982..6287857e7be9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -37,6 +37,7 @@ import kotlin.properties.Delegates.notNull
private const val TAG = "NotifStackSizeCalc"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
+private val SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
/** Calculates number of notifications to display and the height of the notification stack. */
@SysUISingleton
@@ -87,9 +88,10 @@ constructor(
// Could be < 0 if the space available is less than the shelf size. Returns 0 in this case.
maxNotifications = max(0, maxNotifications)
log {
+ val sequence = if (SPEW) " stackHeightSequence=${stackHeightSequence.toList()}" else ""
"computeMaxKeyguardNotifications(" +
"availableSpace=$totalAvailableSpace" +
- " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications"
+ " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications$sequence"
}
return maxNotifications
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index f55e76b3734a..6ff7dd4a240f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3500,11 +3500,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public void onTrackingStopped(boolean expand) {
- if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- if (!expand && !mKeyguardStateController.canDismissLockScreen()) {
- mStatusBarKeyguardViewManager.showBouncer(false /* scrimmed */);
- }
- }
}
// TODO: Figure out way to remove these.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4a912572b7ed..2977ab49af9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -192,6 +192,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.Compile;
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.Utils;
@@ -216,7 +217,9 @@ import javax.inject.Provider;
@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationPanelViewController extends PanelViewController {
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean DEBUG_DRAWABLE = false;
/**
* The parallax amount of the quick settings translation when dragging down the panel
@@ -816,7 +819,7 @@ public class NotificationPanelViewController extends PanelViewController {
mSettingsChangeObserver = new SettingsChangeObserver(handler);
mSplitShadeEnabled =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
- mView.setWillNotDraw(!DEBUG);
+ mView.setWillNotDraw(!DEBUG_DRAWABLE);
mLargeScreenShadeHeaderController = largeScreenShadeHeaderController;
mLayoutInflater = layoutInflater;
mFeatureFlags = featureFlags;
@@ -890,7 +893,7 @@ public class NotificationPanelViewController extends PanelViewController {
mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener());
- if (DEBUG) {
+ if (DEBUG_DRAWABLE) {
mView.getOverlay().add(new DebugDrawable());
}
@@ -1189,7 +1192,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void reInflateViews() {
- if (DEBUG) Log.d(TAG, "reInflateViews");
+ if (DEBUG_LOGCAT) Log.d(TAG, "reInflateViews");
// Re-inflate the status view group.
KeyguardStatusView keyguardStatusView =
mNotificationContainerParent.findViewById(R.id.keyguard_status_view);
@@ -1293,6 +1296,8 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateMaxDisplayedNotifications(boolean recompute) {
if (recompute) {
mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1);
+ } else {
+ if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
}
if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
@@ -1545,6 +1550,19 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.getHeight()
- staticTopPadding
- bottomPadding;
+
+ if (SPEW_LOGCAT) {
+ Log.d(TAG, "getSpaceForLockscreenNotifications()"
+ + " availableSpace=" + availableSpace
+ + " NSSL.height=" + mNotificationStackScrollLayoutController.getHeight()
+ + " NSSL.top=" + mNotificationStackScrollLayoutController.getTop()
+ + " staticTopPadding=" + staticTopPadding
+ + " bottomPadding=" + bottomPadding
+ + " lockIconPadding=" + lockIconPadding
+ + " mIndicationBottomPadding=" + mIndicationBottomPadding
+ + " mAmbientIndicationBottomPadding=" + mAmbientIndicationBottomPadding
+ );
+ }
return availableSpace;
}
@@ -1553,7 +1571,12 @@ public class NotificationPanelViewController extends PanelViewController {
*/
@VisibleForTesting
int computeMaxKeyguardNotifications() {
- if (mAmbientState.getFractionToShade() > 0 || mAmbientState.getDozeAmount() > 0) {
+ if (mAmbientState.getFractionToShade() > 0) {
+ if (SPEW_LOGCAT) {
+ Log.v(TAG, "Internally skipping computeMaxKeyguardNotifications()"
+ + " fractionToShade=" + mAmbientState.getFractionToShade()
+ );
+ }
return mMaxAllowedKeyguardNotifications;
}
@@ -1743,7 +1766,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private boolean onQsIntercept(MotionEvent event) {
- if (DEBUG) Log.d(TAG, "onQsIntercept");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onQsIntercept");
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -1798,7 +1821,7 @@ public class NotificationPanelViewController extends PanelViewController {
if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded))
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
- if (DEBUG) Log.d(TAG, "onQsIntercept - start tracking expansion");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onQsIntercept - start tracking expansion");
mView.getParent().requestDisallowInterceptTouchEvent(true);
mQsTracking = true;
traceQsJank(true /* startTracing */, false /* wasCancelled */);
@@ -2075,7 +2098,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void handleQsDown(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
event.getX(), event.getY(), -1)) {
- if (DEBUG) Log.d(TAG, "handleQsDown");
+ if (DEBUG_LOGCAT) Log.d(TAG, "handleQsDown");
mFalsingCollector.onQsDown();
mQsTracking = true;
onQsExpansionStarted();
@@ -2189,7 +2212,7 @@ public class NotificationPanelViewController extends PanelViewController {
break;
case MotionEvent.ACTION_MOVE:
- if (DEBUG) Log.d(TAG, "onQSTouch move");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onQSTouch move");
setQsExpansion(h + mInitialHeightOnTouch);
if (h >= getFalsingThreshold()) {
mQsTouchAboveFalsingThreshold = true;
@@ -2341,7 +2364,7 @@ public class NotificationPanelViewController extends PanelViewController {
mCentralSurfaces.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
}
- if (DEBUG) {
+ if (DEBUG_DRAWABLE) {
mView.invalidate();
}
}
@@ -2998,7 +3021,7 @@ public class NotificationPanelViewController extends PanelViewController {
// This is a circular dependency and should be avoided, otherwise we'll have
// a stack overflow.
if (mStackScrollerMeasuringPass > 2) {
- if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
+ if (DEBUG_LOGCAT) Log.d(TAG, "Unstable notification panel height. Aborting.");
} else {
positionClockAndNotifications();
}
@@ -3032,7 +3055,7 @@ public class NotificationPanelViewController extends PanelViewController {
updateNotificationTranslucency();
updatePanelExpanded();
updateGestureExclusionRect();
- if (DEBUG) {
+ if (DEBUG_DRAWABLE) {
mView.invalidate();
}
}
@@ -3675,7 +3698,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void onQsPanelScrollChanged(int scrollY) {
mLargeScreenShadeHeaderController.setQsScrollY(scrollY);
if (scrollY > 0 && !mQsFullyExpanded) {
- if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
+ if (DEBUG_LOGCAT) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
// If we are scrolling QS, we should be fully expanded.
expandWithQs();
}
@@ -4070,7 +4093,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
public void setHeaderDebugInfo(String text) {
- if (DEBUG) mHeaderDebugInfo = text;
+ if (DEBUG_DRAWABLE) mHeaderDebugInfo = text;
}
public void onThemeChanged() {
@@ -4112,7 +4135,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
if (!isFullyCollapsed() && onQsIntercept(event)) {
- if (DEBUG) Log.d(TAG, "onQsIntercept true");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onQsIntercept true");
return true;
}
return super.onInterceptTouchEvent(event);
@@ -4183,7 +4206,7 @@ public class NotificationPanelViewController extends PanelViewController {
handled |= mHeadsUpTouchHelper.onTouchEvent(event);
if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
- if (DEBUG) Log.d(TAG, "handleQsTouch true");
+ if (DEBUG_LOGCAT) Log.d(TAG, "handleQsTouch true");
return true;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
@@ -4623,7 +4646,7 @@ public class NotificationPanelViewController extends PanelViewController {
private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
@Override
public void onThemeChanged() {
- if (DEBUG) Log.d(TAG, "onThemeChanged");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onThemeChanged");
mThemeResId = mView.getContext().getThemeResId();
reInflateViews();
}
@@ -4631,7 +4654,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onSmallestScreenWidthChanged() {
Trace.beginSection("onSmallestScreenWidthChanged");
- if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onSmallestScreenWidthChanged");
// Can affect multi-user switcher visibility as it depends on screen size by default:
// it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
@@ -4648,7 +4671,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onDensityOrFontScaleChanged() {
- if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onDensityOrFontScaleChanged");
reInflateViews();
}
}
@@ -4661,7 +4684,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onChange(boolean selfChange) {
- if (DEBUG) Log.d(TAG, "onSettingsChanged");
+ if (DEBUG_LOGCAT) Log.d(TAG, "onSettingsChanged");
// Can affect multi-user switcher visibility
reInflateViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 48f57774218b..e3c070e3686e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -483,12 +483,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer == null) {
return;
}
+ mBouncer.hide(destroyView);
if (mShowing) {
// If we were showing the bouncer and then aborting, we need to also clear out any
// potential actions unless we actually unlocked.
cancelPostAuthActions();
}
- mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index fa9161a19cb1..f599e3b12c57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -578,19 +578,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void computeMaxKeyguardNotifications_dozeAmountNotZero_returnsExistingMax() {
- when(mAmbientState.getDozeAmount()).thenReturn(0.5f);
- mNotificationPanelViewController.setMaxDisplayedNotifications(-1);
-
- // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value
- assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications())
- .isEqualTo(-1);
- }
-
- @Test
public void computeMaxKeyguardNotifications_noTransition_updatesMax() {
when(mAmbientState.getFractionToShade()).thenReturn(0f);
- when(mAmbientState.getDozeAmount()).thenReturn(0f);
mNotificationPanelViewController.setMaxDisplayedNotifications(-1);
// computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index aaa5a6bdbe83..0c1d04253bf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -320,6 +320,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, true /* afterKeyguardGone */);
+ when(mBouncer.isShowing()).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(true);
mStatusBarKeyguardViewManager.hide(0, 30);
verify(action, never()).onDismiss();
@@ -327,6 +328,20 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void testHidingBouncer_cancelsGoneRunnable() {
+ OnDismissAction action = mock(OnDismissAction.class);
+ Runnable cancelAction = mock(Runnable.class);
+ mStatusBarKeyguardViewManager.dismissWithAction(
+ action, cancelAction, true /* afterKeyguardGone */);
+
+ when(mBouncer.isShowing()).thenReturn(false);
+ mStatusBarKeyguardViewManager.hideBouncer(true);
+
+ verify(action, never()).onDismiss();
+ verify(cancelAction).run();
+ }
+
+ @Test
public void testHiding_doesntCancelWhenShowing() {
OnDismissAction action = mock(OnDismissAction.class);
Runnable cancelAction = mock(Runnable.class);
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index e27b7a659ae6..09a05bb6b40e 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -1135,41 +1135,13 @@ public class CameraExtensionsProxyService extends Service {
CameraSessionConfig ret = new CameraSessionConfig();
ret.outputConfigs = new ArrayList<>();
for (Camera2OutputConfigImpl output : outputConfigs) {
- CameraOutputConfig entry = new CameraOutputConfig();
- entry.outputId = new OutputConfigId();
- entry.outputId.id = output.getId();
- entry.physicalCameraId = output.getPhysicalCameraId();
- entry.surfaceGroupId = output.getSurfaceGroupId();
- if (output instanceof SurfaceOutputConfigImpl) {
- SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
- entry.type = CameraOutputConfig.TYPE_SURFACE;
- entry.surface = surfaceConfig.getSurface();
- } else if (output instanceof ImageReaderOutputConfigImpl) {
- ImageReaderOutputConfigImpl imageReaderOutputConfig =
- (ImageReaderOutputConfigImpl) output;
- entry.type = CameraOutputConfig.TYPE_IMAGEREADER;
- entry.size = new android.hardware.camera2.extension.Size();
- entry.size.width = imageReaderOutputConfig.getSize().getWidth();
- entry.size.height = imageReaderOutputConfig.getSize().getHeight();
- entry.imageFormat = imageReaderOutputConfig.getImageFormat();
- entry.capacity = imageReaderOutputConfig.getMaxImages();
- } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
- MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
- (MultiResolutionImageReaderOutputConfigImpl) output;
- entry.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
- entry.imageFormat = multiResReaderConfig.getImageFormat();
- entry.capacity = multiResReaderConfig.getMaxImages();
- } else {
- throw new IllegalStateException("Unknown output config type!");
- }
+ CameraOutputConfig entry = getCameraOutputConfig(output);
List<Camera2OutputConfigImpl> sharedOutputs =
output.getSurfaceSharingOutputConfigs();
if ((sharedOutputs != null) && (!sharedOutputs.isEmpty())) {
- entry.surfaceSharingOutputConfigs = new ArrayList<>();
+ entry.sharedSurfaceConfigs = new ArrayList<>();
for (Camera2OutputConfigImpl sharedOutput : sharedOutputs) {
- OutputConfigId outputId = new OutputConfigId();
- outputId.id = sharedOutput.getId();
- entry.surfaceSharingOutputConfigs.add(outputId);
+ entry.sharedSurfaceConfigs.add(getCameraOutputConfig(sharedOutput));
}
}
ret.outputConfigs.add(entry);
@@ -1854,4 +1826,36 @@ public class CameraExtensionsProxyService extends Service {
}
}
}
+
+ private static CameraOutputConfig getCameraOutputConfig(Camera2OutputConfigImpl output) {
+ CameraOutputConfig ret = new CameraOutputConfig();
+ ret.outputId = new OutputConfigId();
+ ret.outputId.id = output.getId();
+ ret.physicalCameraId = output.getPhysicalCameraId();
+ ret.surfaceGroupId = output.getSurfaceGroupId();
+ if (output instanceof SurfaceOutputConfigImpl) {
+ SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
+ ret.type = CameraOutputConfig.TYPE_SURFACE;
+ ret.surface = surfaceConfig.getSurface();
+ } else if (output instanceof ImageReaderOutputConfigImpl) {
+ ImageReaderOutputConfigImpl imageReaderOutputConfig =
+ (ImageReaderOutputConfigImpl) output;
+ ret.type = CameraOutputConfig.TYPE_IMAGEREADER;
+ ret.size = new android.hardware.camera2.extension.Size();
+ ret.size.width = imageReaderOutputConfig.getSize().getWidth();
+ ret.size.height = imageReaderOutputConfig.getSize().getHeight();
+ ret.imageFormat = imageReaderOutputConfig.getImageFormat();
+ ret.capacity = imageReaderOutputConfig.getMaxImages();
+ } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
+ MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
+ (MultiResolutionImageReaderOutputConfigImpl) output;
+ ret.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
+ ret.imageFormat = multiResReaderConfig.getImageFormat();
+ ret.capacity = multiResReaderConfig.getMaxImages();
+ } else {
+ throw new IllegalStateException("Unknown output config type!");
+ }
+
+ return ret;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
index 9e8b2228195e..26a295ef9253 100644
--- a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
+++ b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
@@ -56,39 +56,41 @@ public class CustomScrollView extends ScrollView {
private int mWidth = -1;
private int mHeight = -1;
- private int mMaxPortraitBodyHeightPercent = 20;
- private int mMaxLandscapeBodyHeightPercent = 20;
+ private int mMaxPortraitBodyHeightPercent;
+ private int mMaxLandscapeBodyHeightPercent;
+ private int mAttrBasedMaxHeightPercent;
public CustomScrollView(Context context) {
super(context);
- setMaxBodyHeightPercent();
+ setMaxBodyHeightPercent(context);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
- setMaxBodyHeightPercent();
+ setMaxBodyHeightPercent(context);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setMaxBodyHeightPercent();
+ setMaxBodyHeightPercent(context);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setMaxBodyHeightPercent();
+ setMaxBodyHeightPercent(context);
}
- private void setMaxBodyHeightPercent() {
+ private void setMaxBodyHeightPercent(Context context) {
+ mAttrBasedMaxHeightPercent = getAttrBasedMaxHeightPercent(context);
mMaxPortraitBodyHeightPercent = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_SAVE_DIALOG_PORTRAIT_BODY_HEIGHT_MAX_PERCENT,
- mMaxPortraitBodyHeightPercent);
+ mAttrBasedMaxHeightPercent);
mMaxLandscapeBodyHeightPercent = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_SAVE_DIALOG_LANDSCAPE_BODY_HEIGHT_MAX_PERCENT,
- mMaxLandscapeBodyHeightPercent);
+ mAttrBasedMaxHeightPercent);
}
@Override
@@ -117,29 +119,27 @@ public class CustomScrollView extends ScrollView {
final int contentHeight = content.getMeasuredHeight();
int displayHeight = point.y;
- int configBasedMaxHeight = (getResources().getConfiguration().orientation
+ int maxHeight = (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE)
? (int) (mMaxLandscapeBodyHeightPercent * displayHeight / 100)
: (int) (mMaxPortraitBodyHeightPercent * displayHeight / 100);
- mHeight = configBasedMaxHeight > 0
- ? Math.min(contentHeight, configBasedMaxHeight)
- : Math.min(contentHeight, getAttrBasedMaxHeight(context, displayHeight));
+ mHeight = Math.min(contentHeight, maxHeight);
if (sDebug) {
Slog.d(TAG, "calculateDimensions():"
+ " mMaxPortraitBodyHeightPercent=" + mMaxPortraitBodyHeightPercent
+ ", mMaxLandscapeBodyHeightPercent=" + mMaxLandscapeBodyHeightPercent
- + ", configBasedMaxHeight=" + configBasedMaxHeight
- + ", attrBasedMaxHeight=" + getAttrBasedMaxHeight(context, displayHeight)
+ + ", mAttrBasedMaxHeightPercent=" + mAttrBasedMaxHeightPercent
+ + ", maxHeight=" + maxHeight
+ ", contentHeight=" + contentHeight
+ ", w=" + mWidth + ", h=" + mHeight);
}
}
- private int getAttrBasedMaxHeight(Context context, int displayHeight) {
+ private int getAttrBasedMaxHeightPercent(Context context) {
final TypedValue maxHeightAttrTypedValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.autofillSaveCustomSubtitleMaxHeight,
maxHeightAttrTypedValue, true);
- return (int) maxHeightAttrTypedValue.getFraction(displayHeight, displayHeight);
+ return (int) maxHeightAttrTypedValue.getFraction(100, 100);
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index 1d7d44327022..2ab1aa80176a 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -108,10 +108,10 @@ public class CompanionApplicationController {
* CDM binds to the companion app.
*/
public void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName,
- boolean bindImportant) {
+ boolean isSelfManaged) {
if (DEBUG) {
Log.i(TAG, "bind() u" + userId + "/" + packageName
- + " important=" + bindImportant);
+ + " isSelfManaged=" + isSelfManaged);
}
final List<ComponentName> companionServices =
@@ -134,7 +134,7 @@ public class CompanionApplicationController {
serviceConnectors = CollectionUtils.map(companionServices, componentName ->
CompanionDeviceServiceConnector.newInstance(mContext, userId,
- componentName, bindImportant));
+ componentName, isSelfManaged));
mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index b9b29ffb4e86..cb945381dfa5 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -17,7 +17,7 @@
package com.android.server.companion;
import static android.content.Context.BIND_ALMOST_PERCEPTIBLE;
-import static android.content.Context.BIND_IMPORTANT;
+import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE;
import static android.os.Process.THREAD_PRIORITY_DEFAULT;
import android.annotation.NonNull;
@@ -61,21 +61,22 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
/**
* Create a CompanionDeviceServiceConnector instance.
*
- * When bindImportant is false, the binding flag will be BIND_ALMOST_PERCEPTIBLE
+ * For self-managed apps, the binding flag will be BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
+ * (oom_score_adj = VISIBLE_APP_ADJ = 100).
+ *
+ * For non self-managed apps, the binding flag will be BIND_ALMOST_PERCEPTIBLE
* (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = 225). The target service will be treated
* as important as a perceptible app (IMPORTANCE_VISIBLE = 200), and will be unbound when
* the app is removed from task manager.
- * When bindImportant is true, the binding flag will be BIND_IMPORTANT
- * (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = -700). The target service will
- * have the highest priority to avoid being killed (IMPORTANCE_FOREGROUND = 100).
*
* One time permission's importance level to keep session alive is
* IMPORTANCE_FOREGROUND_SERVICE = 125. In order to kill the one time permission session, the
* service importance level should be higher than 125.
*/
static CompanionDeviceServiceConnector newInstance(@NonNull Context context,
- @UserIdInt int userId, @NonNull ComponentName componentName, boolean bindImportant) {
- final int bindingFlags = bindImportant ? BIND_IMPORTANT : BIND_ALMOST_PERCEPTIBLE;
+ @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged) {
+ final int bindingFlags = isSelfManaged ? BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
+ : BIND_ALMOST_PERCEPTIBLE;
return new CompanionDeviceServiceConnector(context, userId, componentName, bindingFlags);
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index aa30c0897d60..a25ac210f9c8 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -152,6 +152,9 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>
* <screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>
*
+ * <screenBrightnessRampIncreaseMaxMillis>2000</screenBrightnessRampIncreaseMaxMillis>
+ * <screenBrightnessRampDecreaseMaxMillis>3000</screenBrightnessRampDecreaseMaxMillis>
+ *
* <lightSensor>
* <type>android.sensor.light</type>
* <name>1234 Ambient Light Sensor</name>
@@ -259,6 +262,8 @@ public class DisplayDeviceConfig {
private float mBrightnessRampFastIncrease = Float.NaN;
private float mBrightnessRampSlowDecrease = Float.NaN;
private float mBrightnessRampSlowIncrease = Float.NaN;
+ private long mBrightnessRampDecreaseMaxMillis = 0;
+ private long mBrightnessRampIncreaseMaxMillis = 0;
private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
private float mScreenBrighteningMinThreshold = 0.0f; // Retain behaviour as though there is
@@ -534,6 +539,14 @@ public class DisplayDeviceConfig {
return mBrightnessRampSlowIncrease;
}
+ public long getBrightnessRampDecreaseMaxMillis() {
+ return mBrightnessRampDecreaseMaxMillis;
+ }
+
+ public long getBrightnessRampIncreaseMaxMillis() {
+ return mBrightnessRampIncreaseMaxMillis;
+ }
+
public int getAmbientHorizonLong() {
return mAmbientHorizonLong;
}
@@ -628,6 +641,8 @@ public class DisplayDeviceConfig {
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
+ ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+ + ", mBrightnessRampDecreaseMaxMillis=" + mBrightnessRampDecreaseMaxMillis
+ + ", mBrightnessRampIncreaseMaxMillis=" + mBrightnessRampIncreaseMaxMillis
+ ", mAmbientHorizonLong=" + mAmbientHorizonLong
+ ", mAmbientHorizonShort=" + mAmbientHorizonShort
+ ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
@@ -725,6 +740,8 @@ public class DisplayDeviceConfig {
mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX;
+ mBrightnessRampDecreaseMaxMillis = 0;
+ mBrightnessRampIncreaseMaxMillis = 0;
setSimpleMappingStrategyValues();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
@@ -1115,6 +1132,15 @@ public class DisplayDeviceConfig {
}
loadBrightnessRampsFromConfigXml();
}
+
+ final BigInteger increaseMax = config.getScreenBrightnessRampIncreaseMaxMillis();
+ if (increaseMax != null) {
+ mBrightnessRampIncreaseMaxMillis = increaseMax.intValue();
+ }
+ final BigInteger decreaseMax = config.getScreenBrightnessRampDecreaseMaxMillis();
+ if (decreaseMax != null) {
+ mBrightnessRampDecreaseMaxMillis = decreaseMax.intValue();
+ }
}
private void loadBrightnessRampsFromConfigXml() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 7dce2380407e..a4f49549c7eb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -76,6 +76,8 @@ class DisplayManagerShellCommand extends ShellCommand {
return setUserDisabledHdrTypes();
case "get-user-disabled-hdr-types":
return getUserDisabledHdrTypes();
+ case "get-displays":
+ return getDisplays();
case "dock":
return setDockedAndIdle();
case "undock":
@@ -133,6 +135,9 @@ class DisplayManagerShellCommand extends ShellCommand {
pw.println(" Sets the user disabled HDR types as TYPES");
pw.println(" get-user-disabled-hdr-types");
pw.println(" Returns the user disabled HDR types");
+ pw.println(" get-displays [CATEGORY]");
+ pw.println(" Returns the current displays. Can specify string category among");
+ pw.println(" DisplayManager.DISPLAY_CATEGORY_*; must use the actual string value.");
pw.println(" dock");
pw.println(" Sets brightness to docked + idle screen brightness mode");
pw.println(" undock");
@@ -141,6 +146,18 @@ class DisplayManagerShellCommand extends ShellCommand {
Intent.printIntentArgsHelp(pw , "");
}
+ private int getDisplays() {
+ String category = getNextArg();
+ DisplayManager dm = mService.getContext().getSystemService(DisplayManager.class);
+ Display[] displays = dm.getDisplays(category);
+ PrintWriter out = getOutPrintWriter();
+ out.println("Displays:");
+ for (int i = 0; i < displays.length; i++) {
+ out.println(" " + displays[i]);
+ }
+ return 0;
+ }
+
private int setBrightness() {
String brightnessText = getNextArg();
if (brightnessText == null) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 80ff8349a153..d13a9a3ec27e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -255,6 +255,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// to reach the final state.
private final boolean mBrightnessBucketsInDozeConfig;
+ // Maximum time a ramp animation can take.
+ private long mBrightnessRampIncreaseMaxTimeMillis;
+ private long mBrightnessRampDecreaseMaxTimeMillis;
+
// The pending power request.
// Initially null until the first call to requestPowerState.
@GuardedBy("mLock")
@@ -507,7 +511,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final Resources resources = context.getResources();
-
// DOZE AND DIM SETTINGS
mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
@@ -641,7 +644,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mIsRbcActive = mCdsi.isReduceBrightColorsActivated();
mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits);
-
// If rbc is turned on, off or there is a change in strength, we want to reset the short
// term model. Since the nits range at which brightness now operates has changed due to
// RBC/strength change, any short term model based on the previous range should be
@@ -837,6 +839,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
loadNitsRange(mContext.getResources());
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
+ if (mScreenBrightnessRampAnimator != null) {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ }
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -883,6 +890,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
noteScreenState(mPowerState.getScreenState());
@@ -1007,6 +1017,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease();
mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease();
mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease();
+ mBrightnessRampDecreaseMaxTimeMillis =
+ mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis();
+ mBrightnessRampIncreaseMaxTimeMillis =
+ mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis();
}
private void loadNitsRange(Resources resources) {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 2567e4306293..690ec3fb5aaf 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -34,6 +34,8 @@ class RampAnimator<T> {
private float mCurrentValue;
private float mTargetValue;
private float mRate;
+ private float mAnimationIncreaseMaxTimeSecs;
+ private float mAnimationDecreaseMaxTimeSecs;
private boolean mAnimating;
private float mAnimatedValue; // higher precision copy of mCurrentValue
@@ -43,13 +45,24 @@ class RampAnimator<T> {
private Listener mListener;
- public RampAnimator(T object, FloatProperty<T> property) {
+ RampAnimator(T object, FloatProperty<T> property) {
mObject = object;
mProperty = property;
mChoreographer = Choreographer.getInstance();
}
/**
+ * Sets the maximum time that a brightness animation can take.
+ */
+ public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
+ long animationRampDecreaseMaxTimeMillis) {
+ mAnimationIncreaseMaxTimeSecs = (animationRampIncreaseMaxTimeMillis > 0)
+ ? (animationRampIncreaseMaxTimeMillis / 1000.0f) : 0.0f;
+ mAnimationDecreaseMaxTimeSecs = (animationRampDecreaseMaxTimeMillis > 0)
+ ? (animationRampDecreaseMaxTimeMillis / 1000.0f) : 0.0f;
+ }
+
+ /**
* Starts animating towards the specified value.
*
* If this is the first time the property is being set or if the rate is 0,
@@ -83,6 +96,15 @@ class RampAnimator<T> {
return false;
}
+ // Adjust the rate so that we do not exceed our maximum animation time.
+ if (target > mCurrentValue && mAnimationIncreaseMaxTimeSecs > 0.0f
+ && ((target - mCurrentValue) / rate) > mAnimationIncreaseMaxTimeSecs) {
+ rate = (target - mCurrentValue) / mAnimationIncreaseMaxTimeSecs;
+ } else if (target < mCurrentValue && mAnimationDecreaseMaxTimeSecs > 0.0f
+ && ((mCurrentValue - target) / rate) > mAnimationDecreaseMaxTimeSecs) {
+ rate = (mCurrentValue - target) / mAnimationDecreaseMaxTimeSecs;
+ }
+
// Adjust the rate based on the closest target.
// If a faster rate is specified, then use the new rate so that we converge
// more rapidly based on the new request.
@@ -209,6 +231,17 @@ class RampAnimator<T> {
}
/**
+ * Sets the maximum time that a brightness animation can take.
+ */
+ public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
+ long animationRampDecreaseMaxTimeMillis) {
+ mFirst.setAnimationTimeLimits(animationRampIncreaseMaxTimeMillis,
+ animationRampDecreaseMaxTimeMillis);
+ mSecond.setAnimationTimeLimits(animationRampIncreaseMaxTimeMillis,
+ animationRampDecreaseMaxTimeMillis);
+ }
+
+ /**
* Starts animating towards the specified values.
*
* If this is the first time the property is being set or if the rate is 0,
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9e5da450c8a5..d536a463f7b4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -40,6 +40,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
@@ -85,6 +86,7 @@ import android.view.ViewConfiguration;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalManagerRegistry;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
@@ -540,14 +542,19 @@ public class MediaSessionService extends SystemService implements Monitor {
if (TextUtils.isEmpty(packageName)) {
throw new IllegalArgumentException("packageName may not be empty");
}
- String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
- final int packageCount = packages.length;
- for (int i = 0; i < packageCount; i++) {
- if (packageName.equals(packages[i])) {
- return;
- }
+ if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
+ // If the caller is shell, then trust the packageName given and allow it
+ // to proceed.
+ return;
+ }
+ final PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ final int actualUid = packageManagerInternal.getPackageUid(
+ packageName, 0 /* flags */, UserHandle.getUserId(uid));
+ if (!UserHandle.isSameApp(uid, actualUid)) {
+ throw new IllegalArgumentException("packageName does not belong to the calling uid; "
+ + "pkg=" + packageName + ", uid=" + uid);
}
- throw new IllegalArgumentException("packageName is not owned by the calling process");
}
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1bcb41b784d9..d3580a4e3317 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -658,6 +658,9 @@ public class NotificationManagerService extends SystemService {
private int mWarnRemoteViewsSizeBytes;
private int mStripRemoteViewsSizeBytes;
+ @VisibleForTesting
+ protected boolean mShowReviewPermissionsNotification;
+
private MetricsLogger mMetricsLogger;
private NotificationChannelLogger mNotificationChannelLogger;
private TriPredicate<String, Integer, String> mAllowedManagedServicePackages;
@@ -2281,7 +2284,8 @@ public class NotificationManagerService extends SystemService {
mPermissionHelper,
mNotificationChannelLogger,
mAppOps,
- new SysUiStatsEvent.BuilderFactory());
+ new SysUiStatsEvent.BuilderFactory(),
+ mShowReviewPermissionsNotification);
mPreferencesHelper.updateFixedImportance(mUm.getUsers());
mRankingHelper = new RankingHelper(getContext(),
mRankingHandler,
@@ -2470,6 +2474,9 @@ public class NotificationManagerService extends SystemService {
WorkerHandler handler = new WorkerHandler(Looper.myLooper());
+ mShowReviewPermissionsNotification = getContext().getResources().getBoolean(
+ R.bool.config_notificationReviewPermissions);
+
init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
@@ -6321,6 +6328,11 @@ public class NotificationManagerService extends SystemService {
@Override
public void sendReviewPermissionsNotification() {
+ if (!mShowReviewPermissionsNotification) {
+ // don't show if this notification is turned off
+ return;
+ }
+
// This method is meant to be called from the JobService upon running the job for this
// notification having been rescheduled; so without checking any other state, it will
// send the notification.
@@ -6937,9 +6949,14 @@ public class NotificationManagerService extends SystemService {
try {
if (mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)
&& mTelecomManager != null) {
- return mTelecomManager.isInManagedCall()
- || mTelecomManager.isInSelfManagedCall(
- pkg, UserHandle.getUserHandleForUid(uid));
+ try {
+ return mTelecomManager.isInManagedCall()
+ || mTelecomManager.isInSelfManagedCall(
+ pkg, UserHandle.getUserHandleForUid(uid));
+ } catch (IllegalStateException ise) {
+ // Telecom is not ready (this is likely early boot), so there are no calls.
+ return false;
+ }
}
return false;
} finally {
@@ -11654,6 +11671,11 @@ public class NotificationManagerService extends SystemService {
}
protected void maybeShowInitialReviewPermissionsNotification() {
+ if (!mShowReviewPermissionsNotification) {
+ // if this notification is disabled by settings do not ever show it
+ return;
+ }
+
int currentState = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
REVIEW_NOTIF_STATE_UNKNOWN);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 97133a56779d..477b8da61e0f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -86,7 +86,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -191,6 +190,7 @@ public class PreferencesHelper implements RankingConfig {
private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING;
private boolean mAreChannelsBypassingDnd;
private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
+ private boolean mShowReviewPermissionsNotification;
private boolean mAllowInvalidShortcuts = false;
@@ -198,7 +198,8 @@ public class PreferencesHelper implements RankingConfig {
ZenModeHelper zenHelper, PermissionHelper permHelper,
NotificationChannelLogger notificationChannelLogger,
AppOpsManager appOpsManager,
- SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
+ SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
+ boolean showReviewPermissionsNotification) {
mContext = context;
mZenModeHelper = zenHelper;
mRankingHandler = rankingHandler;
@@ -207,6 +208,7 @@ public class PreferencesHelper implements RankingConfig {
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
mStatsEventBuilderFactory = statsEventBuilderFactory;
+ mShowReviewPermissionsNotification = showReviewPermissionsNotification;
XML_VERSION = 4;
@@ -226,7 +228,8 @@ public class PreferencesHelper implements RankingConfig {
final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION);
- if (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION) {
+ if (mShowReviewPermissionsNotification
+ && (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION)) {
// make a note that we should show the notification at some point.
// it shouldn't be possible for the user to already have seen it, as the XML version
// would be newer then.
diff --git a/services/core/java/com/android/server/pm/ApexPackageInfo.java b/services/core/java/com/android/server/pm/ApexPackageInfo.java
index 07f2fd3e1d1f..f959a52f0374 100644
--- a/services/core/java/com/android/server/pm/ApexPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ApexPackageInfo.java
@@ -27,7 +27,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.ArrayMap;
import android.util.PrintWriterPrinter;
-import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -271,11 +270,11 @@ class ApexPackageInfo {
}
}
ipw.println("Active APEX packages:");
- dumpFromPackagesCache(getActivePackages(), packageName, ipw);
+ dumpPackages(getActivePackages(), packageName, ipw);
ipw.println("Inactive APEX packages:");
- dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
+ dumpPackages(getInactivePackages(), packageName, ipw);
ipw.println("Factory APEX packages:");
- dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
+ dumpPackages(getFactoryPackages(), packageName, ipw);
}
@GuardedBy("mLock")
@@ -370,7 +369,7 @@ class ApexPackageInfo {
* only information about that specific package will be dumped.
* @param ipw the {@link IndentingPrintWriter} object to send information to.
*/
- private static void dumpFromPackagesCache(List<PackageInfo> packagesCache,
+ static void dumpPackages(List<PackageInfo> packagesCache,
@Nullable String packageName, IndentingPrintWriter ipw) {
ipw.println();
ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 45a139dfb28f..a53e9be51b5f 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -984,9 +984,15 @@ public class ComputerEngine implements Computer {
TAG, "getApplicationInfo " + packageName
+ ": " + p);
}
+ final boolean matchApex = (flags & MATCH_APEX) != 0;
if (p != null) {
PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null) return null;
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!matchApex && p.isApex()) {
+ return null;
+ }
+ }
if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
return null;
}
@@ -1001,20 +1007,22 @@ public class ComputerEngine implements Computer {
}
return ai;
}
- if ((flags & PackageManager.MATCH_APEX) != 0) {
- // For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo
- // returned from getApplicationInfo, but for APEX packages difference shouldn't be
- // very big.
- // TODO(b/155328545): generate proper application info for APEXes as well.
- int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE;
- if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- apexFlags = ApexManager.MATCH_FACTORY_PACKAGE;
- }
- final PackageInfo pi = mApexPackageInfo.getPackageInfo(packageName, apexFlags);
- if (pi == null) {
- return null;
+ if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (matchApex) {
+ // For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo
+ // returned from getApplicationInfo, but for APEX packages difference shouldn't be
+ // very big.
+ // TODO(b/155328545): generate proper application info for APEXes as well.
+ int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE;
+ if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+ apexFlags = ApexManager.MATCH_FACTORY_PACKAGE;
+ }
+ final PackageInfo pi = mApexPackageInfo.getPackageInfo(packageName, apexFlags);
+ if (pi == null) {
+ return null;
+ }
+ return pi.applicationInfo;
}
- return pi.applicationInfo;
}
if ("android".equals(packageName) || "system".equals(packageName)) {
return androidApplication();
@@ -1704,14 +1712,22 @@ public class ComputerEngine implements Computer {
packageName = resolveInternalPackageName(packageName, versionCode);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
+ final boolean matchApex = (flags & MATCH_APEX) != 0;
if (matchFactoryOnly) {
// Instant app filtering for APEX modules is ignored
- if ((flags & MATCH_APEX) != 0) {
- return mApexPackageInfo.getPackageInfo(packageName,
- ApexManager.MATCH_FACTORY_PACKAGE);
+ if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (matchApex) {
+ return mApexPackageInfo.getPackageInfo(packageName,
+ ApexManager.MATCH_FACTORY_PACKAGE);
+ }
}
final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName);
if (ps != null) {
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!matchApex && ps.getPkg() != null && ps.getPkg().isApex()) {
+ return null;
+ }
+ }
if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
return null;
}
@@ -1731,6 +1747,11 @@ public class ComputerEngine implements Computer {
}
if (p != null) {
final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!matchApex && p.isApex()) {
+ return null;
+ }
+ }
if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
return null;
}
@@ -1751,8 +1772,11 @@ public class ComputerEngine implements Computer {
}
return generatePackageInfo(ps, flags, userId);
}
- if ((flags & MATCH_APEX) != 0) {
- return mApexPackageInfo.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (matchApex) {
+ return mApexPackageInfo.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ }
}
return null;
}
@@ -1809,6 +1833,11 @@ public class ComputerEngine implements Computer {
ps = psDisabled;
}
}
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!listApex && ps.getPkg() != null && ps.getPkg().isApex()) {
+ continue;
+ }
+ }
if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
continue;
}
@@ -1834,6 +1863,11 @@ public class ComputerEngine implements Computer {
ps = psDisabled;
}
}
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!listApex && p.isApex()) {
+ continue;
+ }
+ }
if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
continue;
}
@@ -1846,11 +1880,13 @@ public class ComputerEngine implements Computer {
}
}
}
- if (listApex) {
- if (listFactory) {
- list.addAll(mApexPackageInfo.getFactoryPackages());
- } else {
- list.addAll(mApexPackageInfo.getActivePackages());
+ if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (listApex) {
+ if (listFactory) {
+ list.addAll(mApexPackageInfo.getFactoryPackages());
+ } else {
+ list.addAll(mApexPackageInfo.getActivePackages());
+ }
}
}
return new ParceledListSlice<>(list);
@@ -3338,13 +3374,58 @@ public class ComputerEngine implements Computer {
case DumpState.DUMP_APEX: {
if (packageName == null || isApexPackage(packageName)) {
mApexManager.dump(pw);
- mApexPackageInfo.dump(pw, packageName);
+ dumpApex(pw, packageName);
}
break;
}
} // switch
}
+ private void generateApexPackageInfo(List<PackageInfo> activePackages,
+ List<PackageInfo> inactivePackages, List<PackageInfo> factoryPackages) {
+ for (AndroidPackage p : mPackages.values()) {
+ final String packageName = p.getPackageName();
+ PackageStateInternal ps = mSettings.getPackage(packageName);
+ if (!p.isApex() || ps == null) {
+ continue;
+ }
+ PackageInfo pi = generatePackageInfo(ps, 0, 0);
+ if (pi == null) {
+ continue;
+ }
+ pi.isActiveApex = true;
+ activePackages.add(pi);
+ if (!ps.isUpdatedSystemApp()) {
+ factoryPackages.add(pi);
+ } else {
+ PackageStateInternal psDisabled = mSettings.getDisabledSystemPkg(packageName);
+ pi = generatePackageInfo(psDisabled, 0, 0);
+ if (pi != null) {
+ factoryPackages.add(pi);
+ inactivePackages.add(pi);
+ }
+ }
+ }
+ }
+
+ private void dumpApex(PrintWriter pw, String packageName) {
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ List<PackageInfo> activePackages = new ArrayList<>();
+ List<PackageInfo> inactivePackages = new ArrayList<>();
+ List<PackageInfo> factoryPackages = new ArrayList<>();
+ generateApexPackageInfo(activePackages, inactivePackages, factoryPackages);
+ ipw.println("Active APEX packages:");
+ ApexPackageInfo.dumpPackages(activePackages, packageName, ipw);
+ ipw.println("Inactive APEX packages:");
+ ApexPackageInfo.dumpPackages(inactivePackages, packageName, ipw);
+ ipw.println("Factory APEX packages:");
+ ApexPackageInfo.dumpPackages(factoryPackages, packageName, ipw);
+ } else {
+ mApexPackageInfo.dump(pw, packageName);
+ }
+ }
+
// The body of findPreferredActivity.
protected PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityBody(
Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
@@ -3721,7 +3802,12 @@ public class ComputerEngine implements Computer {
@Override
public boolean isApexPackage(String packageName) {
- return mApexPackageInfo.isApexPackage(packageName);
+ if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ return mApexPackageInfo.isApexPackage(packageName);
+ } else {
+ final AndroidPackage pkg = mPackages.get(packageName);
+ return pkg != null && pkg.isApex();
+ }
}
@Override
@@ -4710,6 +4796,7 @@ public class ComputerEngine implements Computer {
if (!mUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForApplication(flags, userId);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
+ final boolean listApex = (flags & MATCH_APEX) != 0;
enforceCrossUserPermission(
callingUid,
@@ -4730,6 +4817,11 @@ public class ComputerEngine implements Computer {
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
if (ps.getPkg() != null) {
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!listApex && ps.getPkg().isApex()) {
+ continue;
+ }
+ }
if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
continue;
}
@@ -4758,6 +4850,11 @@ public class ComputerEngine implements Computer {
if (pkg == null) {
continue;
}
+ if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
+ if (!listApex && pkg.isApex()) {
+ continue;
+ }
+ }
if (filterSharedLibPackage(packageState, Binder.getCallingUid(), userId, flags)) {
continue;
}
@@ -5109,7 +5206,7 @@ public class ComputerEngine implements Computer {
final PackageStateInternal ps = mSettings.getPackage(packageName);
// Installer info for Apex is not stored in PackageManager
- if (ps == null && mApexPackageInfo.isApexPackage(packageName)) {
+ if (isApexPackage(packageName)) {
return InstallSource.EMPTY;
}
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 4019c15ee550..b142ba6822d9 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -194,7 +194,6 @@ final class InitAppsHelper {
apexScanResults = mInstallPackageHelper.scanApexPackages(
mApexManager.getAllApexInfos(), mSystemParseFlags, mSystemScanFlags,
packageParser, mExecutorService);
- mApexPackageInfo.notifyScanResult(apexScanResults);
} else {
apexScanResults = mApexPackageInfo.scanApexPackages(
mApexManager.getAllApexInfos(), packageParser, mExecutorService);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7dfa7299ae32..22189ff73bb3 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -343,7 +343,10 @@ final class InstallPackageHelper {
mPm.mTransferredPackages.add(pkg.getPackageName());
}
- if (reconciledPkg.mCollectedSharedLibraryInfos != null) {
+ if (reconciledPkg.mCollectedSharedLibraryInfos != null
+ || (oldPkgSetting != null && oldPkgSetting.getUsesLibraryInfos() != null)) {
+ // Reconcile if the new package or the old package uses shared libraries.
+ // It is possible that the old package uses shared libraries but the new one doesn't.
mSharedLibraries.executeSharedLibrariesUpdateLPw(pkg, pkgSetting, null, null,
reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
}
@@ -826,19 +829,24 @@ final class InstallPackageHelper {
}
return;
}
+
+ processApkInstallRequests(success, installRequests);
+ }
+
+ private void processApkInstallRequests(boolean success, List<InstallRequest> installRequests) {
if (success) {
- for (InstallRequest request : apkInstallRequests) {
+ for (InstallRequest request : installRequests) {
request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
}
synchronized (mPm.mInstallLock) {
- installPackagesTracedLI(apkInstallRequests);
+ installPackagesTracedLI(installRequests);
}
- for (InstallRequest request : apkInstallRequests) {
+ for (InstallRequest request : installRequests) {
request.mArgs.doPostInstall(
request.mInstallResult.mReturnCode, request.mInstallResult.mUid);
}
}
- for (InstallRequest request : apkInstallRequests) {
+ for (InstallRequest request : installRequests) {
restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
request.mInstallResult,
new PostInstallData(request.mArgs,
@@ -880,11 +888,15 @@ final class InstallPackageHelper {
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
ApexInfo apexInfo = mApexManager.installPackage(apexes[0]);
if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
- ParsedPackage parsedPackage = packageParser.parsePackage(
- new File(apexInfo.modulePath), 0, /* useCaches= */ false);
- scanSystemPackageLI(parsedPackage, 0, SCAN_AS_APEX, null);
- mPm.mApexPackageInfo.notifyPackageInstalled(
- apexInfo, parsedPackage.hideAsFinal());
+ // APEX has been handled successfully by apexd. Let's continue the install flow
+ // so it will be scanned and registered with the system.
+ // TODO(b/225756739): Improve atomicity of rebootless APEX install.
+ // The newly installed APEX will not be reverted even if
+ // processApkInstallRequests() fails. Need a way to keep info stored in apexd
+ // and PMS in sync in the face of install failures.
+ request.mInstallResult.mApexInfo = apexInfo;
+ mPm.mHandler.post(() -> processApkInstallRequests(true, requests));
+ return;
} else {
mPm.mApexPackageInfo.notifyPackageInstalled(apexInfo, packageParser);
}
@@ -985,7 +997,12 @@ final class InstallPackageHelper {
+ PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
return;
}
- createdAppId.put(packageName, optimisticallyRegisterAppId(result));
+ final boolean isApex = (result.mRequest.mScanFlags & SCAN_AS_APEX) != 0;
+ if (!isApex) {
+ createdAppId.put(packageName, optimisticallyRegisterAppId(result));
+ } else {
+ result.mPkgSetting.setAppId(Process.INVALID_UID);
+ }
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
} catch (PackageManagerException e) {
@@ -1088,12 +1105,12 @@ final class InstallPackageHelper {
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.mInstallFlags;
- final File tmpPackageFile = new File(args.getCodePath());
final boolean onExternal = args.mVolumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean virtualPreload =
((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.mMoveInfo != null) {
@@ -1112,7 +1129,12 @@ final class InstallPackageHelper {
if (virtualPreload) {
scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
+ if (isApex) {
+ scanFlags |= SCAN_AS_APEX;
+ }
+ final File tmpPackageFile = new File(
+ isApex ? res.mApexInfo.modulePath : args.getCodePath());
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Validity check
@@ -1518,16 +1540,22 @@ final class InstallPackageHelper {
}
}
- if (!args.doRename(res.mReturnCode, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
- }
+ if (!isApex) {
+ if (!args.doRename(res.mReturnCode, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
+ }
- try {
- setUpFsVerityIfPossible(parsedPackage);
- } catch (Installer.InstallerException | IOException | DigestException
- | NoSuchAlgorithmException e) {
- throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed to set up verity: " + e);
+ try {
+ setUpFsVerityIfPossible(parsedPackage);
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to set up verity: " + e);
+ }
+ } else {
+ // Use the path returned by apexd
+ parsedPackage.setPath(res.mApexInfo.modulePath);
+ parsedPackage.setBaseApkPath(res.mApexInfo.modulePath);
}
final PackageFreezer freezer =
@@ -1934,6 +1962,8 @@ final class InstallPackageHelper {
// Set the update and install times
PackageStateInternal deletedPkgSetting = mPm.snapshotComputer()
.getPackageStateInternal(oldPackage.getPackageName());
+ // TODO(b/225756739): For rebootless APEX, consider using lastUpdateMillis provided
+ // by apexd to be more accurate.
reconciledPkg.mPkgSetting
.setFirstInstallTimeFromReplaced(deletedPkgSetting, request.mAllUsers)
.setLastUpdateTime(System.currentTimeMillis());
@@ -2236,6 +2266,8 @@ final class InstallPackageHelper {
for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
& SCAN_AS_INSTANT_APP) != 0);
+ final boolean isApex = ((reconciledPkg.mScanResult.mRequest.mScanFlags
+ & SCAN_AS_APEX) != 0);
final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
final String packageName = pkg.getPackageName();
final String codePath = pkg.getPath();
@@ -2323,7 +2355,8 @@ final class InstallPackageHelper {
android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental)
- && dexoptOptions.isCompilationEnabled();
+ && dexoptOptions.isCompilationEnabled()
+ && !isApex;
if (performDexopt) {
// Compile the layout resources.
@@ -3292,6 +3325,10 @@ final class InstallPackageHelper {
final PackageSetting disabledPs =
mPm.mSettings.getDisabledSystemPkgLPr(packageName);
if (scannedPkg != null) {
+ if (scannedPkg.isApex()) {
+ // APEX on /data has been scanned. No need to expect better.
+ continue;
+ }
/*
* If the system app is both scanned and in the
* disabled packages list, then it must have been
@@ -3391,6 +3428,18 @@ final class InstallPackageHelper {
}
}
+ /**
+ * Scans APEX packages and registers them with the system.
+ *
+ * apexd has its own policy to decide which APEX to activate and which not. The policy might
+ * conflicts that of PMS. The APEX package info stored in PMS is a mirror of that managed by
+ * apexd. To keep things simple and keep activation status in sync for both apexd and PMS, we
+ * don't persist APEX in settings and always scan APEX from scratch during boot. However, some
+ * data like lastUpdateTime will be lost if PackageSetting is not persisted for APEX.
+ *
+ * TODO(b/225756739): Read lastUpdateTime from ApexInfoList to populate PackageSetting correctly
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public List<ApexManager.ScanResult> scanApexPackages(ApexInfo[] allPackages, int parseFlags,
int scanFlags, PackageParser2 packageParser, ExecutorService executorService) {
if (allPackages == null) {
@@ -3408,18 +3457,39 @@ final class InstallPackageHelper {
parsingApexInfo.put(apexFile, ai);
}
- // Process results one by one
- List<ApexManager.ScanResult> results = new ArrayList<>(parsingApexInfo.size());
+ List<ParallelPackageParser.ParseResult> parseResults =
+ new ArrayList<>(parsingApexInfo.size());
for (int i = 0; i < parsingApexInfo.size(); i++) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+ parseResults.add(parseResult);
+ }
+ // Sort the list to ensure we always process factory packages first
+ Collections.sort(parseResults, (a, b) -> {
+ ApexInfo ai = parsingApexInfo.get(a.scanFile);
+ return ai.isFactory ? -1 : 1;
+ });
+
+
+ // Process results one by one
+ List<ApexManager.ScanResult> results = new ArrayList<>(parsingApexInfo.size());
+ for (int i = 0; i < parseResults.size(); i++) {
+ ParallelPackageParser.ParseResult parseResult = parseResults.get(i);
Throwable throwable = parseResult.throwable;
ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
+ int newParseFlags = parseFlags;
int newScanFlags = scanFlags | SCAN_AS_APEX;
+ if (!ai.isFactory) {
+ newParseFlags &= ~ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
+ newScanFlags |= SCAN_NEW_INSTALL;
+ }
if (throwable == null) {
try {
- scanSystemPackageLI(parseResult.parsedPackage, parseFlags, newScanFlags, null);
- AndroidPackage pkg = parseResult.parsedPackage.hideAsFinal();
+ AndroidPackage pkg = addForInitLI(
+ parseResult.parsedPackage, newParseFlags, newScanFlags, null);
+ if (ai.isFactory && !ai.isActive) {
+ disableSystemPackageLPw(pkg);
+ }
results.add(new ApexManager.ScanResult(ai, pkg, pkg.getPackageName()));
} catch (PackageManagerException e) {
throw new IllegalStateException("Failed to scan: " + ai.modulePath, e);
@@ -3638,7 +3708,11 @@ final class InstallPackageHelper {
ReconcilePackageUtils.reconcilePackages(reconcileRequest,
mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
mPm.mSettings);
- appIdCreated = optimisticallyRegisterAppId(scanResult);
+ if ((scanFlags & SCAN_AS_APEX) == 0) {
+ appIdCreated = optimisticallyRegisterAppId(scanResult);
+ } else {
+ scanResult.mPkgSetting.setAppId(Process.INVALID_UID);
+ }
commitReconciledScanResultLocked(reconcileResult.get(pkgName),
mPm.mUserManager.getUserIds());
} catch (PackageManagerException e) {
diff --git a/services/core/java/com/android/server/pm/PackageInstalledInfo.java b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
index d0ca9d845560..1c25dbbfa313 100644
--- a/services/core/java/com/android/server/pm/PackageInstalledInfo.java
+++ b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static com.android.server.pm.PackageManagerService.TAG;
+import android.apex.ApexInfo;
import android.util.ExceptionUtils;
import android.util.Slog;
@@ -45,6 +46,9 @@ final class PackageInstalledInfo {
String mOrigPackage;
String mOrigPermission;
+ // The ApexInfo returned by ApexManager#installPackage, used by rebootless APEX install
+ ApexInfo mApexInfo;
+
PackageInstalledInfo(int currentStatus) {
mReturnCode = currentStatus;
mUid = -1;
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 9befd6e09eb4..0ca5febd0d99 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -48,7 +48,6 @@ import android.util.Xml;
import com.android.internal.util.ArrayUtils;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import org.xmlpull.v1.XmlPullParser;
@@ -595,11 +594,7 @@ final class PreferredActivityHelper {
synchronized (mPm.mLock) {
mPm.mSettings.applyDefaultPreferredAppsLPw(userId);
mPm.mDomainVerificationManager.clearUser(userId);
- final int numPackages = mPm.mPackages.size();
- for (int i = 0; i < numPackages; i++) {
- final AndroidPackage pkg = mPm.mPackages.valueAt(i);
- mPm.mPermissionManager.resetRuntimePermissions(pkg, userId);
- }
+ mPm.mPermissionManager.resetRuntimePermissionsForUser(userId);
}
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
resetNetworkPolicies(userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a4db628d7290..ad303311b1c5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2468,10 +2468,18 @@ public final class Settings implements Watchable, Snappable {
serializer.endTag(null, "permissions");
for (final PackageSetting pkg : mPackages.values()) {
+ if (pkg.getPkg() != null && pkg.getPkg().isApex()) {
+ // Don't persist APEX which doesn't have a valid app id and will fail to load
+ continue;
+ }
writePackageLPr(serializer, pkg);
}
for (final PackageSetting pkg : mDisabledSysPackages.values()) {
+ if (pkg.getPkg() != null && pkg.getPkg().isApex()) {
+ // Don't persist APEX which doesn't have a valid app id and will fail to load
+ continue;
+ }
writeDisabledSysPackageLPr(serializer, pkg);
}
@@ -4968,6 +4976,10 @@ public final class Settings implements Watchable, Snappable {
&& !packageName.equals(ps.getPackageName())) {
continue;
}
+ if (ps.getPkg() != null && ps.getPkg().isApex()) {
+ // Filter APEX packages which will be dumped in the APEX section
+ continue;
+ }
final LegacyPermissionState permissionsState =
mPermissionDataProvider.getLegacyPermissionState(ps.getAppId());
if (permissionNames != null
@@ -5020,6 +5032,10 @@ public final class Settings implements Watchable, Snappable {
&& !packageName.equals(ps.getPackageName())) {
continue;
}
+ if (ps.getPkg() != null && ps.getPkg().isApex()) {
+ // Filter APEX packages which will be dumped in the APEX section
+ continue;
+ }
if (!checkin && !printedSomething) {
if (dumpState.onTitlePrinted())
pw.println();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index edbfa1aa00e0..367beb50473f 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -780,6 +780,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) {
mPermissionManagerServiceImpl.resetRuntimePermissions(pkg, userId);
}
+ @Override
+ public void resetRuntimePermissionsForUser(@UserIdInt int userId) {
+ mPermissionManagerServiceImpl.resetRuntimePermissionsForUser(userId);
+ }
@Override
public Permission getPermissionTEMP(String permName) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index bd3fc50473de..13ea9f377a39 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -103,7 +103,6 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
-import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -1661,30 +1660,16 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
/**
* Reverts user permission state changes (permissions and flags).
*
- * @param pkg The package for which to reset.
+ * @param filterPkg The package for which to reset, or {@code null} for all packages.
* @param userId The device user for which to do a reset.
*/
- private void resetRuntimePermissionsInternal(@NonNull AndroidPackage pkg,
+ private void resetRuntimePermissionsInternal(@Nullable AndroidPackage filterPkg,
@UserIdInt int userId) {
- final String packageName = pkg.getPackageName();
-
- // These are flags that can change base on user actions.
- final int userSettableMask = FLAG_PERMISSION_USER_SET
- | FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_REVOKED_COMPAT
- | FLAG_PERMISSION_REVIEW_REQUIRED
- | FLAG_PERMISSION_ONE_TIME
- | FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
-
- final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
- | FLAG_PERMISSION_POLICY_FIXED;
-
// Delay and combine non-async permission callbacks
- final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
final boolean[] permissionRemoved = new boolean[1];
final ArraySet<Long> revokedPermissions = new ArraySet<>();
- final IntArray syncUpdatedUsers = new IntArray(permissionCount);
- final IntArray asyncUpdatedUsers = new IntArray(permissionCount);
+ final ArraySet<Integer> syncUpdatedUsers = new ArraySet<>();
+ final ArraySet<Integer> asyncUpdatedUsers = new ArraySet<>();
PermissionCallback delayingPermCallback = new PermissionCallback() {
public void onGidsChanged(int appId, int userId) {
@@ -1746,6 +1731,55 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
};
+ if (filterPkg != null) {
+ resetRuntimePermissionsInternal(filterPkg, userId, delayingPermCallback);
+ } else {
+ mPackageManagerInt.forEachPackage(pkg ->
+ resetRuntimePermissionsInternal(pkg, userId, delayingPermCallback));
+ }
+
+ // Execute delayed callbacks
+ if (permissionRemoved[0]) {
+ mDefaultPermissionCallback.onPermissionRemoved();
+ }
+
+ // Slight variation on the code in mPermissionCallback.onPermissionRevoked() as we cannot
+ // kill uid while holding mPackages-lock
+ if (!revokedPermissions.isEmpty()) {
+ int numRevokedPermissions = revokedPermissions.size();
+ for (int i = 0; i < numRevokedPermissions; i++) {
+ int revocationUID = IntPair.first(revokedPermissions.valueAt(i));
+ int revocationUserId = IntPair.second(revokedPermissions.valueAt(i));
+
+ mOnPermissionChangeListeners.onPermissionsChanged(revocationUID);
+
+ // Kill app later as we are holding mPackages
+ mHandler.post(() -> killUid(UserHandle.getAppId(revocationUID), revocationUserId,
+ KILL_APP_REASON_PERMISSIONS_REVOKED));
+ }
+ }
+
+ mPackageManagerInt.writePermissionSettings(ArrayUtils.convertToIntArray(syncUpdatedUsers),
+ false);
+ mPackageManagerInt.writePermissionSettings(ArrayUtils.convertToIntArray(asyncUpdatedUsers),
+ true);
+ }
+
+ private void resetRuntimePermissionsInternal(@NonNull AndroidPackage pkg,
+ @UserIdInt int userId, @NonNull PermissionCallback delayingPermCallback) {
+ // These are flags that can change base on user actions.
+ final int userSettableMask = FLAG_PERMISSION_USER_SET
+ | FLAG_PERMISSION_USER_FIXED
+ | FLAG_PERMISSION_REVOKED_COMPAT
+ | FLAG_PERMISSION_REVIEW_REQUIRED
+ | FLAG_PERMISSION_ONE_TIME
+ | FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
+
+ final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
+ | FLAG_PERMISSION_POLICY_FIXED;
+
+ final String packageName = pkg.getPackageName();
+ final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
for (int i = 0; i < permissionCount; i++) {
final String permName = pkg.getRequestedPermissions().get(i);
@@ -1824,30 +1858,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
userId, null, delayingPermCallback);
}
}
-
- // Execute delayed callbacks
- if (permissionRemoved[0]) {
- mDefaultPermissionCallback.onPermissionRemoved();
- }
-
- // Slight variation on the code in mPermissionCallback.onPermissionRevoked() as we cannot
- // kill uid while holding mPackages-lock
- if (!revokedPermissions.isEmpty()) {
- int numRevokedPermissions = revokedPermissions.size();
- for (int i = 0; i < numRevokedPermissions; i++) {
- int revocationUID = IntPair.first(revokedPermissions.valueAt(i));
- int revocationUserId = IntPair.second(revokedPermissions.valueAt(i));
-
- mOnPermissionChangeListeners.onPermissionsChanged(revocationUID);
-
- // Kill app later as we are holding mPackages
- mHandler.post(() -> killUid(UserHandle.getAppId(revocationUID), revocationUserId,
- KILL_APP_REASON_PERMISSIONS_REVOKED));
- }
- }
-
- mPackageManagerInt.writePermissionSettings(syncUpdatedUsers.toArray(), false);
- mPackageManagerInt.writePermissionSettings(asyncUpdatedUsers.toArray(), true);
}
/**
@@ -5178,6 +5188,12 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
+ public void resetRuntimePermissionsForUser(@UserIdInt int userId) {
+ Preconditions.checkArgumentNonNegative(userId, "userId");
+ resetRuntimePermissionsInternal(null, userId);
+ }
+
+ @Override
public Permission getPermissionTEMP(String permName) {
synchronized (mLock) {
return mRegistry.getPermission(permName);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 3771f030aefa..02d184e2b1e0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -428,6 +428,13 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
@UserIdInt int userId);
/**
+ * Reset the runtime permission state changes for all packages in a user.
+ *
+ * @param userId the user ID
+ */
+ void resetRuntimePermissionsForUser(@UserIdInt int userId);
+
+ /**
* Read legacy permission state from package settings.
*
* TODO(zhanghai): This is a temporary method because we should not expose
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index d2c4ec4cc5a5..67d51813778c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -102,6 +102,14 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter
@UserIdInt int userId);
/**
+ * Reset the runtime permission state changes for all packages in a user.
+ *
+ * @param userId the user ID
+ */
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void resetRuntimePermissionsForUser(@UserIdInt int userId);
+
+ /**
* Read legacy permission state from package settings.
*
* TODO(zhanghai): This is a temporary method because we should not expose
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f5df738c8848..e700103bf0d2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4450,14 +4450,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* @return Whether we are allowed to show non-starting windows at the moment. We disallow
- * showing windows during transitions in case we have windows that have wide-color-gamut
- * color mode set to avoid jank in the middle of the transition.
+ * showing windows while the transition animation is playing in case we have windows
+ * that have wide-color-gamut color mode set to avoid jank in the middle of the
+ * animation.
*/
boolean canShowWindows() {
final boolean drawn = mTransitionController.isShellTransitionsEnabled()
? mSyncState != SYNC_STATE_WAITING_FOR_DRAW : allDrawn;
final boolean animating = mTransitionController.isShellTransitionsEnabled()
- ? mTransitionController.inTransition(this)
+ ? mTransitionController.inPlayingTransition(this)
: isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
return drawn && !(animating && hasNonDefaultColorWindow());
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index e5ec4c3dfcd2..a78d25f4e21a 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1522,8 +1522,8 @@ public class DisplayRotation {
}
@Override
- public boolean isKeyguardLocked() {
- return mService.isKeyguardLocked();
+ public boolean isKeyguardShowingAndNotOccluded() {
+ return mService.isKeyguardShowingAndNotOccluded();
}
@Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6b3981a0c1cc..61ed6d3d8d8f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4567,22 +4567,12 @@ class Task extends TaskFragment {
moveToFront(reason, null);
}
- void moveToFront(String reason, Task task) {
- if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {
- final Task adjacentTask = getAdjacentTaskFragment().asTask();
- if (adjacentTask != null) {
- adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */);
- }
- }
- moveToFrontInner(reason, task);
- }
-
/**
* @param reason The reason for moving the root task to the front.
* @param task If non-null, the task will be moved to the top of the root task.
*/
@VisibleForTesting
- void moveToFrontInner(String reason, Task task) {
+ void moveToFront(String reason, Task task) {
if (!isAttached()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index a9e80af04e1c..d9835722130f 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -29,7 +29,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -91,20 +90,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
*/
private int mColorLayerCounter = 0;
- /**
- * Given that the split-screen divider does not have an AppWindowToken, it
- * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
- * it will need to be interleaved with some of our children, appearing on top of
- * both docked root tasks but underneath any assistant root tasks.
- *
- * To solve this problem we have this anchor control, which will always exist so
- * we can always assign it the correct value in our {@link #assignChildLayers}.
- * Likewise since it always exists, we can always
- * assign the divider a layer relative to it. This way we prevent linking lifecycle
- * events between tasks and the divider window.
- */
- private SurfaceControl mSplitScreenDividerAnchor;
-
// Cached reference to some special tasks we tend to get a lot so we don't need to loop
// through the list to find them.
private Task mRootHomeTask;
@@ -730,12 +715,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Place root home tasks to the bottom.
layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer);
- // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
- // make app pair split only have single root then we can just attach the
- // divider to the single root task in shell.
- layer = Math.max(layer, SPLIT_DIVIDER_LAYER + 1);
adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
- t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
}
/**
@@ -763,19 +743,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
continue;
}
- final Task childTask = child.asTask();
- final boolean inAdjacentTask = childTask != null
- && child.inMultiWindowMode()
- && childTask.getRootTask().getAdjacentTaskFragment() != null;
-
- if (inAdjacentTask) {
- hasAdjacentTask = true;
- } else if (hasAdjacentTask && startLayer < SPLIT_DIVIDER_LAYER) {
- // Task on top of adjacent tasks should be higher than split divider layer so
- // set it as start.
- startLayer = SPLIT_DIVIDER_LAYER + 1;
- }
-
child.assignLayer(t, startLayer++);
}
@@ -802,31 +769,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
return activity != null ? activity.createRemoteAnimationTarget(record) : null;
}
- SurfaceControl getSplitScreenDividerAnchor() {
- return mSplitScreenDividerAnchor;
- }
-
- @Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- if (getParent() != null) {
- super.onParentChanged(newParent, oldParent, () -> {
- mSplitScreenDividerAnchor = makeChildSurface(null)
- .setName("splitScreenDividerAnchor")
- .setCallsite("TaskDisplayArea.onParentChanged")
- .build();
-
- getSyncTransaction()
- .show(mSplitScreenDividerAnchor);
- });
- } else {
- super.onParentChanged(newParent, oldParent);
- mWmService.mTransactionFactory.get()
- .remove(mSplitScreenDividerAnchor)
- .apply();
- mSplitScreenDividerAnchor = null;
- }
- }
-
void setBackgroundColor(@ColorInt int colorInt) {
setBackgroundColor(colorInt, false /* restore */);
}
@@ -872,12 +814,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
setBackgroundColor(mBackgroundColor, true /* restore */);
}
- if (mSplitScreenDividerAnchor == null) {
- return;
- }
-
- // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
- t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
reassignLayer(t);
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index cb090ca77fa8..470e835ddfc5 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -173,14 +173,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private TaskFragment mAdjacentTaskFragment;
/**
- * Whether to move adjacent task fragment together when re-positioning.
- *
- * @see #mAdjacentTaskFragment
- */
- // TODO(b/207185041): Remove this once having a single-top root for split screen.
- boolean mMoveAdjacentTogether;
-
- /**
* Prevents duplicate calls to onTaskAppeared.
*/
boolean mTaskFragmentAppearedSent;
@@ -332,15 +324,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return service.mWindowOrganizerController.getTaskFragment(token);
}
- void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment, boolean moveTogether) {
+ void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
if (mAdjacentTaskFragment == taskFragment) {
return;
}
resetAdjacentTaskFragment();
if (taskFragment != null) {
mAdjacentTaskFragment = taskFragment;
- mMoveAdjacentTogether = moveTogether;
- taskFragment.setAdjacentTaskFragment(this, moveTogether);
+ taskFragment.setAdjacentTaskFragment(this);
}
}
@@ -349,11 +340,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
- mAdjacentTaskFragment.mMoveAdjacentTogether = false;
}
mAdjacentTaskFragment = null;
mDelayLastActivityRemoval = false;
- mMoveAdjacentTogether = false;
}
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3af372076b78..93425800e25e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -131,18 +131,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*/
private static final int STATE_ABORT = 3;
+ /**
+ * This transition has finished playing successfully.
+ */
+ private static final int STATE_FINISHED = 4;
+
@IntDef(prefix = { "STATE_" }, value = {
STATE_PENDING,
STATE_COLLECTING,
STATE_STARTED,
STATE_PLAYING,
- STATE_ABORT
+ STATE_ABORT,
+ STATE_FINISHED
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionState {}
final @TransitionType int mType;
private int mSyncId = -1;
+ // Used for tracking a Transition throughout a lifecycle (i.e. from STATE_COLLECTING to
+ // STATE_FINISHED or STATE_ABORT), and should only be used for testing and debugging.
+ private int mDebugId = -1;
private @TransitionFlags int mFlags;
private final TransitionController mController;
private final BLASTSyncEngine mSyncEngine;
@@ -202,6 +211,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFlags = flags;
mController = controller;
mSyncEngine = syncEngine;
+
+ controller.mTransitionTracer.logState(this);
}
void addFlag(int flag) {
@@ -242,11 +253,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
}
+ @TransitionState
+ int getState() {
+ return mState;
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
}
+ @VisibleForTesting
+ int getDebugId() {
+ return mDebugId;
+ }
+
@TransitionFlags
int getFlags() {
return mFlags;
@@ -259,6 +280,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
mState = STATE_COLLECTING;
mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
+ mDebugId = mSyncId;
+
+ mController.mTransitionTracer.logState(this);
}
/**
@@ -276,6 +300,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
mSyncId);
applyReady();
+
+ mController.mTransitionTracer.logState(this);
}
/**
@@ -437,14 +463,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
final Rect clipRect;
// No need to clip the display in case seeing the clipped content when during the
- // display rotation.
- if (target.asDisplayContent() != null) {
+ // display rotation. No need to clip activities because they rely on clipping on
+ // task layers.
+ if (target.asDisplayContent() != null || target.asActivityRecord() != null) {
clipRect = null;
- } else if (target.asActivityRecord() != null) {
- // Always use parent bounds of activity because letterbox area (e.g. fixed
- // aspect ratio or size compat mode) should be included.
- clipRect = target.getParent().getRequestedOverrideBounds();
- clipRect.offset(-tmpPos.x, -tmpPos.y);
} else {
clipRect = target.getRequestedOverrideBounds();
clipRect.offset(-tmpPos.x, -tmpPos.y);
@@ -648,6 +670,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
}
+
+ mState = STATE_FINISHED;
+ mController.mTransitionTracer.logState(this);
}
void abort() {
@@ -677,6 +702,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
return;
}
+
if (mTargetDisplays.isEmpty()) {
mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay());
}
@@ -788,6 +814,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
mStartTransaction = transaction;
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
+
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
mController.dispatchLegacyAppTransitionStarting(info);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6491d6bbe26e..2f41d5b3582a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -75,6 +75,7 @@ class TransitionController {
private ITransitionPlayer mTransitionPlayer;
final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter();
+ final TransitionTracer mTransitionTracer;
private IApplicationThread mTransitionPlayerThread;
final ActivityTaskManagerService mAtm;
@@ -100,10 +101,12 @@ class TransitionController {
final StatusBarManagerInternal mStatusBar;
TransitionController(ActivityTaskManagerService atm,
- TaskSnapshotController taskSnapshotController) {
+ TaskSnapshotController taskSnapshotController,
+ TransitionTracer transitionTracer) {
mAtm = atm;
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
mTaskSnapshotController = taskSnapshotController;
+ mTransitionTracer = transitionTracer;
mTransitionPlayerDeath = () -> {
synchronized (mAtm.mGlobalLock) {
// Clean-up/finish any playing transitions.
@@ -207,6 +210,18 @@ class TransitionController {
}
/**
+ * @return {@code true} if transition is actively collecting changes and `wc` is one of them
+ * or a descendant of one of them. {@code false} once playing.
+ */
+ boolean inCollectingTransition(@NonNull WindowContainer wc) {
+ if (!isCollecting()) return false;
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (mCollectingTransition.mParticipants.contains(p)) return true;
+ }
+ return false;
+ }
+
+ /**
* @return {@code true} if transition is actively playing. This is not necessarily {@code true}
* during collection.
*/
@@ -214,6 +229,18 @@ class TransitionController {
return !mPlayingTransitions.isEmpty();
}
+ /**
+ * @return {@code true} if one of the playing transitions contains `wc`.
+ */
+ boolean inPlayingTransition(@NonNull WindowContainer wc) {
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (mPlayingTransitions.get(i).mParticipants.contains(p)) return true;
+ }
+ }
+ return false;
+ }
+
/** @return {@code true} if a transition is running */
boolean inTransition() {
// TODO(shell-transitions): eventually properly support multiple
@@ -222,19 +249,7 @@ class TransitionController {
/** @return {@code true} if a transition is running in a participant subtree of wc */
boolean inTransition(@NonNull WindowContainer wc) {
- if (isCollecting()) {
- for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (isCollecting(p)) return true;
- }
- }
- for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
- for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (mPlayingTransitions.get(i).mParticipants.contains(p)) {
- return true;
- }
- }
- }
- return false;
+ return inCollectingTransition(wc) || inPlayingTransition(wc);
}
boolean inRecentsTransition(@NonNull WindowContainer wc) {
@@ -502,6 +517,7 @@ class TransitionController {
setAnimationRunning(true /* running */);
}
mPlayingTransitions.add(transition);
+ mTransitionTracer.logState(transition);
}
private void setAnimationRunning(boolean running) {
@@ -520,6 +536,7 @@ class TransitionController {
}
transition.abort();
mCollectingTransition = null;
+ mTransitionTracer.logState(transition);
}
/**
diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java
new file mode 100644
index 000000000000..192b9abc62a7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TransitionTracer.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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.wm;
+
+import static android.os.Build.IS_USER;
+
+import static com.android.server.wm.shell.ChangeInfo.CHANGE_FLAGS;
+import static com.android.server.wm.shell.ChangeInfo.HAS_CHANGED;
+import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE;
+import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER;
+import static com.android.server.wm.shell.Transition.CHANGE;
+import static com.android.server.wm.shell.Transition.FLAGS;
+import static com.android.server.wm.shell.Transition.ID;
+import static com.android.server.wm.shell.Transition.STATE;
+import static com.android.server.wm.shell.Transition.TIMESTAMP;
+import static com.android.server.wm.shell.Transition.TRANSITION_TYPE;
+import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER;
+import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_L;
+import static com.android.server.wm.shell.TransitionTraceProto.TRANSITION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.TraceBuffer;
+import com.android.server.wm.Transition.ChangeInfo;
+import com.android.server.wm.shell.TransitionTraceProto;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Helper class to collect and dump transition traces.
+ */
+public class TransitionTracer {
+
+ private static final String LOG_TAG = "TransitionTracer";
+
+ /**
+ * Maximum buffer size, currently defined as 5 MB
+ */
+ private static final int BUFFER_CAPACITY = 5120 * 1024; // 5 MB
+ static final String WINSCOPE_EXT = ".winscope";
+ private static final String TRACE_FILE = "/data/misc/wmtrace/transition_trace" + WINSCOPE_EXT;
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final TransitionTraceBuffer mTraceBuffer = new TransitionTraceBuffer();
+
+ private final Object mEnabledLock = new Object();
+ private volatile boolean mEnabled = false;
+
+ private long mTraceStartTimestamp;
+
+ private class TransitionTraceBuffer {
+ private final TraceBuffer mBuffer = new TraceBuffer(BUFFER_CAPACITY);
+
+ private void pushTransitionState(Transition transition) {
+ final ProtoOutputStream outputStream = new ProtoOutputStream();
+ final long transitionEntryToken = outputStream.start(TRANSITION);
+
+ outputStream.write(ID, transition.getDebugId());
+ outputStream.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+ outputStream.write(TRANSITION_TYPE, transition.mType);
+ outputStream.write(STATE, transition.getState());
+ outputStream.write(FLAGS, transition.getFlags());
+
+ for (int i = 0; i < transition.mChanges.size(); ++i) {
+ final WindowContainer window = transition.mChanges.keyAt(i);
+ final ChangeInfo changeInfo = transition.mChanges.valueAt(i);
+ writeChange(outputStream, window, changeInfo);
+ }
+
+ outputStream.end(transitionEntryToken);
+
+ mBuffer.add(outputStream);
+ }
+
+ private void writeChange(ProtoOutputStream outputStream, WindowContainer window,
+ ChangeInfo changeInfo) {
+ Trace.beginSection("TransitionProto#addChange");
+ final long changeEntryToken = outputStream.start(CHANGE);
+
+ final int transitMode = changeInfo.getTransitMode(window);
+ final boolean hasChanged = changeInfo.hasChanged(window);
+ final int changeFlags = changeInfo.getChangeFlags(window);
+
+ outputStream.write(TRANSIT_MODE, transitMode);
+ outputStream.write(HAS_CHANGED, hasChanged);
+ outputStream.write(CHANGE_FLAGS, changeFlags);
+ window.writeIdentifierToProto(outputStream, WINDOW_IDENTIFIER);
+
+ outputStream.end(changeEntryToken);
+ Trace.endSection();
+ }
+
+ public void writeToFile(File file, ProtoOutputStream proto) throws IOException {
+ mBuffer.writeTraceToFile(file, proto);
+ }
+
+ public void reset() {
+ mBuffer.resetBuffer();
+ }
+ }
+
+ /**
+ * Records the current state of a transition in the transition trace (if it is running).
+ * @param transition the transition that we want to record the state of.
+ */
+ public void logState(com.android.server.wm.Transition transition) {
+ if (!mEnabled) {
+ return;
+ }
+
+ Log.d(LOG_TAG, "Logging state of transition " + transition);
+ mTraceBuffer.pushTransitionState(transition);
+ }
+
+ /**
+ * Starts collecting transitions for the trace.
+ * If called while a trace is already running, this will reset the trace.
+ */
+ public void startTrace(@Nullable PrintWriter pw) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("TransitionTracer#startTrace");
+ LogAndPrintln.i(pw, "Starting shell transition trace.");
+ synchronized (mEnabledLock) {
+ mTraceStartTimestamp = SystemClock.elapsedRealtime();
+ mEnabled = true;
+ mTraceBuffer.reset();
+ }
+ Trace.endSection();
+ }
+
+ /**
+ * Stops collecting the transition trace and dump to trace to file.
+ *
+ * Dumps the trace to @link{TRACE_FILE}.
+ */
+ public void stopTrace(@Nullable PrintWriter pw) {
+ stopTrace(pw, new File(TRACE_FILE));
+ }
+
+ /**
+ * Stops collecting the transition trace and dump to trace to file.
+ * @param outputFile The file to dump the transition trace to.
+ */
+ public void stopTrace(@Nullable PrintWriter pw, File outputFile) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("TransitionTracer#stopTrace");
+ LogAndPrintln.i(pw, "Stopping shell transition trace.");
+ synchronized (mEnabledLock) {
+ if (!mEnabled) {
+ LogAndPrintln.e(pw,
+ "Error: Tracing can't be stopped because it hasn't been started.");
+ return;
+ }
+
+ mEnabled = false;
+ writeTraceToFileLocked(pw, outputFile);
+ }
+ Trace.endSection();
+ }
+
+ boolean isEnabled() {
+ return mEnabled;
+ }
+
+ private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) {
+ Trace.beginSection("TransitionTracer#writeTraceToFileLocked");
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.write(TransitionTraceProto.TIMESTAMP, mTraceStartTimestamp);
+ int pid = android.os.Process.myPid();
+ LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath()
+ + " from process " + pid);
+ mTraceBuffer.writeToFile(file, proto);
+ } catch (IOException e) {
+ LogAndPrintln.e(pw, "Unable to write buffer to file", e);
+ }
+ Trace.endSection();
+ }
+
+ private static class LogAndPrintln {
+ private static void i(@Nullable PrintWriter pw, String msg) {
+ Log.i(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+
+ private static void e(@Nullable PrintWriter pw, String msg) {
+ Log.e(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println("ERROR: " + msg);
+ pw.flush();
+ }
+ }
+
+ private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) {
+ Log.e(LOG_TAG, msg, e);
+ if (pw != null) {
+ pw.println("ERROR: " + msg + " ::\n " + e);
+ pw.flush();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 44cb4b900255..8b2b830e90e0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -460,6 +460,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
+ final TransitionTracer mTransitionTracer;
private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
@@ -1240,6 +1241,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
Choreographer.getInstance());
+ mTransitionTracer = new TransitionTracer();
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
@@ -5884,6 +5886,21 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void startTransitionTrace() {
+ mTransitionTracer.startTrace(null /* printwriter */);
+ }
+
+ @Override
+ public void stopTransitionTrace() {
+ mTransitionTracer.stopTrace(null /* printwriter */);
+ }
+
+ @Override
+ public boolean isTransitionTraceEnabled() {
+ return mTransitionTracer.isEnabled();
+ }
+
+ @Override
public boolean registerCrossWindowBlurEnabledListener(
ICrossWindowBlurEnabledListener listener) {
return mBlurController.registerCrossWindowBlurEnabledListener(listener);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 8a6a4df0fd42..428a60fb0791 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -149,6 +149,8 @@ public class WindowManagerShellCommand extends ShellCommand {
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
+ case "shell":
+ return runWmShellCommand(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -1171,6 +1173,47 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runWmShellCommand(PrintWriter pw) {
+ String arg = getNextArg();
+
+ switch (arg) {
+ case "tracing":
+ return runWmShellTracing(pw);
+ case "help":
+ default:
+ return runHelp(pw);
+ }
+ }
+
+ private int runHelp(PrintWriter pw) {
+ pw.println("Window Manager Shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" tracing <start/stop>");
+ pw.println(" Start/stop shell transition tracing.");
+
+ return 0;
+ }
+
+ private int runWmShellTracing(PrintWriter pw) {
+ String arg = getNextArg();
+
+ switch (arg) {
+ case "start":
+ mInternal.mTransitionTracer.startTrace(pw);
+ break;
+ case "stop":
+ mInternal.mTransitionTracer.stopTrace(pw);
+ break;
+ default:
+ getErrPrintWriter()
+ .println("Error: expected 'start' or 'stop', but got '" + arg + "'");
+ return -1;
+ }
+
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 128b3292983e..45376ead9b20 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -147,7 +147,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
void setWindowManager(WindowManagerService wms) {
- mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController);
+ mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController,
+ wms.mTransitionTracer);
mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
}
@@ -820,7 +821,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
- tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
+ tf1.setAdjacentTaskFragment(tf2);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
final Bundle bundle = hop.getLaunchOptions();
@@ -1209,7 +1210,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task");
return 0;
}
- root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
+ root1.setAdjacentTaskFragment(root2);
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index de87ab9dcce0..3e165e442d79 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -296,9 +296,9 @@ public abstract class WindowOrientationListener {
/**
* Whether the device is in the lock screen.
- * @return returns true if the screen is locked. Otherwise, returns false.
+ * @return returns true if the key guard is showing on the lock screen.
*/
- public abstract boolean isKeyguardLocked();
+ public abstract boolean isKeyguardShowingAndNotOccluded();
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
@@ -1151,7 +1151,7 @@ public abstract class WindowOrientationListener {
FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT);
if (isRotationResolverEnabled()) {
- if (isKeyguardLocked()) {
+ if (isKeyguardShowingAndNotOccluded()) {
if (mLastRotationResolution != ROTATION_UNSET
&& SystemClock.uptimeMillis() - mLastRotationResolutionTimeStamp
< mRotationMemorizationTimeoutMillis) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f43800bd9d5..fd379bf1d9f4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -139,6 +139,12 @@ class WindowSurfaceController {
"Destroying surface %s called by %s", this, Debug.getCallers(8));
try {
if (mSurfaceControl != null) {
+ if (mAnimator.mIsWallpaper && !mAnimator.mWin.mWindowRemovalAllowed
+ && !mAnimator.mWin.mRemoveOnExit) {
+ // The wallpaper surface should have the same lifetime as its window.
+ Slog.e(TAG, "Unexpected removing wallpaper surface of " + mAnimator.mWin
+ + " by " + Debug.getCallers(8));
+ }
t.remove(mSurfaceControl);
}
} catch (RuntimeException e) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 22810b294883..ddbb930ee20c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -364,11 +363,7 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
void assignLayer(SurfaceControl.Transaction t, int layer) {
- if (windowType == TYPE_DOCK_DIVIDER) {
- // See {@link DisplayContent#mSplitScreenDividerAnchor}
- super.assignRelativeLayer(t,
- mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1);
- } else if (mRoundedCornerOverlay) {
+ if (mRoundedCornerOverlay) {
super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1);
} else {
super.assignLayer(t, layer);
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 0e9a04f1fdde..09044e72f60b 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -57,6 +57,16 @@
<xs:element type="nonNegativeDecimal" name="screenBrightnessRampSlowIncrease">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Maximum time in milliseconds that a brightness increase animation
+ can take. -->
+ <xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampIncreaseMaxMillis">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Maximum time in milliseconds that a brightness decrease animation
+ can take. -->
+ <xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampDecreaseMaxMillis">
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="sensorDetails" name="lightSensor">
<xs:annotation name="final"/>
</xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 075edd77af1d..e8b13ca6356e 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -48,8 +48,10 @@ package com.android.server.display.config {
method public com.android.server.display.config.DisplayQuirks getQuirks();
method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
+ method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxMillis();
method public final java.math.BigDecimal getScreenBrightnessRampFastDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
+ method public final java.math.BigInteger getScreenBrightnessRampIncreaseMaxMillis();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
@@ -64,8 +66,10 @@ package com.android.server.display.config {
method public void setQuirks(com.android.server.display.config.DisplayQuirks);
method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
+ method public final void setScreenBrightnessRampDecreaseMaxMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampFastDecrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampFastIncrease(java.math.BigDecimal);
+ method public final void setScreenBrightnessRampIncreaseMaxMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index c64ff9e128e6..3de65c19a1a4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -185,7 +185,7 @@ public class WindowOrientationListenerTest {
}
@Override
- public boolean isKeyguardLocked() {
+ public boolean isKeyguardShowingAndNotOccluded() {
return mIsScreenLocked;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 62acc7a240bf..ac5bc8621a4c 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -19,7 +19,6 @@ package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_CAN_COLORIZE;
@@ -9332,6 +9331,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false))
.isFalse();
+
+ // telecom manager is not ready - blocked
+ mService.setTelecomManager(mTelecomManager);
+ when(mTelecomManager.isInCall()).thenThrow(new IllegalStateException("not ready"));
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false))
+ .isFalse();
}
@Test
@@ -9431,6 +9437,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mUsageStats).registerBlocked(any());
verify(mUsageStats, never()).registerPostedByApp(any());
+
+ // telecom is not ready - notifications should be blocked but no crashes
+ mService.setTelecomManager(mTelecomManager);
+ when(mTelecomManager.isInCall()).thenThrow(new IllegalStateException("not ready"));
+ reset(mUsageStats);
+
+ mService.addEnqueuedNotification(r);
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats).registerBlocked(any());
+ verify(mUsageStats, never()).registerPostedByApp(any());
}
@Test
@@ -9572,7 +9590,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testMaybeShowReviewPermissionsNotification_flagOff() {
+ mService.setShowReviewPermissionsNotification(false);
+ reset(mMockNm);
+
+ // If state is SHOULD_SHOW, it would show, but not if the flag is off!
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
+ NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW);
+ mService.maybeShowInitialReviewPermissionsNotification();
+ verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class));
+ }
+
+ @Test
public void testMaybeShowReviewPermissionsNotification_unknown() {
+ mService.setShowReviewPermissionsNotification(true);
reset(mMockNm);
// Set up various possible states of the settings int and confirm whether or not the
@@ -9588,6 +9620,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testMaybeShowReviewPermissionsNotification_shouldShow() {
+ mService.setShowReviewPermissionsNotification(true);
reset(mMockNm);
// If state is SHOULD_SHOW, it ... should show
@@ -9602,6 +9635,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testMaybeShowReviewPermissionsNotification_alreadyShown() {
+ mService.setShowReviewPermissionsNotification(true);
reset(mMockNm);
// If state is either USER_INTERACTED or DISMISSED, we should not show this on boot
@@ -9620,6 +9654,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testMaybeShowReviewPermissionsNotification_reshown() {
+ mService.setShowReviewPermissionsNotification(true);
reset(mMockNm);
// If we have re-shown the notification and the user did not subsequently interacted with
@@ -9635,6 +9670,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testRescheduledReviewPermissionsNotification() {
+ mService.setShowReviewPermissionsNotification(true);
reset(mMockNm);
// when rescheduled, the notification goes through the NotificationManagerInternal service
@@ -9653,4 +9689,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
}
+
+ @Test
+ public void testRescheduledReviewPermissionsNotification_flagOff() {
+ mService.setShowReviewPermissionsNotification(false);
+ reset(mMockNm);
+
+ // no notification should be sent if the flag is off
+ mInternalService.sendReviewPermissionsNotification();
+ verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8d50ceaf74e9..598a22bbde39 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -292,7 +292,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
resetZenModeHelper();
mAudioAttributes = new AudioAttributes.Builder()
@@ -621,7 +621,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -691,7 +691,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
@@ -769,7 +769,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -824,9 +824,66 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testReadXml_newXml_permissionNotificationOff() throws Exception {
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+
+ String xml = "<ranking version=\"3\">\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+ + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" />\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_O + "\" >\n"
+ + "<channel id=\"ido\" name=\"name2\" importance=\"2\" show_badge=\"true\"/>\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_P + "\" >\n"
+ + "<channel id=\"idp\" name=\"name3\" importance=\"4\" locked=\"2\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+ NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+ idn.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ idn.setShowBadge(false);
+ NotificationChannel ido = new NotificationChannel("ido", "name2", IMPORTANCE_LOW);
+ ido.setShowBadge(true);
+ ido.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ NotificationChannel idp = new NotificationChannel("idp", "name3", IMPORTANCE_HIGH);
+ idp.lockFields(2);
+ idp.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+
+ loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+
+ assertEquals(idn, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, idn.getId(), false));
+ compareChannels(ido, mHelper.getNotificationChannel(PKG_O, UID_O, ido.getId(), false));
+ compareChannels(idp, mHelper.getNotificationChannel(PKG_P, UID_P, idp.getId(), false));
+
+ verify(mPermissionHelper, never()).setNotificationPermission(any());
+
+ // while this is the same case as above, if the permission helper is set to not show the
+ // review permissions notification it should not write anything to the settings int
+ assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN,
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
+ NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
+ }
+
+ @Test
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -882,7 +939,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_migration_NoUid() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"2\">\n"
@@ -915,7 +972,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_NoUid() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"3\">\n"
@@ -947,7 +1004,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForNonBackup_postMigration() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(1, "first"), new Pair(true, false));
@@ -1027,7 +1084,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(1, "first"), new Pair(true, false));
@@ -1113,7 +1170,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
@@ -1192,7 +1249,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(1, "first"), new Pair(true, false));
@@ -2117,7 +2174,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
@@ -2145,7 +2202,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2168,7 +2225,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2220,7 +2277,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -2233,7 +2290,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -3229,7 +3286,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "</ranking>\n";
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3243,7 +3300,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3341,7 +3398,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3354,7 +3411,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3368,7 +3425,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3382,7 +3439,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
// appears disabled
@@ -3402,7 +3459,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
// appears disabled
@@ -3422,7 +3479,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3478,7 +3535,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3516,7 +3573,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -4150,7 +4207,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4170,7 +4227,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testNormalConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4190,7 +4247,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testNoConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4210,7 +4267,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_noTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4230,7 +4287,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_twice() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
@@ -4242,7 +4299,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_recentTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
@@ -4260,7 +4317,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
parser.nextTag();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.readXml(parser, true, USER_SYSTEM);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
@@ -4272,7 +4329,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testUnDelete_time() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
@@ -4292,7 +4349,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_longTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory);
+ mAppOpsManager, mStatsEventBuilderFactory, false);
long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 4ed7d35a097f..b49e5cbfa9dc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -114,6 +114,11 @@ public class TestableNotificationManagerService extends NotificationManagerServi
mChannelToastsSent.add(uid);
}
+ // Helper method for testing behavior when turning on/off the review permissions notification.
+ protected void setShowReviewPermissionsNotification(boolean setting) {
+ mShowReviewPermissionsNotification = setting;
+ }
+
public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
private int mGetStrongAuthForUserReturnValue = 0;
StrongAuthTrackerFake(Context context) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f11023f34459..5be1ecef0360 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -794,7 +794,7 @@ public class ActivityStarterTests extends WindowTestsBase {
// Create adjacent tasks and put one activity under it
final Task parent = new TaskBuilder(mSupervisor).build();
final Task adjacentParent = new TaskBuilder(mSupervisor).build();
- parent.setAdjacentTaskFragment(adjacentParent, true);
+ parent.setAdjacentTaskFragment(adjacentParent);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setParentTask(parent)
.setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 00e1ed226d7e..8656a4fecef1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -593,7 +593,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
.setCreatedByOrganizer(true);
final Task splitRoot1 = builder.build();
final Task splitRoot2 = builder.build();
- splitRoot1.setAdjacentTaskFragment(splitRoot2, false /* moveTogether */);
+ splitRoot1.setAdjacentTaskFragment(splitRoot2);
final ActivityRecord activity1 = createActivityRecordWithParentTask(splitRoot1);
activity1.setVisible(false);
activity1.mVisibleRequested = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e8f1d2390c34..2a9fcb9d070b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -82,7 +82,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -108,7 +108,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -129,7 +129,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -756,7 +756,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
adjacentRootTask.mCreatedByOrganizer = true;
final Task candidateTask = createTaskInRootTask(rootTask, 0 /* userId*/);
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
// Verify the launch root with candidate task
Task actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 5340a79f4b94..7cdf5a8629cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -32,7 +32,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -412,7 +411,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
- mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
+ mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
assertApplyTransactionDisallowed(mTransaction);
@@ -630,7 +629,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(errorToken), any(IllegalArgumentException.class));
- verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean());
+ verify(mTaskFragment, never()).setAdjacentTaskFragment(any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 3ed484ac7391..228cb65aab38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -202,7 +202,7 @@ public class TaskFragmentTest extends WindowTestsBase {
doReturn(true).when(primaryActivity).supportsPictureInPicture();
doReturn(false).when(secondaryActivity).supportsPictureInPicture();
- primaryTf.setAdjacentTaskFragment(secondaryTf, false /* moveAdjacentTogether */);
+ primaryTf.setAdjacentTaskFragment(secondaryTf);
primaryActivity.setState(RESUMED, "test");
secondaryActivity.setState(RESUMED, "test");
@@ -448,7 +448,7 @@ public class TaskFragmentTest extends WindowTestsBase {
.setOrganizer(mOrganizer)
.setFragmentToken(new Binder())
.build();
- tf0.setAdjacentTaskFragment(tf1, false /* moveAdjacentTogether */);
+ tf0.setAdjacentTaskFragment(tf1);
tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
task.setBounds(0, 0, 1200, 1000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 44b1d83580d6..71387147150e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -59,7 +59,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
@@ -1417,25 +1416,6 @@ public class TaskTests extends WindowTestsBase {
}
@Test
- public void testMoveToFront_moveAdjacentTask() {
- final Task task1 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final Task task2 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- spyOn(task2);
-
- task1.setAdjacentTaskFragment(task2, false /* moveTogether */);
- task1.moveToFront("" /* reason */);
- verify(task2, never()).moveToFrontInner(anyString(), isNull());
-
- // Reset adjacent tasks to move together.
- task1.setAdjacentTaskFragment(null, false /* moveTogether */);
- task1.setAdjacentTaskFragment(task2, true /* moveTogether */);
- task1.moveToFront("" /* reason */);
- verify(task2).moveToFrontInner(anyString(), isNull());
- }
-
- @Test
public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() {
final Task task = createTask(mDisplayContent);
final TaskFragment tfBehind = createTaskFragmentWithParentTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index d9d819b5f00f..234bfa7ad772 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -88,7 +88,11 @@ public class TransitionTests extends WindowTestsBase {
final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
private Transition createTestTransition(int transitType) {
- TransitionController controller = mock(TransitionController.class);
+ TransitionTracer tracer = mock(TransitionTracer.class);
+ final TransitionController controller = new TransitionController(
+ mock(ActivityTaskManagerService.class), mock(TaskSnapshotController.class),
+ mock(TransitionTracer.class));
+
final BLASTSyncEngine sync = createTestBLASTSyncEngine();
final Transition t = new Transition(transitType, 0 /* flags */, controller, sync);
t.startCollecting(0 /* timeoutMs */);
@@ -584,7 +588,7 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTimeout() {
final TransitionController controller = new TransitionController(mAtm,
- mock(TaskSnapshotController.class));
+ mock(TaskSnapshotController.class), mock(TransitionTracer.class));
final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
final CountDownLatch latch = new CountDownLatch(1);
// When the timeout is reached, it will finish the sync-group and notify transaction ready.
@@ -841,7 +845,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
- final TransitionController controller = new TransitionController(mAtm, snapshotController);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController,
+ mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -905,7 +910,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTransientLaunch() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
- final TransitionController controller = new TransitionController(mAtm, snapshotController);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController,
+ mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -968,7 +974,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testNotReadyPushPop() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
- final TransitionController controller = new TransitionController(mAtm, snapshotController);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController,
+ mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index f08b9fddd6af..08bad70a1411 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -690,7 +690,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final RunningTaskInfo info2 = task2.getTaskInfo();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */);
+ wct.setAdjacentRoots(info1.token, info2.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(task1.getAdjacentTaskFragment(), task2);
assertEquals(task2.getAdjacentTaskFragment(), task1);
@@ -700,8 +700,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
- task1.setAdjacentTaskFragment(null, false /* moveTogether */);
- task2.setAdjacentTaskFragment(null, false /* moveTogether */);
+ task1.setAdjacentTaskFragment(null);
+ task2.setAdjacentTaskFragment(null);
wct = new WindowContainerTransaction();
wct.clearLaunchAdjacentFlagRoot(info1.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index e138d52b8ef6..b973fca74977 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1550,7 +1550,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mSecondary = mService.mTaskOrganizerController.createRootTask(
display, WINDOWING_MODE_MULTI_WINDOW, null);
- mPrimary.setAdjacentTaskFragment(mSecondary, true);
+ mPrimary.setAdjacentTaskFragment(mSecondary);
display.getDefaultTaskDisplayArea().setLaunchAdjacentFlagRootTask(mSecondary);
final Rect primaryBounds = new Rect();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 2df1d23c0497..77fca451547d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -328,7 +328,6 @@ public class ZOrderingTests extends WindowTestsBase {
assertWindowHigher(mImeWindow, imeSystemOverlayTarget);
assertWindowHigher(mImeWindow, mChildAppWindowAbove);
assertWindowHigher(mImeWindow, mAppWindow);
- assertWindowHigher(mImeWindow, mDockedDividerWindow);
// The IME has a higher base layer than the status bar so we may expect it to go
// above the status bar once they are both in the Non-App layer, as past versions of this
@@ -349,7 +348,6 @@ public class ZOrderingTests extends WindowTestsBase {
assertWindowHigher(mImeWindow, mChildAppWindowAbove);
assertWindowHigher(mImeWindow, mAppWindow);
- assertWindowHigher(mImeWindow, mDockedDividerWindow);
assertWindowHigher(mImeWindow, mStatusBarWindow);
// And, IME dialogs should always have an higher layer than the IME.
@@ -489,77 +487,6 @@ public class ZOrderingTests extends WindowTestsBase {
}
@Test
- public void testDockedDividerPosition() {
- final Task pinnedTask =
- createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- final WindowState pinnedWindow =
- createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
-
- final Task belowTask =
- createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState belowTaskWindow =
- createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
-
- final Task splitScreenTask1 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow1 =
- createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
- final Task splitScreenTask2 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow2 =
- createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
-
- final Task aboveTask =
- createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState aboveTaskWindow =
- createAppWindow(aboveTask, ACTIVITY_TYPE_STANDARD, "aboveTaskWindow");
-
- mDisplayContent.assignChildLayers(mTransaction);
-
- assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow2, belowTaskWindow);
- assertWindowHigher(mDockedDividerWindow, splitWindow1);
- assertWindowHigher(mDockedDividerWindow, splitWindow2);
- assertWindowHigher(aboveTaskWindow, mDockedDividerWindow);
- assertWindowHigher(pinnedWindow, aboveTaskWindow);
- }
-
-
- @Test
- public void testDockedDividerPosition_noAboveTask() {
- final Task pinnedTask =
- createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- final WindowState pinnedWindow =
- createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
-
- final Task belowTask =
- createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState belowTaskWindow =
- createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
-
- final Task splitScreenTask1 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow1 =
- createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
- final Task splitScreenTask2 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow2 =
- createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
-
- mDisplayContent.assignChildLayers(mTransaction);
-
- assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow2, belowTaskWindow);
- assertWindowHigher(mDockedDividerWindow, splitWindow1);
- assertWindowHigher(mDockedDividerWindow, splitWindow2);
- assertWindowHigher(pinnedWindow, mDockedDividerWindow);
- }
-
- @Test
public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() {
// create RecentsAnimationController
IRecentsAnimationRunner mockRunner = mock(IRecentsAnimationRunner.class);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 43fcc8feaec2..73b2510e6cf1 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -35,6 +35,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutServiceInternal;
@@ -104,6 +105,7 @@ import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -126,6 +128,7 @@ public class VoiceInteractionManagerService extends SystemService {
final ActivityManagerInternal mAmInternal;
final ActivityTaskManagerInternal mAtmInternal;
final UserManagerInternal mUserManagerInternal;
+ final PackageManagerInternal mPackageManagerInternal;
final ArrayMap<Integer, VoiceInteractionManagerServiceStub.SoundTriggerSession>
mLoadedKeyphraseIds = new ArrayMap<>();
ShortcutServiceInternal mShortcutServiceInternal;
@@ -146,6 +149,8 @@ public class VoiceInteractionManagerService extends SystemService {
LocalServices.getService(ActivityTaskManagerInternal.class));
mUserManagerInternal = Objects.requireNonNull(
LocalServices.getService(UserManagerInternal.class));
+ mPackageManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(PackageManagerInternal.class));
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
@@ -369,6 +374,21 @@ public class VoiceInteractionManagerService extends SystemService {
return new SoundTriggerSessionBinderProxy(session);
}
+ @GuardedBy("this")
+ private void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) {
+ if (mImpl == null) {
+ Slog.w(TAG, "Cannot grant implicit access because mImpl is null.");
+ return;
+ }
+
+ final int grantRecipientAppId = UserHandle.getAppId(grantRecipientUid);
+ final int grantRecipientUserId = UserHandle.getUserId(grantRecipientUid);
+ final int voiceInteractionUid = mImpl.mInfo.getServiceInfo().applicationInfo.uid;
+ mPackageManagerInternal.grantImplicitAccess(
+ grantRecipientUserId, intent, grantRecipientAppId, voiceInteractionUid,
+ /* direct= */ true);
+ }
+
private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity(
IBinder client) {
Identity identity = new Identity();
@@ -386,6 +406,7 @@ public class VoiceInteractionManagerService extends SystemService {
void startLocalVoiceInteraction(final IBinder token, Bundle options) {
if (mImpl == null) return;
+ final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
mImpl.showSessionLocked(options,
@@ -397,6 +418,11 @@ public class VoiceInteractionManagerService extends SystemService {
@Override
public void onShown() {
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ VoiceInteractionManagerServiceStub.this
+ .grantImplicitAccessLocked(callingUid,
+ /* intent= */ null);
+ }
mAtmInternal.onLocalVoiceInteractionStarted(token,
mImpl.mActiveSession.mSession,
mImpl.mActiveSession.mInteractor);
@@ -965,8 +991,16 @@ public class VoiceInteractionManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.startVoiceActivityLocked(callingFeatureId, callingPid, callingUid,
- token, intent, resolvedType);
+ final ActivityInfo activityInfo = intent.resolveActivityInfo(
+ mContext.getPackageManager(), PackageManager.MATCH_ALL);
+ if (activityInfo != null) {
+ final int activityUid = activityInfo.applicationInfo.uid;
+ grantImplicitAccessLocked(activityUid, intent);
+ } else {
+ Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity.");
+ }
+ return mImpl.startVoiceActivityLocked(
+ callingFeatureId, callingPid, callingUid, token, intent, resolvedType);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -1005,6 +1039,15 @@ public class VoiceInteractionManagerService extends SystemService {
}
final long caller = Binder.clearCallingIdentity();
try {
+ // Getting the UID corresponding to the taskId, and grant the visibility to it.
+ final ActivityTokens tokens = mAtmInternal
+ .getAttachedNonFinishingActivityForTask(taskId, /* token= */ null);
+ final ComponentName componentName = mAtmInternal.getActivityName(
+ tokens.getActivityToken());
+ grantImplicitAccessLocked(mPackageManagerInternal.getPackageUid(
+ componentName.getPackageName(), PackageManager.MATCH_ALL,
+ UserHandle.myUserId()), /* intent= */ null);
+
mImpl.requestDirectActionsLocked(token, taskId, assistToken,
cancellationCallback, resultCallback);
} finally {
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index fa1bae41f433..5e111639b100 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -232,13 +232,14 @@ public final class DataProfile implements Parcelable {
}
/**
- * @return True if the profile is enabled.
+ * @return {@code true} if the profile is enabled. If the profile only has a
+ * {@link TrafficDescriptor}, but no {@link ApnSetting}, then this profile is always enabled.
*/
public boolean isEnabled() {
if (mApnSetting != null) {
return mApnSetting.isEnabled();
}
- return false;
+ return true;
}
/**
@@ -534,7 +535,7 @@ public final class DataProfile implements Parcelable {
@Type
private int mType = -1;
- private boolean mEnabled;
+ private boolean mEnabled = true;
@ApnType
private int mSupportedApnTypesBitmask;
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
index f1e84b1d8a33..b44e8b42f052 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
@@ -20,7 +20,7 @@ import java.util.List;
public class Utils {
- public static final int ASM_VERSION = Opcodes.ASM7;
+ public static final int ASM_VERSION = Opcodes.ASM9;
/**
* Reads a comma separated configuration similar to the Jack definition.