Merge "Adding empty locale-config case" into tm-dev
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e9df50f..961135f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1207,11 +1207,6 @@
*/
public void sendTrackballEventSync(MotionEvent event) {
validateNotAppThread();
- if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
- throw new IllegalArgumentException(
- "Cannot inject pointer events from sendTrackballEventSync()."
- + " Use sendPointerSync() to inject pointer events.");
- }
if (!event.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
event.setSource(InputDevice.SOURCE_TRACKBALL);
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5df64e3..d94ad3a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -623,6 +623,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ if (!mPhysicalCameraSettings.containsKey(mLogicalCameraId)) {
+ throw new IllegalStateException("Physical camera settings map must contain a key for "
+ + "the logical camera id.");
+ }
+
int physicalCameraCount = mPhysicalCameraSettings.size();
dest.writeInt(physicalCameraCount);
//Logical camera id and settings always come first.
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
index 89db857..8a40d00 100644
--- a/core/java/android/hardware/input/InputDeviceSensorManager.java
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -98,7 +98,7 @@
*/
private void updateInputDeviceSensorInfoLocked(int deviceId) {
final InputDevice inputDevice = InputDevice.getDevice(deviceId);
- if (inputDevice.hasSensor()) {
+ if (inputDevice != null && inputDevice.hasSensor()) {
final InputSensorInfo[] sensorInfos =
mInputManager.getSensorList(deviceId);
populateSensorsForInputDeviceLocked(deviceId, sensorInfos);
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 03d1151..bd6c4e1 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -151,6 +151,8 @@
private boolean mDrawLegacyNavigationBarBackground;
+ private final Rect mTempRect = new Rect();
+
Impl(@NonNull InputMethodService inputMethodService) {
mService = inputMethodService;
}
@@ -281,13 +283,12 @@
touchableRegion.set(originalInsets.touchableRegion);
break;
}
- final Rect navBarRect = new Rect(decor.getLeft(),
- decor.getBottom() - systemInsets.bottom,
+ mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom,
decor.getRight(), decor.getBottom());
if (touchableRegion == null) {
- touchableRegion = new Region(navBarRect);
+ touchableRegion = new Region(mTempRect);
} else {
- touchableRegion.union(navBarRect);
+ touchableRegion.union(mTempRect);
}
dest.touchableRegion.set(touchableRegion);
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
index a379c33..3df5616 100644
--- a/core/java/android/net/TEST_MAPPING
+++ b/core/java/android/net/TEST_MAPPING
@@ -17,7 +17,7 @@
"path": "frameworks/opt/net/wifi"
}
],
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksCoreTests",
"options": [
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ee1e80d..c45a4c7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2767,17 +2767,16 @@
dispatchApplyInsets(host);
}
+ if (mFirst) {
+ // make sure touch mode code executes by setting cached value
+ // to opposite of the added touch mode.
+ mAttachInfo.mInTouchMode = !mAddedTouchMode;
+ ensureTouchModeLocally(mAddedTouchMode);
+ }
+
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
-
- final Resources res = mView.getContext().getResources();
-
- if (mFirst) {
- // make sure touch mode code executes by setting cached value
- // to opposite of the added touch mode.
- mAttachInfo.mInTouchMode = !mAddedTouchMode;
- ensureTouchModeLocally(mAddedTouchMode);
- } else {
+ if (!mFirst) {
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
@@ -2797,7 +2796,7 @@
}
// Ask host how big it wants to be
- windowSizeMayChange |= measureHierarchy(host, lp, res,
+ windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
}
@@ -6407,6 +6406,24 @@
return FINISH_HANDLED;
}
+ // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
+ // view tree and invoke the appropriate {@link OnBackInvokedCallback}.
+ if (isBack(event)
+ && mContext != null
+ && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+ OnBackInvokedCallback topCallback =
+ getOnBackInvokedDispatcher().getTopCallback();
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ if (topCallback != null) {
+ topCallback.onBackInvoked();
+ return FINISH_HANDLED;
+ }
+ } else {
+ // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
+ return FINISH_NOT_HANDLED;
+ }
+ }
+
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
@@ -6416,19 +6433,6 @@
return FINISH_NOT_HANDLED;
}
- if (isBack(event)
- && mContext != null
- && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
- // Invoke the appropriate {@link OnBackInvokedCallback} if the new back
- // navigation should be used, and the key event is not handled by anything else.
- OnBackInvokedCallback topCallback =
- getOnBackInvokedDispatcher().getTopCallback();
- if (topCallback != null) {
- topCallback.onBackInvoked();
- return FINISH_HANDLED;
- }
- }
-
// This dispatch is for windows that don't have a Window.Callback. Otherwise,
// the Window.Callback usually will have already called this (see
// DecorView.superDispatchKeyEvent) leaving this call a no-op.
@@ -10766,11 +10770,7 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
-
- ev.setDisplayId(mContext.getDisplay().getDisplayId());
- if (mView != null) {
- mView.dispatchKeyEvent(ev);
- }
+ enqueueInputEvent(ev);
}
private void registerCompatOnBackInvokedCallback() {
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 5007df5..4b9a957 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -41,14 +41,13 @@
* @hide
*/
public class WindowContextController {
- // TODO(220049234): Disable attach debug logging before shipping.
- private static final boolean DEBUG_ATTACH = true;
+ private static final boolean DEBUG_ATTACH = false;
private static final String TAG = "WindowContextController";
/**
- * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
+ * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
* {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
- * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}.
+ * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}.
*/
@VisibleForTesting
public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index bea2c78..cad8b9b 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -51,9 +51,10 @@
private IWindowSession mWindowSession;
private IWindow mWindow;
private static final String TAG = "WindowOnBackDispatcher";
- private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
- private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
- .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
+ private static final boolean ENABLE_PREDICTIVE_BACK = SystemProperties
+ .getInt("persist.wm.debug.predictive_back", 1) != 0;
+ private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
+ .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -254,18 +255,18 @@
public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) {
// new back is enabled if the feature flag is enabled AND the app does not explicitly
// request legacy back.
- boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED;
+ boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK;
// If the context is null, we assume true and fallback on the two other conditions.
boolean appRequestsPredictiveBack =
context != null && context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
if (DEBUG) {
Log.d(TAG, TextUtils.formatSimple("App: %s featureFlagEnabled=%s "
- + "appRequestsPredictiveBack=%s",
+ + "appRequestsPredictiveBack=%s alwaysEnforce=%s",
context != null ? context.getApplicationInfo().packageName : "null context",
- featureFlagEnabled, appRequestsPredictiveBack));
+ featureFlagEnabled, appRequestsPredictiveBack, ALWAYS_ENFORCE_PREDICTIVE_BACK));
}
- return featureFlagEnabled && appRequestsPredictiveBack;
+ return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 1feb5d4..06d12b5 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -46,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.build.UnboundedSdkLevel;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -58,7 +59,6 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -126,7 +126,7 @@
*
* <p>0 means not specified.
*/
- public final int onBootclasspathSince;
+ public final String onBootclasspathSince;
/**
* SDK version this library was removed from the BOOTCLASSPATH.
@@ -138,7 +138,7 @@
*
* <p>0 means not specified.
*/
- public final int onBootclasspathBefore;
+ public final String onBootclasspathBefore;
/**
* Declares whether this library can be safely ignored from <uses-library> tags.
@@ -155,19 +155,19 @@
@VisibleForTesting
public SharedLibraryEntry(String name, String filename, String[] dependencies,
boolean isNative) {
- this(name, filename, dependencies, 0 /* onBootclasspathSince */,
- 0 /* onBootclasspathBefore */, isNative);
+ this(name, filename, dependencies, null /* onBootclasspathSince */,
+ null /* onBootclasspathBefore */, isNative);
}
@VisibleForTesting
public SharedLibraryEntry(String name, String filename, String[] dependencies,
- int onBootclasspathSince, int onBootclassPathBefore) {
- this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore,
+ String onBootclasspathSince, String onBootclasspathBefore) {
+ this(name, filename, dependencies, onBootclasspathSince, onBootclasspathBefore,
false /* isNative */);
}
SharedLibraryEntry(String name, String filename, String[] dependencies,
- int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) {
+ String onBootclasspathSince, String onBootclasspathBefore, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
@@ -175,16 +175,14 @@
this.onBootclasspathBefore = onBootclasspathBefore;
this.isNative = isNative;
- canBeSafelyIgnored = this.onBootclasspathSince != 0
- && isSdkAtLeast(this.onBootclasspathSince);
- }
-
- private static boolean isSdkAtLeast(int level) {
- if ("REL".equals(Build.VERSION.CODENAME)) {
- return Build.VERSION.SDK_INT >= level;
- }
- return level == Build.VERSION_CODES.CUR_DEVELOPMENT
- || Build.VERSION.SDK_INT >= level;
+ // this entry can be ignored if either:
+ // - onBootclasspathSince is set and we are at or past that SDK
+ // - onBootclasspathBefore is set and we are before that SDK
+ canBeSafelyIgnored =
+ (this.onBootclasspathSince != null
+ && UnboundedSdkLevel.isAtLeast(this.onBootclasspathSince))
+ || (this.onBootclasspathBefore != null
+ && !UnboundedSdkLevel.isAtLeast(this.onBootclasspathBefore));
}
}
@@ -878,10 +876,8 @@
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
String ldependency = parser.getAttributeValue(null, "dependency");
- int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk",
- 0);
- int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk",
- 0);
+ String minDeviceSdk = parser.getAttributeValue(null, "min-device-sdk");
+ String maxDeviceSdk = parser.getAttributeValue(null, "max-device-sdk");
if (lname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
@@ -889,15 +885,18 @@
Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
- boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT;
+ boolean allowedMinSdk =
+ minDeviceSdk == null || UnboundedSdkLevel.isAtLeast(
+ minDeviceSdk);
boolean allowedMaxSdk =
- maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT;
+ maxDeviceSdk == null || UnboundedSdkLevel.isAtMost(
+ maxDeviceSdk);
final boolean exists = new File(lfile).exists();
if (allowedMinSdk && allowedMaxSdk && exists) {
- int bcpSince = XmlUtils.readIntAttribute(parser,
- "on-bootclasspath-since", 0);
- int bcpBefore = XmlUtils.readIntAttribute(parser,
- "on-bootclasspath-before", 0);
+ String bcpSince = parser.getAttributeValue(null,
+ "on-bootclasspath-since");
+ String bcpBefore = parser.getAttributeValue(null,
+ "on-bootclasspath-before");
SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
ldependency == null
? new String[0] : ldependency.split(":"),
@@ -1487,7 +1486,7 @@
addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
}
- if (isFilesystemSupported("erofs")) {
+ if (isErofsSupported()) {
if (isKernelVersionAtLeast(5, 10)) {
addFeature(PackageManager.FEATURE_EROFS, 0);
} else if (isKernelVersionAtLeast(4, 19)) {
@@ -1865,11 +1864,10 @@
return Process.myUid() == Process.SYSTEM_UID;
}
- private static boolean isFilesystemSupported(String fs) {
+ private static boolean isErofsSupported() {
try {
- final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems"));
- final String fsTable = new String(fsTableData, StandardCharsets.UTF_8);
- return fsTable.contains("\t" + fs + "\n");
+ final Path path = Paths.get("/sys/fs/erofs");
+ return Files.exists(path);
} catch (Exception e) {
return false;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 96fe7e1..4075c5f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2086,7 +2086,7 @@
<!-- @SystemApi @hide Allows applications to register network factory or agent -->
<permission android:name="android.permission.NETWORK_FACTORY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide Allows applications to access network stats provider -->
<permission android:name="android.permission.NETWORK_STATS_PROVIDER"
@@ -2275,13 +2275,13 @@
@hide
-->
<permission android:name="android.permission.BLUETOOTH_MAP"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows bluetooth stack to access files
@hide This should only be used by Bluetooth apk.
-->
<permission android:name="android.permission.BLUETOOTH_STACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows uhid write access for creating virtual input devices
@hide
@@ -2552,7 +2552,7 @@
<!-- Allows access to configure network interfaces, configure/use IPSec, etc.
@hide -->
<permission android:name="android.permission.NET_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows registration for remote audio playback. @hide -->
<permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
@@ -2676,7 +2676,7 @@
<!-- Allows listen permission to always reported system signal strength.
@hide Used internally. -->
<permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
@@ -3911,7 +3911,7 @@
Not for use by third party apps.
@hide -->
<permission android:name="android.permission.MANAGE_APP_OPS_MODES"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier|role" />
<!-- @SystemApi Allows an application to open windows that are for use by parts
of the system user interface.
@@ -4792,7 +4792,7 @@
<!-- Allows an application to manage the companion devices.
@hide -->
<permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows an application to subscribe to notifications about the presence status change
of their associated companion device
@@ -5041,7 +5041,7 @@
<!-- @TestApi Allows an application to query audio related state.
@hide -->
<permission android:name="android.permission.QUERY_AUDIO_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows an application to modify what effects are applied to all audio
(matching certain criteria) from any application.
@@ -5114,7 +5114,7 @@
@hide
-->
<permission android:name="android.permission.DEVICE_POWER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows toggling battery saver on the system.
Superseded by DEVICE_POWER permission. @hide @SystemApi
@@ -5140,7 +5140,7 @@
<!-- @hide Allows low-level access to tun tap driver -->
<permission android:name="android.permission.NET_TUNNELING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Run as a manufacturer test application, running as the root user.
Only available when the device is running in manufacturer test mode.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 60d875c..42f789b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2918,6 +2918,11 @@
com.android.settings.intelligence
</string>
+ <!-- System bluetooth stack package name -->
+ <string name="config_systemBluetoothStack" translatable="false">
+ com.android.bluetooth.services
+ </string>
+
<!-- Flag indicating that the media framework should not allow changes or mute on any
stream or global volumes. -->
<bool name="config_useFixedVolume">false</bool>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 870f549..86bad7f 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -184,6 +184,8 @@
<public name="safety_protection_display_text" />
<!-- @hide @SystemApi -->
<public name="config_systemSettingsIntelligence" />
+ <!-- @hide -->
+ <public name="config_systemBluetoothStack" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 58a2073..04ead1b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,6 +33,30 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.bluetooth.services">
+ <permission name="android.permission.DUMP"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.CALL_PRIVILEGED"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.permission.NFC_HANDOVER_STATUS"/>
+ <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.permission.REAL_GET_TASKS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
+ <permission name="android.permission.WRITE_APN_SETTINGS"/>
+ <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.backupconfirm">
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.CRYPT_KEEPER"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index e528df8..3f0b01b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -131,7 +131,7 @@
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
- true /* applyDismissingParallax */);
+ SplitLayout.PARALLAX_DISMISSING);
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
final WindowContainerToken token1 = task1.token;
@@ -327,13 +327,15 @@
@Override
public void onLayoutPositionChanging(SplitLayout layout) {
mSyncQueue.runInSync(t ->
- layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+ layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+ true /* applyResizingOffset */));
}
@Override
public void onLayoutSizeChanging(SplitLayout layout) {
mSyncQueue.runInSync(t ->
- layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+ layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+ true /* applyResizingOffset */));
}
@Override
@@ -342,7 +344,8 @@
layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t ->
- layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+ layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+ false /* applyResizingOffset */));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 577ced5..42ac195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -48,18 +48,16 @@
* Controls the window animation run when a user initiates a back gesture.
*/
public class BackAnimationController implements RemoteCallable<BackAnimationController> {
-
- private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
- "persist.debug.back_predictability_progress_threshold";
- // By default, enable new back dispatching without any animations.
- private static final int BACK_PREDICTABILITY_PROP =
- SystemProperties.getInt("persist.debug.back_predictability", 1);
- public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0;
- private static final int PROGRESS_THRESHOLD = SystemProperties
- .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1);
private static final String TAG = "BackAnimationController";
+ private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
+ "persist.wm.debug.predictive_back_progress_threshold";
+ public static final boolean IS_ENABLED =
+ SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0;
+ private static final int PROGRESS_THRESHOLD = SystemProperties
+ .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
@VisibleForTesting
- boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0;
+ boolean mEnableAnimations = SystemProperties.getInt(
+ "persist.wm.debug.predictive_back_anim", 0) != 0;
/**
* Location of the initial touch event of the back gesture.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 116d352..ec81d23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -72,6 +72,10 @@
*/
public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
+ public static final int PARALLAX_NONE = 0;
+ public static final int PARALLAX_DISMISSING = 1;
+ public static final int PARALLAX_ALIGN_CENTER = 2;
+
private final int mDividerWindowWidth;
private final int mDividerInsets;
private final int mDividerSize;
@@ -87,7 +91,7 @@
private final SplitWindowManager mSplitWindowManager;
private final DisplayImeController mDisplayImeController;
private final ImePositionProcessor mImePositionProcessor;
- private final DismissingEffectPolicy mDismissingEffectPolicy;
+ private final ResizingEffectPolicy mSurfaceEffectPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
private final InsetsState mInsetsState = new InsetsState();
@@ -105,7 +109,7 @@
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
- boolean applyDismissingParallax) {
+ int parallaxType) {
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
mRotation = configuration.windowConfiguration.getRotation();
@@ -115,7 +119,7 @@
parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
- mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
+ mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
final Resources resources = context.getResources();
mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width);
@@ -281,7 +285,7 @@
}
DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
- mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
+ mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
}
/** Inflates {@link DividerView} on the root surface. */
@@ -486,7 +490,8 @@
/** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
- SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+ SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2,
+ boolean applyResizingOffset) {
final SurfaceControl dividerLeash = getDividerLeash();
if (dividerLeash != null) {
mTempRect.set(getRefDividerBounds());
@@ -506,7 +511,10 @@
return;
}
- mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
+ mSurfaceEffectPolicy.adjustDimSurface(t, dimLayer1, dimLayer2);
+ if (applyResizingOffset) {
+ mSurfaceEffectPolicy.adjustRootSurface(t, leash1, leash2);
+ }
}
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
@@ -590,7 +598,7 @@
* Calls when resizing the split bounds.
*
* @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
- * SurfaceControl, SurfaceControl)
+ * SurfaceControl, SurfaceControl, boolean)
*/
void onLayoutSizeChanging(SplitLayout layout);
@@ -600,7 +608,7 @@
* @see #applyTaskChanges(WindowContainerTransaction, ActivityManager.RunningTaskInfo,
* ActivityManager.RunningTaskInfo)
* @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
- * SurfaceControl, SurfaceControl)
+ * SurfaceControl, SurfaceControl, boolean)
*/
void onLayoutSizeChanged(SplitLayout layout);
@@ -609,7 +617,7 @@
* panel.
*
* @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
- * SurfaceControl, SurfaceControl)
+ * SurfaceControl, SurfaceControl, boolean)
*/
void onLayoutPositionChanging(SplitLayout layout);
@@ -637,21 +645,25 @@
* Calculates and applies proper dismissing parallax offset and dimming value to hint users
* dismissing gesture.
*/
- private class DismissingEffectPolicy {
+ private class ResizingEffectPolicy {
/** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
- private final boolean mApplyParallax;
+ private final int mParallaxType;
+
+ int mShrinkSide = DOCKED_INVALID;
// The current dismissing side.
int mDismissingSide = DOCKED_INVALID;
// The parallax offset to hint the dismissing side and progress.
- final Point mDismissingParallaxOffset = new Point();
+ final Point mParallaxOffset = new Point();
// The dimming value to hint the dismissing side and progress.
float mDismissingDimValue = 0.0f;
+ final Rect mContentBounds = new Rect();
+ final Rect mSurfaceBounds = new Rect();
- DismissingEffectPolicy(boolean applyDismissingParallax) {
- mApplyParallax = applyDismissingParallax;
+ ResizingEffectPolicy(int parallaxType) {
+ mParallaxType = parallaxType;
}
/**
@@ -662,7 +674,7 @@
*/
void applyDividerPosition(int position, boolean isLandscape) {
mDismissingSide = DOCKED_INVALID;
- mDismissingParallaxOffset.set(0, 0);
+ mParallaxOffset.set(0, 0);
mDismissingDimValue = 0;
int totalDismissingDistance = 0;
@@ -676,15 +688,39 @@
- mDividerSnapAlgorithm.getDismissEndTarget().position;
}
+ final boolean topLeftShrink = isLandscape
+ ? position < mWinBounds1.right : position < mWinBounds1.bottom;
+ if (topLeftShrink) {
+ mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+ mContentBounds.set(mWinBounds1);
+ mSurfaceBounds.set(mBounds1);
+ } else {
+ mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+ mContentBounds.set(mWinBounds2);
+ mSurfaceBounds.set(mBounds2);
+ }
+
if (mDismissingSide != DOCKED_INVALID) {
float fraction = Math.max(0,
Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
- fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+ if (mParallaxType == PARALLAX_DISMISSING) {
+ fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+ if (isLandscape) {
+ mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+ } else {
+ mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+ }
+ }
+ }
+
+ if (mParallaxType == PARALLAX_ALIGN_CENTER) {
if (isLandscape) {
- mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+ mParallaxOffset.x =
+ (mSurfaceBounds.width() - mContentBounds.width()) / 2;
} else {
- mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+ mParallaxOffset.y =
+ (mSurfaceBounds.height() - mContentBounds.height()) / 2;
}
}
}
@@ -704,41 +740,66 @@
}
/** Applies parallax offset and dimming value to the root surface at the dismissing side. */
- boolean adjustDismissingSurface(SurfaceControl.Transaction t,
- SurfaceControl leash1, SurfaceControl leash2,
+ void adjustRootSurface(SurfaceControl.Transaction t,
+ SurfaceControl leash1, SurfaceControl leash2) {
+ SurfaceControl targetLeash = null;
+
+ if (mParallaxType == PARALLAX_DISMISSING) {
+ switch (mDismissingSide) {
+ case DOCKED_TOP:
+ case DOCKED_LEFT:
+ targetLeash = leash1;
+ mTempRect.set(mBounds1);
+ break;
+ case DOCKED_BOTTOM:
+ case DOCKED_RIGHT:
+ targetLeash = leash2;
+ mTempRect.set(mBounds2);
+ break;
+ }
+ } else if (mParallaxType == PARALLAX_ALIGN_CENTER) {
+ switch (mShrinkSide) {
+ case DOCKED_TOP:
+ case DOCKED_LEFT:
+ targetLeash = leash1;
+ mTempRect.set(mBounds1);
+ break;
+ case DOCKED_BOTTOM:
+ case DOCKED_RIGHT:
+ targetLeash = leash2;
+ mTempRect.set(mBounds2);
+ break;
+ }
+ }
+ if (mParallaxType != PARALLAX_NONE && targetLeash != null) {
+ t.setPosition(targetLeash,
+ mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y);
+ // Transform the screen-based split bounds to surface-based crop bounds.
+ mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y);
+ t.setWindowCrop(targetLeash, mTempRect);
+ }
+ }
+
+ void adjustDimSurface(SurfaceControl.Transaction t,
SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
- SurfaceControl targetLeash, targetDimLayer;
+ SurfaceControl targetDimLayer;
switch (mDismissingSide) {
case DOCKED_TOP:
case DOCKED_LEFT:
- targetLeash = leash1;
targetDimLayer = dimLayer1;
- mTempRect.set(mBounds1);
break;
case DOCKED_BOTTOM:
case DOCKED_RIGHT:
- targetLeash = leash2;
targetDimLayer = dimLayer2;
- mTempRect.set(mBounds2);
break;
case DOCKED_INVALID:
default:
t.setAlpha(dimLayer1, 0).hide(dimLayer1);
t.setAlpha(dimLayer2, 0).hide(dimLayer2);
- return false;
- }
-
- if (mApplyParallax) {
- t.setPosition(targetLeash,
- mTempRect.left + mDismissingParallaxOffset.x,
- mTempRect.top + mDismissingParallaxOffset.y);
- // Transform the screen-based split bounds to surface-based crop bounds.
- mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
- t.setWindowCrop(targetLeash, mTempRect);
+ return;
}
t.setAlpha(targetDimLayer, mDismissingDimValue)
.setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
- return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 73f3931..7f9ecca2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -63,6 +63,7 @@
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipController;
+import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
@@ -207,7 +208,8 @@
@Provides
static Optional<Pip> providePip(Context context, DisplayController displayController,
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -215,9 +217,11 @@
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
- pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
- phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor));
+ pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
+ pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
+ pipTransitionController, windowManagerShellWrapper, taskStackListener,
+ oneHandedController, mainExecutor));
}
@WMSingleton
@@ -234,6 +238,12 @@
@WMSingleton
@Provides
+ static PipKeepClearAlgorithm providePipKeepClearAlgorithm() {
+ return new PipKeepClearAlgorithm();
+ }
+
+ @WMSingleton
+ @Provides
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 623ef05..175a244 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -107,7 +107,9 @@
private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
private PipBoundsState mPipBoundsState;
+ private PipMotionHelper mPipMotionHelper;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
private TaskStackListenerImpl mTaskStackListener;
@@ -241,6 +243,10 @@
Set<Rect> unrestricted) {
if (mPipBoundsState.getDisplayId() == displayId) {
mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+ mPipMotionHelper.moveToBounds(mPipKeepClearAlgorithm.adjust(
+ mPipBoundsState.getBounds(),
+ mPipBoundsState.getRestrictedKeepClearAreas(),
+ mPipBoundsState.getUnrestrictedKeepClearAreas()));
}
}
};
@@ -293,7 +299,8 @@
@Nullable
public static Pip create(Context context, DisplayController displayController,
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -307,9 +314,9 @@
}
return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
- pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
- pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
- taskStackListener, oneHandedController, mainExecutor)
+ pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
+ phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor)
.mImpl;
}
@@ -317,7 +324,9 @@
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm,
@NonNull PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
PipTaskOrganizer pipTaskOrganizer,
@@ -339,7 +348,9 @@
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
+ mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipMotionHelper = pipMotionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mMainExecutor = mainExecutor;
mMediaController = pipMediaController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
new file mode 100644
index 0000000..a83258f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
@@ -0,0 +1,44 @@
+/*
+ * 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.wm.shell.pip.phone;
+
+import android.graphics.Rect;
+
+import java.util.Set;
+
+/**
+ * Calculates the adjusted position that does not occlude keep clear areas.
+ */
+public class PipKeepClearAlgorithm {
+
+ /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */
+ public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas,
+ Set<Rect> unrestrictedKeepClearAreas) {
+ if (restrictedKeepClearAreas.isEmpty()) {
+ return defaultBounds;
+ }
+ // TODO(b/183746978): implement the adjustment algorithm
+ // naively check if areas intersect, an if so move PiP upwards
+ Rect outBounds = new Rect(defaultBounds);
+ for (Rect r : restrictedKeepClearAreas) {
+ if (r.intersect(outBounds)) {
+ outBounds.offset(0, r.top - outBounds.bottom);
+ }
+ }
+ return outBounds;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index f20870f..aec51ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -377,7 +377,8 @@
return;
}
- mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+ mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
+ false /* applyResizingOffset */);
for (int i = 0; i < apps.length; ++i) {
if (apps[i].mode == MODE_OPENING) {
t.show(apps[i].leash);
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 e150cf9..45931de 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
@@ -30,6 +30,7 @@
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -495,7 +496,8 @@
// Using legacy transitions, so we can't use blast sync since it conflicts.
mTaskOrganizer.applyTransaction(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
+ mSyncQueue.runInSync(t ->
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
}
/**
@@ -704,9 +706,11 @@
mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
mTaskOrganizer.applyTransaction(wct);
- mSyncQueue.runInSync(t -> t
- .setWindowCrop(mMainStage.mRootLeash, null)
- .setWindowCrop(mSideStage.mRootLeash, null));
+ mSyncQueue.runInSync(t -> {
+ setResizingSplits(false /* resizing */);
+ t.setWindowCrop(mMainStage.mRootLeash, null)
+ .setWindowCrop(mSideStage.mRootLeash, null);
+ });
// Hide divider and reset its position.
mSplitLayout.resetDividerPosition();
@@ -780,7 +784,7 @@
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
mSplitLayout.init();
setDividerVisibility(true, t);
- updateSurfaceBounds(mSplitLayout, t);
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
setSplitsVisible(true);
mShouldUpdateRecents = true;
updateRecentTasksSplitPair();
@@ -925,7 +929,8 @@
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mRootTaskInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
+ mDisplayImeController, mTaskOrganizer,
+ PARALLAX_ALIGN_CENTER /* parallaxType */);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
@@ -1075,7 +1080,7 @@
prepareEnterSplitScreen(wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
- updateSurfaceBounds(mSplitLayout, t);
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
setDividerVisibility(true, t);
});
}
@@ -1094,8 +1099,6 @@
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
- setResizingSplits(false /* resizing */);
-
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
@@ -1104,6 +1107,7 @@
return;
}
+ setResizingSplits(false /* resizing */);
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(dismissTop, wct);
@@ -1121,14 +1125,14 @@
@Override
public void onLayoutPositionChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
}
@Override
public void onLayoutSizeChanging(SplitLayout layout) {
mSyncQueue.runInSync(t -> {
setResizingSplits(true /* resizing */);
- updateSurfaceBounds(layout, t);
+ updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
mMainStage.onResizing(getMainStageBounds(), t);
mSideStage.onResizing(getSideStageBounds(), t);
});
@@ -1142,7 +1146,7 @@
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setResizingSplits(false /* resizing */);
- updateSurfaceBounds(layout, t);
+ updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
mMainStage.onResized(t);
mSideStage.onResized(t);
});
@@ -1174,13 +1178,15 @@
layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
}
- void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+ void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
+ boolean applyResizingOffset) {
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
- bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
+ bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
+ applyResizingOffset);
}
void setResizingSplits(boolean resizing) {
@@ -1220,7 +1226,6 @@
mTaskOrganizer.applyTransaction(wct);
}
- @Override
public void onDisplayAdded(int displayId) {
if (displayId != DEFAULT_DISPLAY) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
index f1520ed..0717405 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
@@ -253,7 +253,8 @@
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
- mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+ mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
+ false /* applyResizingOffset */);
if (apps != null) {
for (int i = 0; i < apps.length; ++i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 6ef20e3..ac25c75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -265,7 +265,8 @@
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.addTask(task, getSideStageBounds(), wct);
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t));
+ mSyncQueue.runInSync(
+ t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */));
return true;
}
@@ -801,12 +802,12 @@
@Override
public void onLayoutPositionChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
}
@Override
public void onLayoutSizeChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
mSideStage.setOutlineVisibility(false);
}
@@ -816,7 +817,7 @@
updateWindowBounds(layout, wct);
updateUnfoldBounds();
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
mSideStage.setOutlineVisibility(true);
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
@@ -840,13 +841,15 @@
layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
}
- void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+ void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
+ boolean applyResizingOffset) {
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
- bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
+ bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
+ applyResizingOffset);
}
@Override
@@ -882,7 +885,7 @@
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
+ mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
if (mMainUnfoldController != null && mSideUnfoldController != null) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 8b4e1f8..f1e602f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -73,7 +73,7 @@
mCallbacks,
mDisplayImeController,
mTaskOrganizer,
- false /* applyDismissingParallax */));
+ SplitLayout.PARALLAX_NONE));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index aef298e..deb955b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -74,6 +74,7 @@
@Mock private PhonePipMenuController mMockPhonePipMenuController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+ @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
@Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
@Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
@@ -97,9 +98,10 @@
return null;
}).when(mMockExecutor).execute(any());
mPipController = new PipController(mContext, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
- mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
- mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+ mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
+ mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
+ mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mMockOneHandedController, mMockExecutor);
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -128,9 +130,10 @@
when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
assertNull(PipController.create(spyContext, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
- mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
- mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+ mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
+ mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
+ mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mMockOneHandedController, mMockExecutor));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
new file mode 100644
index 0000000..f657b5e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.wm.shell.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+/**
+ * Unit tests against {@link PipKeepClearAlgorithm}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipKeepClearAlgorithmTest extends ShellTestCase {
+
+ private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
+
+
+ @Before
+ public void setUp() throws Exception {
+ mPipKeepClearAlgorithm = new PipKeepClearAlgorithm();
+ }
+
+ @Test
+ public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() {
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
+ Set.of());
+
+ assertFalse(outBounds.contains(keepClearRect));
+ }
+
+ @Test
+ public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() {
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
+ Set.of());
+
+ assertEquals(inBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+ // TODO(b/183746978): update this test to accommodate for the updated algorithm
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+ Set.of(keepClearRect));
+
+ assertEquals(inBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+ Set.of(keepClearRect));
+
+ assertEquals(inBounds, outBounds);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 061136c6..c571d44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -309,7 +309,7 @@
public void testFinishEnterSplitScreen_applySurfaceLayout() {
mStageCoordinator.finishEnterSplitScreen(new SurfaceControl.Transaction());
- verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any());
+ verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
}
private class UnfoldControllerProvider implements
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 120b09a..de35514 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -41,6 +41,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import android.view.IRecentsAnimationController;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
@@ -244,22 +245,28 @@
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
RecentsAnimationListener recents) {
- ArrayList<TransitionInfo.Change> openingTasks = null;
+ SparseArray<TransitionInfo.Change> openingTasks = null;
boolean cancelRecents = false;
boolean homeGoingAway = false;
boolean hasChangingApp = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
- if (change.getTaskInfo() != null) {
- if (change.getTaskInfo().topActivityType == ACTIVITY_TYPE_HOME) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null) {
+ if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
// canceling recents animation
cancelRecents = true;
}
if (openingTasks == null) {
- openingTasks = new ArrayList<>();
+ openingTasks = new SparseArray<>();
}
- openingTasks.add(change);
+ if (taskInfo.hasParentTask()) {
+ // Collects opening leaf tasks only since Launcher monitors leaf task
+ // ids to perform recents animation.
+ openingTasks.remove(taskInfo.parentTaskId);
+ }
+ openingTasks.put(taskInfo.taskId, change);
}
} else if (change.getMode() == TRANSIT_CLOSE
|| change.getMode() == TRANSIT_TO_BACK) {
@@ -287,7 +294,7 @@
int pauseMatches = 0;
if (!cancelRecents) {
for (int i = 0; i < openingTasks.size(); ++i) {
- if (mPausingTasks.contains(openingTasks.get(i).getContainer())) {
+ if (mPausingTasks.contains(openingTasks.valueAt(i).getContainer())) {
++pauseMatches;
}
}
@@ -308,10 +315,11 @@
final RemoteAnimationTargetCompat[] targets =
new RemoteAnimationTargetCompat[openingTasks.size()];
for (int i = 0; i < openingTasks.size(); ++i) {
- mOpeningLeashes.add(openingTasks.get(i).getLeash());
+ final TransitionInfo.Change change = openingTasks.valueAt(i);
+ mOpeningLeashes.add(change.getLeash());
// We are receiving new opening tasks, so convert to onTasksAppeared.
final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
- openingTasks.get(i), layer, info, t);
+ change, layer, info, t);
mLeashMap.put(mOpeningLeashes.get(i), target.leash);
t.reparent(target.leash, mInfo.getRootLeash());
t.setLayer(target.leash, layer);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 63b2b20..2ac2408 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -376,7 +376,6 @@
boolean withinSensorArea =
isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
if (withinSensorArea) {
- mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
Log.v(TAG, "onTouch | action down");
// The pointer that causes ACTION_DOWN is always at index 0.
@@ -792,6 +791,7 @@
+ " current: " + mOverlay.getRequestId());
return;
}
+ mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
if (!mOnFingerDown) {
playStartHaptic();
@@ -806,11 +806,9 @@
final UdfpsView view = mOverlay.getOverlayView();
if (view != null) {
- Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0);
view.startIllumination(() -> {
mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId);
mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
- Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 6d727b4..b172e92 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -65,6 +65,7 @@
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.view.CrossWindowBlurListeners;
@@ -452,6 +453,12 @@
@Provides
@Singleton
+ static CarrierConfigManager provideCarrierConfigManager(Context context) {
+ return context.getSystemService(CarrierConfigManager.class);
+ }
+
+ @Provides
+ @Singleton
static WindowManager provideWindowManager(Context context) {
return context.getSystemService(WindowManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c9a61a8..44580aa 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -162,6 +162,17 @@
public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
+ // 1200 - predictive back
+ @Keep
+ public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
+ 1200, "persist.wm.debug.predictive_back", true);
+ @Keep
+ public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK_ANIM = new SysPropBooleanFlag(
+ 1201, "persist.wm.debug.predictive_back_anim", false);
+ @Keep
+ public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
+ new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 96ae646..290bf0d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -41,26 +41,23 @@
import javax.inject.Inject;
-import dagger.Lazy;
-
public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
private final Context mContext;
- private final Lazy<GlobalActionsDialogLite> mGlobalActionsDialogLazy;
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final BlurUtils mBlurUtils;
private final CommandQueue mCommandQueue;
- private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final GlobalActionsDialogLite mGlobalActionsDialog;
private boolean mDisabled;
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils,
+ GlobalActionsDialogLite globalActionsDialog, BlurUtils blurUtils,
KeyguardStateController keyguardStateController,
DeviceProvisionedController deviceProvisionedController) {
mContext = context;
- mGlobalActionsDialogLazy = globalActionsDialogLazy;
+ mGlobalActionsDialog = globalActionsDialog;
mKeyguardStateController = keyguardStateController;
mDeviceProvisionedController = deviceProvisionedController;
mCommandQueue = commandQueue;
@@ -71,16 +68,12 @@
@Override
public void destroy() {
mCommandQueue.removeCallback(this);
- if (mGlobalActionsDialog != null) {
- mGlobalActionsDialog.destroy();
- mGlobalActionsDialog = null;
- }
+ mGlobalActionsDialog.destroy();
}
@Override
public void showGlobalActions(GlobalActionsManager manager) {
if (mDisabled) return;
- mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
}
@@ -189,7 +182,7 @@
final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
mDisabled = disabled;
- if (disabled && mGlobalActionsDialog != null) {
+ if (disabled) {
mGlobalActionsDialog.dismissDialog();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 05b2c50..3a727ba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -681,7 +681,6 @@
button.setOnClickListener(v -> {
if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
- mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT);
action.run();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 3c6805b..cd86fff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -19,6 +19,7 @@
import android.app.StatusBarManager
import android.content.Context
import android.media.MediaRoute2Info
+import android.util.Log
import android.view.View
import androidx.annotation.StringRes
import com.android.internal.logging.UiEventLogger
@@ -221,7 +222,12 @@
*/
fun getSenderStateFromId(
@StatusBarManager.MediaTransferSenderState displayState: Int,
- ): ChipStateSender = values().first { it.stateInt == displayState }
+ ): ChipStateSender? = try {
+ values().first { it.stateInt == displayState }
+ } catch (e: NoSuchElementException) {
+ Log.e(TAG, "Could not find requested state $displayState", e)
+ null
+ }
/**
* Returns the state int from [StatusBarManager] associated with the given sender state
@@ -238,3 +244,5 @@
// process and we should keep the user informed about it as long as possible (but don't allow it to
// continue indefinitely).
private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
+
+private const val TAG = "ChipStateSender"
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index f5abe28..40265a3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -143,6 +143,7 @@
private boolean mDeadZoneConsuming = false;
private final NavigationBarTransitions mBarTransitions;
private final OverviewProxyService mOverviewProxyService;
+ @Nullable
private AutoHideController mAutoHideController;
// performs manual animation in sync with layout transitions
@@ -316,7 +317,7 @@
};
private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
- if (visible) {
+ if (visible && mAutoHideController != null) {
mAutoHideController.touchAutoHide();
}
notifyActiveTouchRegions();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 2435497..3e00a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -18,6 +18,7 @@
import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import android.app.Dialog;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -60,20 +61,25 @@
import com.android.settingslib.utils.PowerUtil;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.volume.Events;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Objects;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
*/
@SysUISingleton
@@ -164,11 +170,15 @@
private ActivityStarter mActivityStarter;
private final BroadcastSender mBroadcastSender;
+ private final Lazy<BatteryController> mBatteryControllerLazy;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
+
/**
*/
@Inject
public PowerNotificationWarnings(Context context, ActivityStarter activityStarter,
- BroadcastSender broadcastSender) {
+ BroadcastSender broadcastSender, Lazy<BatteryController> batteryControllerLazy,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mNoMan = mContext.getSystemService(NotificationManager.class);
mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -176,6 +186,8 @@
mReceiver.init();
mActivityStarter = activityStarter;
mBroadcastSender = broadcastSender;
+ mBatteryControllerLazy = batteryControllerLazy;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mUseSevereDialog = mContext.getResources().getBoolean(R.bool.config_severe_battery_dialog);
}
@@ -685,8 +697,19 @@
}
d.setShowForAllUsers(true);
d.setOnDismissListener((dialog) -> mSaverConfirmation = null);
- d.show();
+ WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView();
+ if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) {
+ mDialogLaunchAnimator.showFromView(d, ref.get());
+ } else {
+ d.show();
+ }
mSaverConfirmation = d;
+ mBatteryControllerLazy.get().clearLastPowerSaverStartView();
+ }
+
+ @VisibleForTesting
+ Dialog getSaverConfirmationDialog() {
+ return mSaverConfirmation;
}
private boolean isEnglishLocale() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 7d8a28f..1004fca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -116,6 +116,11 @@
public void handleSetListening(boolean listening) {
super.handleSetListening(listening);
mSetting.setListening(listening);
+ if (!listening) {
+ // If we stopped listening, it means that the tile is not visible. In that case, we
+ // don't need to save the view anymore
+ mBatteryController.clearLastPowerSaverStartView();
+ }
}
@Override
@@ -128,7 +133,7 @@
if (getState().state == Tile.STATE_UNAVAILABLE) {
return;
}
- mBatteryController.setPowerSaveMode(!mPowerSave);
+ mBatteryController.setPowerSaveMode(!mPowerSave, view);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index d785059..27586b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -84,6 +85,7 @@
private final DeviceProvisionedController mDeviceProvisionedController;
private final KeyguardStateController mKeyguardStateController;
+ private final SecureSettings mSecureSettings;
private final Object mLock = new Object();
// Lazy
@@ -187,6 +189,7 @@
protected NotificationPresenter mPresenter;
protected ContentObserver mLockscreenSettingsObserver;
protected ContentObserver mSettingsObserver;
+ private boolean mHideSilentNotificationsOnLockscreen;
private NotificationEntryManager getEntryManager() {
if (mEntryManager == null) {
@@ -208,6 +211,7 @@
@Main Handler mainHandler,
DeviceProvisionedController deviceProvisionedController,
KeyguardStateController keyguardStateController,
+ SecureSettings secureSettings,
DumpManager dumpManager) {
mContext = context;
mMainHandler = mainHandler;
@@ -222,6 +226,7 @@
mKeyguardManager = keyguardManager;
mBroadcastDispatcher = broadcastDispatcher;
mDeviceProvisionedController = deviceProvisionedController;
+ mSecureSettings = secureSettings;
mKeyguardStateController = keyguardStateController;
dumpManager.registerDumpable(this);
@@ -256,12 +261,18 @@
};
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+ mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
mLockscreenSettingsObserver,
UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ mLockscreenSettingsObserver,
+ UserHandle.USER_ALL);
+
+ mContext.getContentResolver().registerContentObserver(
+ mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
true,
mLockscreenSettingsObserver,
UserHandle.USER_ALL);
@@ -272,7 +283,7 @@
if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+ mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
false,
mSettingsObserver,
UserHandle.USER_ALL);
@@ -366,7 +377,7 @@
}
}
boolean exceedsPriorityThreshold;
- if (hideSilentNotificationsOnLockscreen()) {
+ if (mHideSilentNotificationsOnLockscreen) {
exceedsPriorityThreshold =
entry.getBucket() == BUCKET_MEDIA_CONTROLS
|| (entry.getBucket() != BUCKET_SILENT
@@ -377,11 +388,6 @@
return mShowLockscreenNotifications && exceedsPriorityThreshold;
}
- private boolean hideSilentNotificationsOnLockscreen() {
- return whitelistIpcs(() -> Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0);
- }
-
private void setShowLockscreenNotifications(boolean show) {
mShowLockscreenNotifications = show;
}
@@ -391,7 +397,7 @@
}
protected void updateLockscreenNotificationSetting() {
- final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ final boolean show = mSecureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1,
mCurrentUserId) != 0;
@@ -400,10 +406,13 @@
final boolean allowedByDpm = (dpmFlags
& DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+ mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser(
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0;
+
setShowLockscreenNotifications(show && allowedByDpm);
if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
- final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ final boolean remoteInput = mSecureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
0,
mCurrentUserId) != 0;
@@ -426,8 +435,7 @@
}
if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
+ final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
@@ -492,8 +500,7 @@
}
if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
+ final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index 4a6d7e1..8d7fc98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -21,8 +21,6 @@
import com.android.settingslib.WirelessUtils;
-import java.util.List;
-
/** Shows the operator name */
public class OperatorNameView extends TextView {
private boolean mDemoMode;
@@ -43,8 +41,10 @@
mDemoMode = demoMode;
}
- void update(boolean showOperatorName, boolean hasMobile,
- List<OperatorNameViewController.SubInfo> subs) {
+ void update(boolean showOperatorName,
+ boolean hasMobile,
+ OperatorNameViewController.SubInfo sub
+ ) {
setVisibility(showOperatorName ? VISIBLE : GONE);
boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
@@ -55,24 +55,21 @@
}
if (!mDemoMode) {
- updateText(subs);
+ updateText(sub);
}
}
- void updateText(List<OperatorNameViewController.SubInfo> subs) {
+ void updateText(OperatorNameViewController.SubInfo subInfo) {
+ CharSequence carrierName = null;
CharSequence displayText = null;
- final int N = subs.size();
- for (int i = 0; i < N; i++) {
- OperatorNameViewController.SubInfo subInfo = subs.get(i);
- CharSequence carrierName = subs.get(i).getCarrierName();
- if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) {
- if (subInfo.stateInService()) {
- displayText = subInfo.getCarrierName();
- break;
- }
+ if (subInfo != null) {
+ carrierName = subInfo.getCarrierName();
+ }
+ if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) {
+ if (subInfo.stateInService()) {
+ displayText = carrierName;
}
}
-
setText(displayText);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index 8a4c4b5..8afc72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -20,6 +20,7 @@
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.view.View;
@@ -30,12 +31,11 @@
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.ViewController;
-import java.util.ArrayList;
-import java.util.List;
-
import javax.inject.Inject;
/** Controller for {@link OperatorNameView}. */
@@ -47,19 +47,22 @@
private final TunerService mTunerService;
private final TelephonyManager mTelephonyManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final CarrierConfigTracker mCarrierConfigTracker;
private OperatorNameViewController(OperatorNameView view,
DarkIconDispatcher darkIconDispatcher,
NetworkController networkController,
TunerService tunerService,
TelephonyManager telephonyManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ CarrierConfigTracker carrierConfigTracker) {
super(view);
mDarkIconDispatcher = darkIconDispatcher;
mNetworkController = networkController;
mTunerService = tunerService;
mTelephonyManager = telephonyManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mCarrierConfigTracker = carrierConfigTracker;
}
@Override
@@ -79,24 +82,22 @@
}
private void update() {
- mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0,
- mTelephonyManager.isDataCapable(), getSubInfos());
+ SubInfo defaultSubInfo = getDefaultSubInfo();
+ boolean showOperatorName =
+ mCarrierConfigTracker
+ .getShowOperatorNameInStatusBarConfig(defaultSubInfo.getSubId())
+ && (mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0);
+ mView.update(showOperatorName, mTelephonyManager.isDataCapable(), getDefaultSubInfo());
}
- private List<SubInfo> getSubInfos() {
- List<SubInfo> result = new ArrayList<>();
- List<SubscriptionInfo> subscritionInfos =
- mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
-
- for (SubscriptionInfo subscriptionInfo : subscritionInfos) {
- int subId = subscriptionInfo.getSubscriptionId();
- result.add(new SubInfo(
- subscriptionInfo.getCarrierName(),
- mKeyguardUpdateMonitor.getSimState(subId),
- mKeyguardUpdateMonitor.getServiceState(subId)));
- }
-
- return result;
+ private SubInfo getDefaultSubInfo() {
+ int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ SubscriptionInfo sI = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(defaultSubId);
+ return new SubInfo(
+ sI.getSubscriptionId(),
+ sI.getCarrierName(),
+ mKeyguardUpdateMonitor.getSimState(defaultSubId),
+ mKeyguardUpdateMonitor.getServiceState(defaultSubId));
}
/** Factory for constructing an {@link OperatorNameViewController}. */
@@ -106,22 +107,32 @@
private final TunerService mTunerService;
private final TelephonyManager mTelephonyManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final CarrierConfigTracker mCarrierConfigTracker;
@Inject
- public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController,
- TunerService tunerService, TelephonyManager telephonyManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ public Factory(DarkIconDispatcher darkIconDispatcher,
+ NetworkController networkController,
+ TunerService tunerService,
+ TelephonyManager telephonyManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ CarrierConfigTracker carrierConfigTracker) {
mDarkIconDispatcher = darkIconDispatcher;
mNetworkController = networkController;
mTunerService = tunerService;
mTelephonyManager = telephonyManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mCarrierConfigTracker = carrierConfigTracker;
}
/** Create an {@link OperatorNameViewController}. */
public OperatorNameViewController create(OperatorNameView view) {
- return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController,
- mTunerService, mTelephonyManager, mKeyguardUpdateMonitor);
+ return new OperatorNameViewController(view,
+ mDarkIconDispatcher,
+ mNetworkController,
+ mTunerService,
+ mTelephonyManager,
+ mKeyguardUpdateMonitor,
+ mCarrierConfigTracker);
}
}
@@ -152,7 +163,7 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshCarrierInfo() {
- mView.updateText(getSubInfos());
+ mView.updateText(getDefaultSubInfo());
}
};
@@ -176,17 +187,26 @@
};
static class SubInfo {
+ private final int mSubId;
private final CharSequence mCarrierName;
private final int mSimState;
private final ServiceState mServiceState;
- private SubInfo(CharSequence carrierName,
- int simState, ServiceState serviceState) {
+ private SubInfo(
+ int subId,
+ CharSequence carrierName,
+ int simState,
+ ServiceState serviceState) {
+ mSubId = subId;
mCarrierName = carrierName;
mSimState = simState;
mServiceState = serviceState;
}
+ int getSubId() {
+ return mSubId;
+ }
+
boolean simReady() {
return mSimState == TelephonyManager.SIM_STATE_READY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 6c6ec19..06532c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.SecureSettings;
import java.util.concurrent.Executor;
@@ -263,6 +264,7 @@
NetworkController networkController,
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
+ CarrierConfigTracker carrierConfigTracker,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings,
@@ -282,6 +284,7 @@
networkController,
statusBarStateController,
commandQueue,
+ carrierConfigTracker,
collapsedStatusBarFragmentLogger,
operatorNameViewControllerFactory,
secureSettings,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 8194957..9e48b76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -32,6 +32,7 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
+import android.telephony.SubscriptionManager;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -69,6 +70,9 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.CarrierConfigTracker;
+import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener;
+import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
@@ -115,6 +119,7 @@
private final NotificationIconAreaController mNotificationIconAreaController;
private final PanelExpansionStateManager mPanelExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
+ private final CarrierConfigTracker mCarrierConfigTracker;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final SecureSettings mSecureSettings;
private final Executor mMainExecutor;
@@ -137,6 +142,28 @@
private OperatorNameViewController mOperatorNameViewController;
private StatusBarSystemEventAnimator mSystemEventAnimator;
+ private final CarrierConfigChangedListener mCarrierConfigCallback =
+ new CarrierConfigChangedListener() {
+ @Override
+ public void onCarrierConfigChanged() {
+ if (mOperatorNameViewController == null) {
+ initOperatorName();
+ } else {
+ // Already initialized, KeyguardUpdateMonitorCallback will handle the update
+ }
+ }
+ };
+
+ private final DefaultDataSubscriptionChangedListener mDefaultDataListener =
+ new DefaultDataSubscriptionChangedListener() {
+ @Override
+ public void onDefaultSubscriptionChanged(int subId) {
+ if (mOperatorNameViewController == null) {
+ initOperatorName();
+ }
+ }
+ };
+
@SuppressLint("ValidFragment")
public CollapsedStatusBarFragment(
StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
@@ -153,6 +180,7 @@
NetworkController networkController,
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
+ CarrierConfigTracker carrierConfigTracker,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings,
@@ -172,6 +200,7 @@
mNetworkController = networkController;
mStatusBarStateController = statusBarStateController;
mCommandQueue = commandQueue;
+ mCarrierConfigTracker = carrierConfigTracker;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
mSecureSettings = secureSettings;
@@ -212,6 +241,8 @@
initNotificationIconArea();
mSystemEventAnimator =
new StatusBarSystemEventAnimator(mSystemIconArea, getResources());
+ mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
+ mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
}
@VisibleForTesting
@@ -283,6 +314,8 @@
if (mNetworkController.hasEmergencyCryptKeeperText()) {
mNetworkController.removeCallback(mSignalCallback);
}
+ mCarrierConfigTracker.removeCallback(mCarrierConfigCallback);
+ mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener);
}
/** Initializes views related to the notification icon area. */
@@ -569,11 +602,16 @@
}
private void initOperatorName() {
- if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) {
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) {
ViewStub stub = mStatusBar.findViewById(R.id.operator_name);
mOperatorNameViewController =
mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate());
mOperatorNameViewController.init();
+ // This view should not be visible on lock-screen
+ if (mKeyguardStateController.isShowing()) {
+ hideOperatorName(false);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 95a7316..ecaa28b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import android.annotation.Nullable;
+import android.view.View;
import com.android.systemui.Dumpable;
import com.android.systemui.demomode.DemoMode;
@@ -24,6 +25,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
public interface BatteryController extends DemoMode, Dumpable,
CallbackController<BatteryStateChangeCallback> {
@@ -35,7 +37,32 @@
/**
* Sets if the current device is in power save mode.
*/
- void setPowerSaveMode(boolean powerSave);
+ default void setPowerSaveMode(boolean powerSave) {
+ setPowerSaveMode(powerSave, null);
+ }
+
+ /**
+ * Sets if the current device is in power save mode.
+ *
+ * Can pass the view that triggered the request.
+ */
+ void setPowerSaveMode(boolean powerSave, @Nullable View view);
+
+ /**
+ * Gets a reference to the last view used when called {@link #setPowerSaveMode}.
+ */
+ @Nullable
+ default WeakReference<View> getLastPowerSaverStartView() {
+ return null;
+ }
+
+ /**
+ * Clears the last view used when called {@link #setPowerSaveMode}.
+ *
+ * Immediately after calling this, a call to {@link #getLastPowerSaverStartView()} should return
+ * {@code null}.
+ */
+ default void clearLastPowerSaverStartView() {}
/**
* Returns {@code true} if the device is currently plugged in.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 9e2c478..1e71dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -28,6 +28,7 @@
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.util.Log;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -45,8 +46,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Default implementation of a {@link BatteryController}. This controller monitors for battery
@@ -85,6 +88,11 @@
private Estimate mEstimate;
private boolean mFetchingEstimate = false;
+ // Use AtomicReference because we may request it from a different thread
+ // Use WeakReference because we are keeping a reference to a View that's not as long lived
+ // as this controller.
+ private AtomicReference<WeakReference<View>> mPowerSaverStartView = new AtomicReference<>();
+
@VisibleForTesting
public BatteryControllerImpl(
Context context,
@@ -141,11 +149,22 @@
}
@Override
- public void setPowerSaveMode(boolean powerSave) {
+ public void setPowerSaveMode(boolean powerSave, View view) {
+ if (powerSave) mPowerSaverStartView.set(new WeakReference<>(view));
BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true);
}
@Override
+ public WeakReference<View> getLastPowerSaverStartView() {
+ return mPowerSaverStartView.get();
+ }
+
+ @Override
+ public void clearLastPowerSaverStartView() {
+ mPowerSaverStartView.set(null);
+ }
+
+ @Override
public void addCallback(@NonNull BatteryController.BatteryStateChangeCallback cb) {
synchronized (mChangeCallbacks) {
mChangeCallbacks.add(cb);
diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
index 14190fa..5f7d745 100644
--- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
@@ -23,43 +23,73 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
import android.util.SparseBooleanArray;
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.Set;
import javax.inject.Inject;
/**
- * Tracks the Carrier Config values.
+ * Tracks CarrierConfigs for each subId, as well as the default configuration. CarrierConfigurations
+ * do not trigger a device configuration event, so any UI that relies on carrier configurations must
+ * register with the tracker to get proper updates.
+ *
+ * The tracker also listens for `TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED`
+ *
+ * @see CarrierConfigChangedListener to listen for updates
*/
@SysUISingleton
-public class CarrierConfigTracker extends BroadcastReceiver {
+public class CarrierConfigTracker
+ extends BroadcastReceiver
+ implements CallbackController<CarrierConfigTracker.CarrierConfigChangedListener> {
private final SparseBooleanArray mCallStrengthConfigs = new SparseBooleanArray();
private final SparseBooleanArray mNoCallingConfigs = new SparseBooleanArray();
private final SparseBooleanArray mCarrierProvisionsWifiMergedNetworks =
new SparseBooleanArray();
+ private final SparseBooleanArray mShowOperatorNameConfigs = new SparseBooleanArray();
private final CarrierConfigManager mCarrierConfigManager;
+ private final Set<CarrierConfigChangedListener> mListeners = new ArraySet<>();
+ private final Set<DefaultDataSubscriptionChangedListener> mDataListeners =
+ new ArraySet<>();
private boolean mDefaultCallStrengthConfigLoaded;
private boolean mDefaultCallStrengthConfig;
private boolean mDefaultNoCallingConfigLoaded;
private boolean mDefaultNoCallingConfig;
private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded;
private boolean mDefaultCarrierProvisionsWifiMergedNetworks;
+ private boolean mDefaultShowOperatorNameConfigLoaded;
+ private boolean mDefaultShowOperatorNameConfig;
@Inject
- public CarrierConfigTracker(Context context, BroadcastDispatcher broadcastDispatcher) {
- mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
- broadcastDispatcher.registerReceiver(
- this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ public CarrierConfigTracker(
+ CarrierConfigManager carrierConfigManager,
+ BroadcastDispatcher broadcastDispatcher) {
+ mCarrierConfigManager = carrierConfigManager;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ broadcastDispatcher.registerReceiver(this, filter);
}
@Override
public void onReceive(Context context, Intent intent) {
- if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
- return;
+ String action = intent.getAction();
+ if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
+ updateFromNewCarrierConfig(intent);
+ } else if (TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
+ updateDefaultDataSubscription(intent);
}
+ }
+ private void updateFromNewCarrierConfig(Intent intent) {
final int subId = intent.getIntExtra(
CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -84,6 +114,29 @@
mCarrierProvisionsWifiMergedNetworks.put(subId, config.getBoolean(
CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL));
}
+ synchronized (mShowOperatorNameConfigs) {
+ mShowOperatorNameConfigs.put(subId, config.getBoolean(
+ CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL));
+ }
+
+ notifyCarrierConfigChanged();
+ }
+
+ private void updateDefaultDataSubscription(Intent intent) {
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, -1);
+ notifyDefaultDataSubscriptionChanged(subId);
+ }
+
+ private void notifyCarrierConfigChanged() {
+ for (CarrierConfigChangedListener l : mListeners) {
+ l.onCarrierConfigChanged();
+ }
+ }
+
+ private void notifyDefaultDataSubscriptionChanged(int subId) {
+ for (DefaultDataSubscriptionChangedListener l : mDataListeners) {
+ l.onDefaultSubscriptionChanged(subId);
+ }
}
/**
@@ -139,4 +192,73 @@
}
return mDefaultCarrierProvisionsWifiMergedNetworks;
}
+
+ /**
+ * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the default config
+ */
+ public boolean getShowOperatorNameInStatusBarConfigDefault() {
+ if (!mDefaultShowOperatorNameConfigLoaded) {
+ mDefaultShowOperatorNameConfig = CarrierConfigManager.getDefaultConfig().getBoolean(
+ CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL);
+ mDefaultShowOperatorNameConfigLoaded = true;
+ }
+
+ return mDefaultShowOperatorNameConfig;
+ }
+
+ /**
+ * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the given subId, or the
+ * default value if no override exists
+ *
+ * @param subId the subscription id for which to query the config
+ */
+ public boolean getShowOperatorNameInStatusBarConfig(int subId) {
+ if (mShowOperatorNameConfigs.indexOfKey(subId) >= 0) {
+ return mShowOperatorNameConfigs.get(subId);
+ } else {
+ return getShowOperatorNameInStatusBarConfigDefault();
+ }
+ }
+
+ @Override
+ public void addCallback(@NonNull CarrierConfigChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeCallback(@NonNull CarrierConfigChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /** */
+ public void addDefaultDataSubscriptionChangedListener(
+ @NonNull DefaultDataSubscriptionChangedListener listener) {
+ mDataListeners.add(listener);
+ }
+
+ /** */
+ public void removeDataSubscriptionChangedListener(
+ DefaultDataSubscriptionChangedListener listener) {
+ mDataListeners.remove(listener);
+ }
+
+ /**
+ * Called when carrier config changes
+ */
+ public interface CarrierConfigChangedListener {
+ /** */
+ void onCarrierConfigChanged();
+ }
+
+ /**
+ * Called when the default data subscription changes. Listeners may want to query
+ * subId-dependent configuration values when this event happens
+ */
+ public interface DefaultDataSubscriptionChangedListener {
+ /**
+ * @param subId the new default data subscription id per
+ * {@link SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX}
+ */
+ void onDefaultSubscriptionChanged(int subId);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 9116716..538a9c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -105,6 +105,7 @@
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
@Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var falsingManager: FalsingManager
+ @Mock private lateinit var transitionParent: ViewGroup
private lateinit var appIcon: ImageView
private lateinit var albumView: ImageView
private lateinit var titleText: TextView
@@ -242,6 +243,10 @@
whenever(viewHolder.seamlessText).thenReturn(seamlessText)
whenever(viewHolder.seekBar).thenReturn(seekBar)
+ // Transition View
+ whenever(view.parent).thenReturn(transitionParent)
+ whenever(view.rootView).thenReturn(transitionParent)
+
// Action buttons
whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause)
whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index ef53154..9a01464 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -277,6 +277,17 @@
}
@Test
+ fun commandQueueCallback_invalidStateParam_noChipShown() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ 100,
+ routeInfo,
+ null
+ )
+
+ verify(windowManager, never()).addView(any(), any())
+ }
+
+ @Test
fun receivesNewStateFromCommandQueue_isLogged() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index a156820..1ffa9dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -25,29 +25,48 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class PowerNotificationWarningsTest extends SysuiTestCase {
public static final String FORMATTED_45M = "0h 45m";
@@ -55,14 +74,34 @@
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
private PowerNotificationWarnings mPowerNotificationWarnings;
+ @Mock
+ private BatteryController mBatteryController;
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock
+ private View mView;
+
+ private BroadcastReceiver mReceiver;
+
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Context wrapper = new ContextWrapper(mContext) {
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+ mReceiver = receiver;
+ return null;
+ }
+ };
+
// Test Instance.
mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
ActivityStarter starter = mDependency.injectMockDependency(ActivityStarter.class);
BroadcastSender broadcastSender = mDependency.injectMockDependency(BroadcastSender.class);
- mPowerNotificationWarnings = new PowerNotificationWarnings(mContext, starter,
- broadcastSender);
+ mPowerNotificationWarnings = new PowerNotificationWarnings(wrapper, starter,
+ broadcastSender, () -> mBatteryController, mDialogLaunchAnimator);
BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
mPowerNotificationWarnings.updateSnapshot(snapshot);
@@ -168,4 +207,52 @@
mPowerNotificationWarnings.mUsbHighTempDialog.dismiss();
}
+
+ @Test
+ public void testDialogStartedFromLauncher_viewVisible() {
+ when(mBatteryController.getLastPowerSaverStartView())
+ .thenReturn(new WeakReference<>(mView));
+ when(mView.isAggregatedVisible()).thenReturn(true);
+
+ Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
+ intent.putExtras(new Bundle());
+
+ mReceiver.onReceive(mContext, intent);
+
+ verify(mDialogLaunchAnimator).showFromView(any(), eq(mView));
+
+ mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
+ }
+
+ @Test
+ public void testDialogStartedNotFromLauncher_viewNotVisible() {
+ when(mBatteryController.getLastPowerSaverStartView())
+ .thenReturn(new WeakReference<>(mView));
+ when(mView.isAggregatedVisible()).thenReturn(false);
+
+ Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
+ intent.putExtras(new Bundle());
+
+ mReceiver.onReceive(mContext, intent);
+
+ verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
+
+ assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
+ mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
+ }
+
+ @Test
+ public void testDialogShownNotFromLauncher() {
+ when(mBatteryController.getLastPowerSaverStartView()).thenReturn(null);
+
+ Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
+ intent.putExtras(new Bundle());
+
+ mReceiver.onReceive(mContext, intent);
+
+ verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
+
+ assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
+ mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 1bf8351..3d9205e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -21,6 +21,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -38,6 +39,9 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -63,6 +67,8 @@
private lateinit var qsLogger: QSLogger
@Mock
private lateinit var batteryController: BatteryController
+ @Mock
+ private lateinit var view: View
private lateinit var secureSettings: SecureSettings
private lateinit var testableLooper: TestableLooper
private lateinit var tile: BatterySaverTile
@@ -105,4 +111,26 @@
assertEquals(USER + 1, tile.mSetting.currentUser)
}
+
+ @Test
+ fun testClickingPowerSavePassesView() {
+ tile.onPowerSaveChanged(true)
+ tile.handleClick(view)
+
+ tile.onPowerSaveChanged(false)
+ tile.handleClick(view)
+
+ verify(batteryController).setPowerSaveMode(true, view)
+ verify(batteryController).setPowerSaveMode(false, view)
+ }
+
+ @Test
+ fun testStopListeningClearsViewInController() {
+ clearInvocations(batteryController)
+ tile.handleSetListening(true)
+ verify(batteryController, never()).clearLastPowerSaverStartView()
+
+ tile.handleSetListening(false)
+ verify(batteryController).clearLastPowerSaverStartView()
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 48f8206..7687d12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -66,6 +66,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeSettings;
import com.google.android.collect.Lists;
@@ -109,6 +110,7 @@
private UserInfo mCurrentUser;
private UserInfo mSecondaryUser;
private UserInfo mWorkUser;
+ private FakeSettings mSettings;
private TestNotificationLockscreenUserManager mLockscreenUserManager;
private NotificationEntry mCurrentUserNotif;
private NotificationEntry mSecondaryUserNotif;
@@ -120,6 +122,8 @@
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
int currentUserId = ActivityManager.getCurrentUser();
+ mSettings = new FakeSettings();
+ mSettings.setUserId(ActivityManager.getCurrentUser());
mCurrentUser = new UserInfo(currentUserId, "", 0);
mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
@@ -157,48 +161,45 @@
@Test
public void testLockScreenShowNotificationsFalse() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@Test
public void testLockScreenShowNotificationsTrue() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@Test
public void testLockScreenAllowPrivateNotificationsTrue() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
}
@Test
public void testLockScreenAllowPrivateNotificationsFalse() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
}
@Test
public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mWorkUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
}
@Test
public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mWorkUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
}
@@ -206,8 +207,8 @@
@Test
public void testCurrentUserPrivateNotificationsNotRedacted() {
// GIVEN current user doesn't allow private notifications to show
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
// THEN current user's notification is redacted
@@ -217,8 +218,8 @@
@Test
public void testCurrentUserPrivateNotificationsRedacted() {
// GIVEN current user allows private notifications to show
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
// THEN current user's notification isn't redacted
@@ -228,8 +229,8 @@
@Test
public void testWorkPrivateNotificationsRedacted() {
// GIVEN work profile doesn't private notifications to show
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mWorkUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
// THEN work profile notification is redacted
@@ -239,8 +240,8 @@
@Test
public void testWorkPrivateNotificationsNotRedacted() {
// GIVEN work profile allows private notifications to show
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mWorkUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
// THEN work profile notification isn't redacted
@@ -250,12 +251,11 @@
@Test
public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
// GIVEN work profile allows private notifications to show but the other users don't
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mWorkUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mSecondaryUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
@@ -270,12 +270,11 @@
@Test
public void testWorkProfileRedacted_otherUsersNotRedacted() {
// GIVEN work profile doesn't allow private notifications to show but the other users do
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mWorkUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mSecondaryUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
@@ -291,10 +290,9 @@
public void testSecondaryUserNotRedacted_currentUserRedacted() {
// GIVEN secondary profile allows private notifications to show but the current user
// doesn't allow private notifications to show
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mSecondaryUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
@@ -328,10 +326,9 @@
@Test
public void testShowSilentNotifications_settingSaysShow() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
NotificationEntry entry = new NotificationEntryBuilder()
.setImportance(IMPORTANCE_LOW)
@@ -343,10 +340,9 @@
@Test
public void testShowSilentNotifications_settingSaysHide() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
final Notification notification = mock(Notification.class);
when(notification.isForegroundService()).thenReturn(true);
@@ -360,10 +356,9 @@
@Test
public void testShowSilentNotificationsPeopleBucket_settingSaysHide() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
final Notification notification = mock(Notification.class);
when(notification.isForegroundService()).thenReturn(true);
@@ -377,10 +372,9 @@
@Test
public void testShowSilentNotificationsMediaBucket_settingSaysHide() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
final Notification notification = mock(Notification.class);
when(notification.isForegroundService()).thenReturn(true);
@@ -396,8 +390,8 @@
@Test
public void testKeyguardNotificationSuppressors() {
// GIVEN a notification that should be shown on the lockscreen
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
final NotificationEntry entry = new NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.build();
@@ -433,6 +427,7 @@
Handler.createAsync(Looper.myLooper()),
mDeviceProvisionedController,
mKeyguardStateController,
+ mSettings,
mock(DumpManager.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 5095094..497f7fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -90,6 +91,7 @@
private OperatorNameViewController mOperatorNameViewController;
private SecureSettings mSecureSettings;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private final CarrierConfigTracker mCarrierConfigTracker = mock(CarrierConfigTracker.class);
@Mock
private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
@@ -373,6 +375,7 @@
mNetworkController,
mStatusBarStateController,
mCommandQueue,
+ mCarrierConfigTracker,
new CollapsedStatusBarFragmentLogger(
new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
new DisableFlagsLogger()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 2577dbd..b714df5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -18,6 +18,10 @@
import static android.os.BatteryManager.EXTRA_PRESENT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -30,20 +34,24 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.View;
+import com.android.dx.mockito.inline.extended.StaticInOrder;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
+import org.mockito.MockitoSession;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -53,11 +61,19 @@
@Mock private PowerManager mPowerManager;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private DemoModeController mDemoModeController;
+ @Mock private View mView;
private BatteryControllerImpl mBatteryController;
+ private MockitoSession mMockitoSession;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mMockitoSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(BatterySaverUtils.class)
+ .startMocking();
+
mBatteryController = new BatteryControllerImpl(getContext(),
mock(EnhancedEstimates.class),
mPowerManager,
@@ -68,6 +84,11 @@
mBatteryController.init();
}
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
@Test
public void testBatteryInitialized() {
Assert.assertTrue(mBatteryController.mHasReceivedBattery);
@@ -135,4 +156,33 @@
// THEN it is informed about the battery state
verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true);
}
+
+ @Test
+ public void testBatteryUtilsCalledOnSetPowerSaveMode() {
+ mBatteryController.setPowerSaveMode(true, mView);
+ mBatteryController.setPowerSaveMode(false, mView);
+
+ StaticInOrder inOrder = inOrder(staticMockMarker(BatterySaverUtils.class));
+ inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), true, true));
+ inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), false, true));
+ }
+
+ @Test
+ public void testSaveViewReferenceWhenSettingPowerSaveMode() {
+ mBatteryController.setPowerSaveMode(false, mView);
+
+ Assert.assertNull(mBatteryController.getLastPowerSaverStartView());
+
+ mBatteryController.setPowerSaveMode(true, mView);
+
+ Assert.assertSame(mView, mBatteryController.getLastPowerSaverStartView().get());
+ }
+
+ @Test
+ public void testClearViewReference() {
+ mBatteryController.setPowerSaveMode(true, mView);
+ mBatteryController.clearLastPowerSaverStartView();
+
+ Assert.assertNull(mBatteryController.getLastPowerSaverStartView());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
index e66491e..e660e1f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.settings;
+import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
@@ -34,6 +35,8 @@
private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+ @UserIdInt
+ private int mUserId = UserHandle.USER_CURRENT;
public FakeSettings() {
}
@@ -85,9 +88,13 @@
return Uri.withAppendedPath(CONTENT_URI, name);
}
+ public void setUserId(@UserIdInt int userId) {
+ mUserId = userId;
+ }
+
@Override
public int getUserId() {
- return UserHandle.USER_CURRENT;
+ return mUserId;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index 50c1e73..9ca4db4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -16,6 +16,7 @@
import android.os.Bundle;
import android.testing.LeakCheck;
+import android.view.View;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -47,6 +48,11 @@
}
@Override
+ public void setPowerSaveMode(boolean powerSave, View view) {
+
+ }
+
+ @Override
public boolean isPluggedIn() {
return false;
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 76df8b9..e78c8d1 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -24,8 +24,10 @@
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
+import android.annotation.NonNull;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupManager;
import android.app.backup.FullBackup;
import android.app.backup.IBackupManagerMonitor;
@@ -38,10 +40,12 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.provider.Settings;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
@@ -57,6 +61,7 @@
import com.android.server.backup.utils.RestoreUtils;
import com.android.server.backup.utils.TarBackupReader;
+import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -135,6 +140,8 @@
private boolean mPipesClosed;
private final BackupEligibilityRules mBackupEligibilityRules;
+ private FileMetadata mReadOnlyParent = null;
+
public FullRestoreEngine(
UserBackupManagerService backupManagerService, OperationStorage operationStorage,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
@@ -158,6 +165,22 @@
mBackupEligibilityRules = backupEligibilityRules;
}
+ @VisibleForTesting
+ FullRestoreEngine() {
+ mIsAdbRestore = false;
+ mAllowApks = false;
+ mEphemeralOpToken = 0;
+ mUserId = 0;
+ mBackupEligibilityRules = null;
+ mAgentTimeoutParameters = null;
+ mBuffer = null;
+ mBackupManagerService = null;
+ mOperationStorage = null;
+ mMonitor = null;
+ mMonitorTask = null;
+ mOnlyPackage = null;
+ }
+
public IBackupAgent getAgent() {
return mAgent;
}
@@ -397,6 +420,11 @@
okay = false;
}
+ if (shouldSkipReadOnlyDir(info)) {
+ // b/194894879: We don't support restore of read-only dirs.
+ okay = false;
+ }
+
// At this point we have an agent ready to handle the full
// restore data as well as a pipe for sending data to
// that agent. Tell the agent to start reading from the
@@ -573,6 +601,45 @@
return (info != null);
}
+ boolean shouldSkipReadOnlyDir(FileMetadata info) {
+ if (isValidParent(mReadOnlyParent, info)) {
+ // This file has a read-only parent directory, we shouldn't
+ // restore it.
+ return true;
+ } else {
+ // We're now in a different branch of the file tree, update the parent
+ // value.
+ if (isReadOnlyDir(info)) {
+ // Current directory is read-only. Remember it so that we can skip all
+ // of its contents.
+ mReadOnlyParent = info;
+ Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as "
+ + "read-only dirs are currently not supported.");
+ return true;
+ } else {
+ mReadOnlyParent = null;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) {
+ return parentDir != null
+ && childDir.packageName.equals(parentDir.packageName)
+ && childDir.domain.equals(parentDir.domain)
+ && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path));
+ }
+
+ private static String getPathWithTrailingSeparator(String path) {
+ return path.endsWith(File.separator) ? path : path + File.separator;
+ }
+
+ private static boolean isReadOnlyDir(FileMetadata file) {
+ // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details).
+ return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0;
+ }
+
private void setUpPipes() throws IOException {
synchronized (mPipesLock) {
mPipes = ParcelFileDescriptor.createPipe();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e6bf109..7ab3008 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2132,15 +2132,19 @@
}
}
- PackageMonitor monitor = new PackageMonitor() {
+ if (mPackageMonitorsForUser.get(userId) == null) {
+ PackageMonitor monitor = new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
updateLegacyStorageApps(packageName, uid, false);
}
};
- // TODO(b/149391976): Use different handler?
- monitor.register(mContext, user, true, mHandler);
- mPackageMonitorsForUser.put(userId, monitor);
+ // TODO(b/149391976): Use different handler?
+ monitor.register(mContext, user, true, mHandler);
+ mPackageMonitorsForUser.put(userId, monitor);
+ } else {
+ Slog.w(TAG, "PackageMonitor is already registered for: " + userId);
+ }
}
private static long getLastAccessTime(AppOpsManager manager,
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
index 8be90e0c..b45bfb1 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -30,6 +30,7 @@
import android.os.UserHandle;
import android.os.logcat.ILogcatManagerService;
import android.util.Slog;
+import android.view.InflateException;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
@@ -56,33 +57,46 @@
private String mAlertTitle;
private AlertDialog.Builder mAlertDialog;
private AlertDialog mAlert;
+ private View mAlertView;
private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000;
private static final int MSG_DISMISS_DIALOG = 0;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mContext = this;
- Intent intent = getIntent();
- mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
- mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
- mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
- mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
- mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
- mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+ try {
+ mContext = this;
- if (mAlertTitle != null) {
+ // retrieve Intent extra information
+ Intent intent = getIntent();
+ getIntentInfo(intent);
+ // retrieve the title string from passed intent extra
+ mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+
+ // creaet View
+ mAlertView = createView();
+
+ // create AlertDialog
mAlertDialog = new AlertDialog.Builder(this);
- mAlertDialog.setView(createView());
+ mAlertDialog.setView(mAlertView);
+ // show Alert
mAlert = mAlertDialog.create();
mAlert.show();
+
+ // set Alert Timeout
mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT);
+ } catch (Exception e) {
+ try {
+ Slog.e(TAG, "onCreate failed, declining the logd access", e);
+ mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions", ex);
+ }
}
}
@@ -95,6 +109,19 @@
mAlert = null;
}
+ private void getIntentInfo(Intent intent) throws Exception {
+
+ if (intent == null) {
+ throw new NullPointerException("Intent is null");
+ }
+
+ mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
+ mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
+ mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
+ mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
+ }
+
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
@@ -116,26 +143,41 @@
}
};
- private String getTitleString(Context context, String callingPackage, int uid) {
+ private String getTitleString(Context context, String callingPackage, int uid)
+ throws Exception {
+
PackageManager pm = context.getPackageManager();
- try {
- return context.getString(
- com.android.internal.R.string.log_access_confirmation_title,
- pm.getApplicationInfoAsUser(callingPackage,
- PackageManager.MATCH_DIRECT_BOOT_AUTO,
- UserHandle.getUserId(uid)).loadLabel(pm));
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "App name is unknown.", e);
- return null;
+ if (pm == null) {
+ throw new NullPointerException("PackageManager is null");
}
+
+ CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO,
+ UserHandle.getUserId(uid)).loadLabel(pm);
+ if (appLabel == null) {
+ throw new NameNotFoundException("Application Label is null");
+ }
+
+ return context.getString(com.android.internal.R.string.log_access_confirmation_title,
+ appLabel);
}
- private View createView() {
+ /**
+ * Returns the dialog view.
+ * If we cannot retrieve the package name, it returns null and we decline the full device log
+ * access
+ */
+ private View createView() throws Exception {
+
final View view = getLayoutInflater().inflate(
R.layout.log_access_user_consent_dialog_permission, null /*root*/);
+ if (view == null) {
+ throw new InflateException();
+ }
+
((TextView) view.findViewById(R.id.log_access_dialog_title))
- .setText(mAlertTitle);
+ .setText(mAlertTitle);
Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button);
button_allow.setOnClickListener(this);
@@ -144,6 +186,7 @@
button_deny.setOnClickListener(this);
return view;
+
}
@Override
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 015bf3c5..4c265ad 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -102,16 +102,27 @@
}
}
- private void showDialog(int uid, int gid, int pid, int fd) {
+ /**
+ * Returns the package name.
+ * If we cannot retrieve the package name, it returns null and we decline the full device log
+ * access
+ */
+ private String getPackageName(int uid, int gid, int pid, int fd) {
+
final ActivityManagerInternal activityManagerInternal =
LocalServices.getService(ActivityManagerInternal.class);
+ if (activityManagerInternal != null) {
+ String packageName = activityManagerInternal.getPackageNameByPid(pid);
+ if (packageName != null) {
+ return packageName;
+ }
+ }
PackageManager pm = mContext.getPackageManager();
- String packageName = activityManagerInternal.getPackageNameByPid(pid);
- if (packageName != null) {
- Intent mIntent = createIntent(packageName, uid, gid, pid, fd);
- mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
- return;
+ if (pm == null) {
+ // Decline the logd access if PackageManager is null
+ Slog.e(TAG, "PackageManager is null, declining the logd access");
+ return null;
}
String[] packageNames = pm.getPackagesForUid(uid);
@@ -119,21 +130,19 @@
if (ArrayUtils.isEmpty(packageNames)) {
// Decline the logd access if the app name is unknown
Slog.e(TAG, "Unknown calling package name, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
+ return null;
}
String firstPackageName = packageNames[0];
- if (firstPackageName.isEmpty() || firstPackageName == null) {
+ if (firstPackageName == null || firstPackageName.isEmpty()) {
// Decline the logd access if the package name from uid is unknown
Slog.e(TAG, "Unknown calling package name, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
+ return null;
}
- final Intent mIntent = createIntent(firstPackageName, uid, gid, pid, fd);
- mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
+ return firstPackageName;
+
}
private void declineLogdAccess(int uid, int gid, int pid, int fd) {
@@ -198,16 +207,23 @@
final int procState = LocalServices.getService(ActivityManagerInternal.class)
.getUidProcessState(mUid);
- // If the process is foreground, show a dialog for user consent
+ // If the process is foreground and we can retrieve the package name, show a dialog
+ // for user consent
if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- showDialog(mUid, mGid, mPid, mFd);
- } else {
- /**
- * If the process is background, decline the logd access.
- **/
- declineLogdAccess(mUid, mGid, mPid, mFd);
- return;
+ String packageName = getPackageName(mUid, mGid, mPid, mFd);
+ if (packageName != null) {
+ final Intent mIntent = createIntent(packageName, mUid, mGid, mPid, mFd);
+ mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
+ return;
+ }
}
+
+ /**
+ * If the process is background or cannot retrieve the package name,
+ * decline the logd access.
+ **/
+ declineLogdAccess(mUid, mGid, mPid, mFd);
+ return;
}
}
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 728782c..d0651ed 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -133,6 +133,10 @@
mIntentFilter, null, null);
}
+ public void stop() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
+
/**
* Transfers to a given bluetooth route.
* The dedicated BT device with the route would be activated.
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index b307266..e27cbea 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1150,6 +1150,8 @@
if (DEBUG) {
Slog.d(TAG, userRecord + ": Disposed");
}
+ userRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::stop, userRecord.mHandler));
mUserRecords.remove(userRecord.mUserId);
// Note: User already stopped (by switchUser) so no need to send stop message here.
}
@@ -1330,6 +1332,7 @@
private void start() {
if (!mRunning) {
mRunning = true;
+ mSystemProvider.start();
mWatcher.start();
}
}
@@ -1338,6 +1341,7 @@
if (mRunning) {
mRunning = false;
mWatcher.stop(); // also stops all providers
+ mSystemProvider.stop();
}
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 7878159..d91bf8c 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -71,6 +71,7 @@
private final IAudioService mAudioService;
private final Handler mHandler;
private final Context mContext;
+ private final UserHandle mUser;
private final BluetoothRouteProvider mBtRouteProvider;
private static ComponentName sComponentName = new ComponentName(
@@ -86,6 +87,9 @@
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
int mDeviceVolume;
+ private final AudioManagerBroadcastReceiver mAudioReceiver =
+ new AudioManagerBroadcastReceiver();
+
private final Object mRequestLock = new Object();
@GuardedBy("mRequestLock")
private volatile SessionCreationRequest mPendingSessionCreationRequest;
@@ -108,6 +112,7 @@
mIsSystemRouteProvider = true;
mContext = context;
+ mUser = user;
mHandler = new Handler(Looper.getMainLooper());
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -128,21 +133,33 @@
}
});
updateSessionInfosIfNeeded();
+ }
+ public void start() {
IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
- mContext.registerReceiverAsUser(new AudioManagerBroadcastReceiver(), user,
+ mContext.registerReceiverAsUser(mAudioReceiver, mUser,
intentFilter, null, null);
if (mBtRouteProvider != null) {
mHandler.post(() -> {
- mBtRouteProvider.start(user);
+ mBtRouteProvider.start(mUser);
notifyProviderState();
});
}
updateVolume();
}
+ public void stop() {
+ mContext.unregisterReceiver(mAudioReceiver);
+ if (mBtRouteProvider != null) {
+ mHandler.post(() -> {
+ mBtRouteProvider.stop();
+ notifyProviderState();
+ });
+ }
+ }
+
@Override
public void setCallback(Callback callback) {
super.setCallback(callback);
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 02095eb..4ccf09e 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -2,12 +2,8 @@
"presubmit-large": [
{
"name": "CtsHostsideNetworkTests",
- "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
"options": [
{
- "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
index 0418afb..1a2ff26 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
@@ -19,6 +19,7 @@
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -51,8 +52,11 @@
private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry,
ParsedPackage parsedPackage) {
- if (entry.onBootclasspathBefore != 0
- && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) {
+ if (entry.onBootclasspathBefore != null
+ && isTargetSdkAtMost(
+ parsedPackage.getTargetSdkVersion(),
+ entry.onBootclasspathBefore)
+ && UnboundedSdkLevel.isAtLeast(entry.onBootclasspathBefore)) {
// this package targets an API where this library was in the BCP, so add
// the library transparently in case the package is using it
prefixRequiredLibrary(parsedPackage, entry.name);
@@ -64,4 +68,19 @@
removeLibrary(parsedPackage, entry.name);
}
}
+
+ private static boolean isTargetSdkAtMost(int targetSdk, String onBcpBefore) {
+ if (isCodename(onBcpBefore)) {
+ return targetSdk < 10000;
+ }
+ return targetSdk < Integer.parseInt(onBcpBefore);
+ }
+
+ private static boolean isCodename(String version) {
+ if (version.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+ // assume Android codenames start with upper case letters.
+ return Character.isUpperCase((version.charAt(0)));
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 8ecc51b..3e36431 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -66,6 +66,7 @@
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
IGNORED_FOR_RINGER_MODE,
+ IGNORED_FOR_RINGTONE,
IGNORED_FOR_SETTINGS,
IGNORED_SUPERSEDED,
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index bf32985..d7341cb 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -733,6 +733,12 @@
+ attrs);
}
break;
+ case IGNORED_FOR_RINGTONE:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of ringtone vibration");
+ }
+ break;
+
default:
if (DEBUG) {
Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
@@ -809,6 +815,10 @@
return Vibration.Status.IGNORED_FOR_ALARM;
}
+ if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE) {
+ return Vibration.Status.IGNORED_FOR_RINGTONE;
+ }
+
if (currentVibration.isRepeating()) {
return Vibration.Status.IGNORED_FOR_ONGOING;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 003268b..602e416 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1935,6 +1935,14 @@
} else if (mSourceRecord != null) {
return mSourceRecord.getTask();
} else if (mInTask != null) {
+ // The task is specified from AppTaskImpl, so it may not be attached yet.
+ if (!mInTask.isAttached()) {
+ // Clear reuse task so it can find a proper parent to add the task.
+ if (mReuseTask == mInTask) {
+ mReuseTask = null;
+ }
+ return getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions);
+ }
return mInTask;
} else {
final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */,
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 22a2c41..6e46fa6 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -26,6 +26,8 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
import android.os.UserHandle;
/**
@@ -54,6 +56,16 @@
}
@Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+ }
+ }
+
+ @Override
public void finishAndRemoveTask() {
checkCaller();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index ef0b737..66c625e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -47,12 +47,6 @@
class BackNavigationController {
private static final String TAG = "BackNavigationController";
- // By default, enable new back dispatching without any animations.
- private static final int BACK_PREDICTABILITY_PROP =
- SystemProperties.getInt("persist.debug.back_predictability", 1);
- private static final int ANIMATIONS_MASK = 1 << 1;
- private static final int SCREENSHOT_MASK = 1 << 2;
-
@Nullable
private TaskSnapshotController mTaskSnapshotController;
@@ -60,15 +54,15 @@
* Returns true if the back predictability feature is enabled
*/
static boolean isEnabled() {
- return BACK_PREDICTABILITY_PROP > 0;
+ return SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0;
}
static boolean isScreenshotEnabled() {
- return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0;
+ return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0;
}
private static boolean isAnimationEnabled() {
- return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0;
+ return SystemProperties.getInt("persist.wm.debug.predictive_back_anim", 0) != 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 0038c71..c162e8e 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -175,7 +175,7 @@
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: mActivityRecord.inMultiWindowMode()
- ? mActivityRecord.getRootTask().getBounds()
+ ? mActivityRecord.getTask().getBounds()
: mActivityRecord.getRootTask().getParent().getBounds();
mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
} else if (mLetterbox != null) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 65dca86..83be73a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -176,13 +176,24 @@
t.addTransactionCommittedListener(Runnable::run, () -> {
final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();
+ final Transaction edgeExtensionCreationTransaction = new Transaction();
edgeExtendWindow(animationLeash,
animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
- mFrameTransaction);
+ edgeExtensionCreationTransaction);
synchronized (mLock) {
// only run if animation is not yet canceled by this point
if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
+ // In the case the animation is cancelled, edge extensions are removed
+ // onAnimationLeashLost which is called before onAnimationCancelled.
+ // So we need to check if the edge extensions have already been removed
+ // or not, and if so we don't want to apply the transaction.
+ synchronized (mEdgeExtensionLock) {
+ if (!mEdgeExtensions.isEmpty()) {
+ edgeExtensionCreationTransaction.apply();
+ }
+ }
+
mPreProcessingAnimations.remove(animationLeash);
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 51d68bc..9e54d19 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2606,11 +2606,17 @@
return;
}
+ // Remove immediately if there is display transition because the animation is
+ // usually unnoticeable (e.g. covered by rotation animation) and the animation
+ // bounds could be inconsistent, such as depending on when the window applies
+ // its draw transaction with new rotation.
+ final boolean allowExitAnimation = !getDisplayContent().inTransition();
+
if (wasVisible) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
// Try starting an animation.
- if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) {
ProtoLog.v(WM_DEBUG_ANIM,
"Set animatingExit: reason=remove/applyAnimation win=%s", this);
mAnimatingExit = true;
@@ -2624,7 +2630,8 @@
mWmService.mAccessibilityController.onWindowTransition(this, transit);
}
}
- final boolean isAnimating = mAnimatingExit || isExitAnimationRunningSelfOrParent();
+ final boolean isAnimating = allowExitAnimation
+ && (mAnimatingExit || isExitAnimationRunningSelfOrParent());
final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
&& mActivityRecord.isLastWindow(this);
// We delay the removal of a window if it has a showing surface that can be used to run
@@ -5284,12 +5291,6 @@
if (mControllableInsetProvider != null) {
return;
}
- if (getDisplayContent().inTransition()) {
- // Skip because the animation is usually unnoticeable (e.g. covered by rotation
- // animation) and the animation bounds could be inconsistent, such as depending
- // on when the window applies its draw transaction with new rotation.
- return;
- }
final DisplayInfo displayInfo = getDisplayInfo();
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index e6fd916..d305fc5 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -618,10 +618,17 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
- BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
- mBroadcastReceivers.put(userId, broadcastReceiver);
- mContext.registerReceiverAsUser(
- broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
+
+ if (mBroadcastReceivers.get(userId) == null) {
+ BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
+ mBroadcastReceivers.put(userId, broadcastReceiver);
+ mContext.registerReceiverAsUser(
+ broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
+ } else {
+ // Stopped was not called on this user before setup is called again. This
+ // could happen during consecutive rapid user switching.
+ if (DEBUG) Log.d(TAG, "PerUserBroadcastReceiver was registered for: " + userId);
+ }
ContentObserver contactsContentObserver = new ContactsContentObserver(
BackgroundThread.getHandler());
@@ -639,9 +646,15 @@
// Should never occur for local calls.
}
- PackageMonitor packageMonitor = new PerUserPackageMonitor();
- packageMonitor.register(mContext, null, UserHandle.of(userId), true);
- mPackageMonitors.put(userId, packageMonitor);
+ if (mPackageMonitors.get(userId) == null) {
+ PackageMonitor packageMonitor = new PerUserPackageMonitor();
+ packageMonitor.register(mContext, null, UserHandle.of(userId), true);
+ mPackageMonitors.put(userId, packageMonitor);
+ } else {
+ // Stopped was not called on this user before setup is called again. This
+ // could happen during consecutive rapid user switching.
+ if (DEBUG) Log.d(TAG, "PerUserPackageMonitor was registered for: " + userId);
+ }
if (userId == UserHandle.USER_SYSTEM) {
// The call log and MMS/SMS messages are shared across user profiles. So only need
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
new file mode 100644
index 0000000..049c745
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.backup.restore;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.backup.BackupAgent;
+import android.platform.test.annotations.Presubmit;
+import android.system.OsConstants;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.FileMetadata;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FullRestoreEngineTest {
+ private static final String DEFAULT_PACKAGE_NAME = "package";
+ private static final String DEFAULT_DOMAIN_NAME = "domain";
+ private static final String NEW_PACKAGE_NAME = "new_package";
+ private static final String NEW_DOMAIN_NAME = "new_domain";
+
+ private FullRestoreEngine mRestoreEngine;
+
+ @Before
+ public void setUp() {
+ mRestoreEngine = new FullRestoreEngine();
+ }
+
+ @Test
+ public void shouldSkipReadOnlyDir_skipsAllReadonlyDirsAndTheirChildren() {
+ // Create the file tree.
+ TestFile[] testFiles = new TestFile[] {
+ TestFile.dir("root"),
+ TestFile.file("root/auth_token"),
+ TestFile.dir("root/media"),
+ TestFile.file("root/media/picture1.png"),
+ TestFile.file("root/push_token.txt"),
+ TestFile.dir("root/read-only-dir-1").markReadOnly().expectSkipped(),
+ TestFile.dir("root/read-only-dir-1/writable-subdir").expectSkipped(),
+ TestFile.file("root/read-only-dir-1/writable-subdir/writable-file").expectSkipped(),
+ TestFile.dir("root/read-only-dir-1/writable-subdir/read-only-subdir-2")
+ .markReadOnly().expectSkipped(),
+ TestFile.file("root/read-only-dir-1/writable-file").expectSkipped(),
+ TestFile.file("root/random-stuff.txt"),
+ TestFile.dir("root/database"),
+ TestFile.file("root/database/users.db"),
+ TestFile.dir("root/read-only-dir-2").markReadOnly().expectSkipped(),
+ TestFile.file("root/read-only-dir-2/writable-file-1").expectSkipped(),
+ TestFile.file("root/read-only-dir-2/writable-file-2").expectSkipped(),
+ };
+
+ assertCorrectItemsAreSkipped(testFiles);
+ }
+
+ @Test
+ public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSamePackage() {
+ TestFile[] testFiles = new TestFile[]{
+ TestFile.dir("read-only-dir").markReadOnly().expectSkipped(),
+ TestFile.file("read-only-dir/file").expectSkipped(),
+ TestFile.file("read-only-dir/file-from-different-package")
+ .setPackage(NEW_PACKAGE_NAME),
+ };
+
+ assertCorrectItemsAreSkipped(testFiles);
+ }
+
+ @Test
+ public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSameDomain() {
+ TestFile[] testFiles = new TestFile[]{
+ TestFile.dir("read-only-dir").markReadOnly().expectSkipped(),
+ TestFile.file("read-only-dir/file").expectSkipped(),
+ TestFile.file("read-only-dir/file-from-different-domain")
+ .setDomain(NEW_DOMAIN_NAME),
+ };
+
+ assertCorrectItemsAreSkipped(testFiles);
+ }
+
+ private void assertCorrectItemsAreSkipped(TestFile[] testFiles) {
+ // Verify all directories marked with .expectSkipped are skipped.
+ for (TestFile testFile : testFiles) {
+ boolean actualExcluded = mRestoreEngine.shouldSkipReadOnlyDir(testFile.mMetadata);
+ boolean expectedExcluded = testFile.mShouldSkip;
+ assertWithMessage(testFile.mMetadata.path).that(actualExcluded).isEqualTo(
+ expectedExcluded);
+ }
+ }
+
+ private static class TestFile {
+ private final FileMetadata mMetadata;
+ private boolean mShouldSkip;
+
+ static TestFile dir(String path) {
+ return new TestFile(path, BackupAgent.TYPE_DIRECTORY);
+ }
+
+ static TestFile file(String path) {
+ return new TestFile(path, BackupAgent.TYPE_FILE);
+ }
+
+ TestFile markReadOnly() {
+ mMetadata.mode = 0;
+ return this;
+ }
+
+ TestFile expectSkipped() {
+ mShouldSkip = true;
+ return this;
+ }
+
+ TestFile setPackage(String packageName) {
+ mMetadata.packageName = packageName;
+ return this;
+ }
+
+ TestFile setDomain(String domain) {
+ mMetadata.domain = domain;
+ return this;
+ }
+
+ private TestFile(String path, int type) {
+ FileMetadata metadata = new FileMetadata();
+ metadata.path = path;
+ metadata.type = type;
+ metadata.packageName = DEFAULT_PACKAGE_NAME;
+ metadata.domain = DEFAULT_DOMAIN_NAME;
+ metadata.mode = OsConstants.S_IWUSR; // Mark as writable.
+ mMetadata = metadata;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
index 1d9ea4b..0b144dc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -41,6 +41,8 @@
@RunWith(JUnit4.class)
public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest {
+ private static final String SDK_INT_PLUS_ONE = "" + (Build.VERSION.SDK_INT + 1);
+ private static final String SDK_INT_PLUS_TWO = "" + (Build.VERSION.SDK_INT + 2);
private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries =
new ArrayMap<>(8);
@@ -51,14 +53,19 @@
private void installSharedLibraries() throws Exception {
mSharedLibraries.clear();
- insertLibrary("foo", 0, 0);
- insertLibrary("fooBcpSince30", 30, 0);
- insertLibrary("fooBcpBefore30", 0, 30);
- insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0);
+ insertLibrary("foo", null, null);
+ insertLibrary("fooBcpSince30", "30", null);
+ insertLibrary("fooBcpBefore30", null, "30");
+ // simulate libraries being added to the BCP in a future release
+ insertLibrary("fooSinceFuture", SDK_INT_PLUS_ONE, null);
+ insertLibrary("fooSinceFutureCodename", "Z", null);
+ // simulate libraries being removed from the BCP in a future release
+ insertLibrary("fooBcpBeforeFuture", null, SDK_INT_PLUS_ONE);
+ insertLibrary("fooBcpBeforeFutureCodename", null, "Z");
}
- private void insertLibrary(String libraryName, int onBootclasspathSince,
- int onBootclasspathBefore) {
+ private void insertLibrary(String libraryName, String onBootclasspathSince,
+ String onBootclasspathBefore) {
mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry(
libraryName,
"foo.jar",
@@ -112,7 +119,7 @@
}
@Test
- public void testBcpSince11kNotAppliedWithoutLibrary() {
+ public void testBcpSinceFutureNotAppliedWithoutLibrary() {
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
.hideAsParsed());
@@ -128,15 +135,17 @@
}
@Test
- public void testBcpSince11kNotAppliedWithLibrary() {
+ public void testBcpSinceFutureNotAppliedWithLibrary() {
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
- .addUsesLibrary("fooFromFuture")
+ .addUsesLibrary("fooSinceFuture")
+ .addUsesLibrary("fooSinceFutureCodename")
.hideAsParsed());
AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
- .addUsesLibrary("fooFromFuture")
+ .addUsesLibrary("fooSinceFuture")
+ .addUsesLibrary("fooSinceFutureCodename")
.hideAsParsed())
.hideAsFinal();
@@ -183,7 +192,7 @@
*/
@Test
public void testBcpRemovedThenAddedPast() {
- insertLibrary("fooBcpRemovedThenAdded", 30, 28);
+ insertLibrary("fooBcpRemovedThenAdded", "30", "28");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.N)
@@ -207,7 +216,8 @@
*/
@Test
public void testBcpRemovedThenAddedMiddle_targetQ() {
- insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+ insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
+ insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
@@ -217,6 +227,7 @@
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary("fooBcpRemovedThenAdded")
.addUsesLibrary("fooBcpBefore30")
+ .addUsesLibrary("fooBcpRemovedThenAddedCodename")
.hideAsParsed())
.hideAsFinal();
@@ -232,7 +243,8 @@
*/
@Test
public void testBcpRemovedThenAddedMiddle_targetR() {
- insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+ insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
+ insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
@@ -256,7 +268,8 @@
*/
@Test
public void testBcpRemovedThenAddedMiddle_targetR_usingLib() {
- insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+ insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
+ insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
@@ -274,6 +287,82 @@
checkBackwardsCompatibility(before, after);
}
+ /**
+ * Test a library that was first removed from the BCP [to a mainline module] and later was
+ * moved back to the BCP via a mainline module update. Both things happening in future SDKs.
+ */
+ @Test
+ public void testBcpRemovedThenAddedFuture() {
+ insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE);
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library is still in the BCP
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Test a library that was first removed from the BCP [to a mainline module] and later was
+ * moved back to the BCP via a mainline module update. Both things happening in future SDKs.
+ */
+ @Test
+ public void testBcpRemovedThenAddedFuture_usingLib() {
+ insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE);
+
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+ .addUsesLibrary("fooBcpRemovedThenAdded")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library was removed from the BCP
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpBeforeFuture() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .addUsesLibrary("fooBcpBeforeFuture")
+ .addUsesLibrary("fooBcpBeforeFutureCodename")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library was removed from the BCP
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpBeforeFuture_futureTargetSdk() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+ .addUsesLibrary("fooBcpBeforeFuture")
+ .addUsesLibrary("fooBcpBeforeFutureCodename")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library was removed from the BCP
+ checkBackwardsCompatibility(before, after);
+ }
+
private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
checkBackwardsCompatibility(before, after,
() -> new ApexSharedLibraryUpdater(mSharedLibraries));
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index bfdffc0..20486b3 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -429,18 +429,40 @@
public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException {
String contents =
"<permissions>\n"
- + " <library \n"
- + " name=\"foo\"\n"
- + " file=\"" + mFooJar + "\"\n"
- + " on-bootclasspath-before=\"10\"\n"
- + " on-bootclasspath-since=\"20\"\n"
- + " />\n\n"
- + " </permissions>";
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"" + mFooJar + "\"\n"
+ + " on-bootclasspath-before=\"10\"\n"
+ + " on-bootclasspath-since=\"20\"\n"
+ + " />\n\n"
+ + " </permissions>";
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
- assertThat(entry.onBootclasspathBefore).isEqualTo(10);
- assertThat(entry.onBootclasspathSince).isEqualTo(20);
+ assertThat(entry.onBootclasspathBefore).isEqualTo("10");
+ assertThat(entry.onBootclasspathSince).isEqualTo("20");
+ }
+
+ /**
+ * Tests that readPermissions works correctly for a library with on-bootclasspath-before
+ * and on-bootclasspath-since that uses codenames.
+ */
+ @Test
+ public void readPermissions_allowLibs_parsesSimpleLibraryWithCodenames() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"" + mFooJar + "\"\n"
+ + " on-bootclasspath-before=\"Q\"\n"
+ + " on-bootclasspath-since=\"W\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
+ assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+ assertThat(entry.onBootclasspathSince).isEqualTo("W");
}
/**
@@ -461,8 +483,8 @@
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
- assertThat(entry.onBootclasspathBefore).isEqualTo(10);
- assertThat(entry.onBootclasspathSince).isEqualTo(20);
+ assertThat(entry.onBootclasspathBefore).isEqualTo("10");
+ assertThat(entry.onBootclasspathSince).isEqualTo("20");
}
/**
@@ -543,12 +565,20 @@
*/
@Test
public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException {
+ // depending on whether this test is running before or after finalization, we need to
+ // pass a different parameter
+ String parameter;
+ if ("REL".equals(Build.VERSION.CODENAME)) {
+ parameter = "" + Build.VERSION.SDK_INT;
+ } else {
+ parameter = "ZZZ";
+ }
String contents =
"<permissions>\n"
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
- + " max-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n"
+ + " max-device-sdk=\"" + parameter + "\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index f3d494d..4fbf006 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -602,10 +602,15 @@
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
VibratorManagerService service = createSystemReadyService();
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ // The haptic feedback should be ignored in low power, but not the ringtone. The end
+ // of the test asserts which actual effects ended up playing.
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
service, TEST_TIMEOUT_MILLIS));
+ // Allow the ringtone to complete, as the other vibrations won't cancel it.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
@@ -815,6 +820,29 @@
}
@Test
+ public void vibrate_withOngoingRingtoneVibration_ignoresEffect() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ VibrationEffect alarmEffect = VibrationEffect.createWaveform(
+ new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
+ vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_RINGTONE).build());
+
+ // VibrationThread will start this vibration async, so wait before checking it started.
+ assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // Wait before checking it never played a second effect.
+ assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
+ service, /* timeout= */ 50));
+ }
+
+ @Test
public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
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 9902e83..908de34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1139,18 +1139,8 @@
true /* createdByOrganizer */);
sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
- starter.startActivityInner(
- /* r */targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */true,
- /* options */null,
- /* inTask */null,
- /* inTaskFragment */ taskFragment,
- /* restrictedBgActivity */false,
- /* intentGrants */null);
+ startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+ null /* inTask */, taskFragment);
assertFalse(taskFragment.hasChild());
}
@@ -1167,18 +1157,8 @@
taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID,
"system_uid");
- starter.startActivityInner(
- /* r */targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */true,
- /* options */null,
- /* inTask */null,
- /* inTaskFragment */ taskFragment,
- /* restrictedBgActivity */false,
- /* intentGrants */null);
+ startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+ null /* inTask */, taskFragment);
assertTrue(taskFragment.hasChild());
}
@@ -1195,18 +1175,8 @@
taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
targetRecord.getUid(), "test_process_name");
- starter.startActivityInner(
- /* r */targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */true,
- /* options */null,
- /* inTask */null,
- /* inTaskFragment */ taskFragment,
- /* restrictedBgActivity */false,
- /* intentGrants */null);
+ startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+ null /* inTask */, taskFragment);
assertTrue(taskFragment.hasChild());
}
@@ -1231,18 +1201,8 @@
doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any());
doReturn(signingDetails).when(androidPackage).getSigningDetails();
- starter.startActivityInner(
- /* r */targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */true,
- /* options */null,
- /* inTask */null,
- /* inTaskFragment */ taskFragment,
- /* restrictedBgActivity */false,
- /* intentGrants */null);
+ startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+ null /* inTask */, taskFragment);
assertTrue(taskFragment.hasChild());
}
@@ -1258,23 +1218,30 @@
targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
- starter.startActivityInner(
- /* r */targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */true,
- /* options */null,
- /* inTask */null,
- /* inTaskFragment */ taskFragment,
- /* restrictedBgActivity */false,
- /* intentGrants */null);
+ startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+ null /* inTask */, taskFragment);
assertTrue(taskFragment.hasChild());
}
@Test
+ public void testStartActivityInner_inTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ // Simulate an app uses AppTask to create a non-attached task, and then it requests to
+ // start activity in the task.
+ final Task inTask = new TaskBuilder(mSupervisor).setTaskDisplayArea(null).setTaskId(123)
+ .build();
+ inTask.inRecents = true;
+ assertFalse(inTask.isAttached());
+ final ActivityRecord target = new ActivityBuilder(mAtm).build();
+ startActivityInner(starter, target, null /* source */, null /* options */, inTask,
+ null /* inTaskFragment */);
+
+ assertTrue(inTask.isAttached());
+ assertEquals(inTask, target.getTask());
+ }
+
+ @Test
public void testLaunchCookie_newAndExistingTask() {
final ActivityStarter starter = prepareStarter(0, false);
@@ -1322,21 +1289,20 @@
// Start the target launch-into-pip activity from a source
final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
- starter.startActivityInner(
- /* r */ targetRecord,
- /* sourceRecord */ sourceRecord,
- /* voiceSession */ null,
- /* voiceInteractor */ null,
- /* startFlags */ 0,
- /* doResume */ true,
- /* options */ opts,
- /* inTask */ null,
- /* inTaskFragment */ null,
- /* restrictedBgActivity */ false,
- /* intentGrants */ null);
+ startActivityInner(starter, targetRecord, sourceRecord, opts,
+ null /* inTask */, null /* inTaskFragment */);
// Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord.
assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull();
assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord);
}
+
+ private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
+ ActivityRecord source, ActivityOptions options, Task inTask,
+ TaskFragment inTaskFragment) {
+ starter.startActivityInner(target, source, null /* voiceSession */,
+ null /* voiceInteractor */, 0 /* startFlags */, true /* doResume */,
+ options, inTask, inTaskFragment, false /* restrictedBgActivity */,
+ null /* intentGrants */);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
similarity index 74%
rename from services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index f968999..a8282600 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.START_ABORTED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -26,6 +27,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -45,13 +47,13 @@
import java.util.Set;
/**
- * Tests for the {@link DisplayWindowPolicyControllerHelper} class.
+ * Tests for the {@link DisplayWindowPolicyController} class.
*
* Build/Install/Run:
- * atest WmTests:DisplayWindowPolicyControllerHelperTests
+ * atest WmTests:DisplayWindowPolicyControllerTests
*/
@RunWith(WindowTestRunner.class)
-public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase {
+public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
private static final int TEST_USER_0_ID = 0;
private static final int TEST_USER_1_ID = 10;
@@ -152,8 +154,51 @@
assertTrue(mSecondaryDisplay.mDwpcHelper.isWindowingModeSupported(WINDOWING_MODE_PINNED));
}
+ @Test
+ public void testInterestedWindowFlags() {
+ final int fakeFlag1 = 0x00000010;
+ final int fakeFlag2 = 0x00000100;
+ final int fakeSystemFlag1 = 0x00000010;
+ final int fakeSystemFlag2 = 0x00000100;
+
+ mDwpc.setInterestedWindowFlags(fakeFlag1, fakeSystemFlag1);
+
+ assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag1));
+ assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag2));
+ assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag1));
+ assertFalse(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag2));
+ }
+
+ @Test
+ public void testCanContainActivities() {
+ ActivityStarter starter = new ActivityStarter(mock(ActivityStartController.class), mAtm,
+ mSupervisor, mock(ActivityStartInterceptor.class));
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(mSecondaryDisplay).build();
+ final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord disallowedRecord =
+ new ActivityBuilder(mAtm).setComponent(mDwpc.DISALLOWED_ACTIVITY).build();
+
+ int result = starter.startActivityInner(
+ disallowedRecord,
+ sourceRecord,
+ /* voiceSession */null,
+ /* voiceInteractor */ null,
+ /* startFlags */ 0,
+ /* doResume */true,
+ /* options */null,
+ /* inTask */null,
+ /* inTaskFragment */ null,
+ /* restrictedBgActivity */false,
+ /* intentGrants */null);
+
+ assertEquals(result, START_ABORTED);
+ }
+
private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController {
+ public ComponentName DISALLOWED_ACTIVITY =
+ new ComponentName("fake.package", "DisallowedActivity");
+
ComponentName mTopActivity = null;
int mTopActivityUid = UserHandle.USER_NULL;
ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -161,7 +206,14 @@
@Override
public boolean canContainActivities(@NonNull List<ActivityInfo> activities,
@WindowConfiguration.WindowingMode int windowingMode) {
- return false;
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ final ActivityInfo aInfo = activities.get(i);
+ if (aInfo.getComponentName().equals(DISALLOWED_ACTIVITY)) {
+ return false;
+ }
+ }
+ return true;
}
@Override
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8d7fab4..a2266fb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1959,6 +1959,13 @@
"nr_advanced_threshold_bandwidth_khz_int";
/**
+ * Boolean indicating if operator name should be shown in the status bar
+ * @hide
+ */
+ public static final String KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL =
+ "show_operator_name_in_statusbar_bool";
+
+ /**
* The string is used to filter redundant string from PLMN Network Name that's supplied by
* specific carrier.
*
@@ -8913,6 +8920,7 @@
sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false);
sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 3843a62..249f740 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -21,6 +21,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -227,7 +230,6 @@
this.mIccIdAccessRestricted = iccIdAccessRestricted;
}
-
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -261,7 +263,7 @@
+ ", mCardId="
+ mCardId
+ ", mEid="
- + mEid
+ + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
+ ", mPhysicalSlotIndex="
+ mPhysicalSlotIndex
+ ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 17ce450..dd3639a 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -279,7 +279,7 @@
+ ", mIsEuicc="
+ mIsEuicc
+ ", mCardId="
- + mCardId
+ + SubscriptionInfo.givePrintableIccid(mCardId)
+ ", cardState="
+ mCardStateInfo
+ ", mIsExtendedApduSupported="
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
new file mode 100644
index 0000000..172c433
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.flicker.helpers
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class ImeEditorPopupDialogAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ private val rotation: Int,
+ private val imePackageName: String = IME_PACKAGE,
+ launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+) : ImeAppHelper(instr, launcherName, component) {
+ override fun openIME(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper?
+ ) {
+ val editText = device.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT)
+
+ require(editText != null) {
+ "Text field not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. in split screen)"
+ }
+ editText.click()
+ waitIMEShown(device, wmHelper)
+ }
+
+ fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+ val dismissButton = uiDevice.wait(
+ Until.findObject(By.text("Dismiss")), FIND_TIMEOUT)
+
+ // Pressing back key to dismiss the dialog
+ if (dismissButton != null) {
+ dismissButton.click()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
new file mode 100644
index 0000000..bff099e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.traces.region.RegionSubject
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group4
+class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation, testSpec.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ imeTestApp.launchViaIntent(wmHelper)
+ imeTestApp.openIME(device, wmHelper)
+ }
+ }
+ transitions {
+ imeTestApp.dismissDialog(wmHelper)
+ instrumentation.uiAutomation.syncInputTransactions()
+ }
+ teardown {
+ eachRun {
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ imeTestApp.exit()
+ }
+ }
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ fun imeWindowBecameInvisible() = testSpec.imeWindowBecomesInvisible()
+
+ @Postsubmit
+ @Test
+ fun imeLayerAndImeSnapshotVisibleOnScreen() {
+ testSpec.assertLayers {
+ this.isVisible(FlickerComponentName.IME)
+ .then()
+ .isVisible(FlickerComponentName.IME_SNAPSHOT)
+ .then()
+ .isInvisible(FlickerComponentName.IME)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun imeSnapshotAssociatedOnAppVisibleRegion() {
+ testSpec.assertLayers {
+ this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") {
+ val imeSnapshotLayers = it.subjects.filter {
+ subject -> subject.name.contains(
+ FlickerComponentName.IME_SNAPSHOT.toLayerName()) && subject.isVisible
+ }
+ if (imeSnapshotLayers.isNotEmpty()) {
+ val visibleAreas = imeSnapshotLayers.mapNotNull { imeSnapshotLayer ->
+ imeSnapshotLayer.layer?.visibleRegion }.toTypedArray()
+ val imeVisibleRegion = RegionSubject.assertThat(visibleAreas, this, timestamp)
+ val appVisibleRegion = it.visibleRegion(imeTestApp.component)
+ if (imeVisibleRegion.region.isNotEmpty) {
+ imeVisibleRegion.coversAtMost(appVisibleRegion.region)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 2,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 7f49663..1b60403 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -79,7 +79,7 @@
/**
* Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
*/
- @Presubmit
+ @FlakyTest(bugId = 227142436)
@Test
fun imeLayerExistsEnd() {
testSpec.assertLayersEnd {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index 9c9dedc2..4b8a8c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.quickswitch
import android.platform.test.annotations.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group1
@@ -44,6 +45,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
+@FlakyTest(bugId = 228009808)
open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter)
: QuickSwitchBetweenTwoAppsForwardTest(testSpec) {
@Before
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 739fe02..7f513b2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -119,5 +119,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".ImeEditorPopupDialogActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/CutoutShortEdges"
+ android:label="ImeEditorPopupDialogActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 3040a09..18c95cf 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -56,6 +56,7 @@
public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+
public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
@@ -65,4 +66,10 @@
public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
+
+ public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME =
+ "ImeEditorPopupDialogActivity";
+ public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
new file mode 100644
index 0000000..a8613f5
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.flicker.testapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+public class ImeEditorPopupDialogActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ setContentView(R.layout.activity_simple);
+
+ final EditText editText = new EditText(this);
+ editText.setHint("focused editText");
+ final AlertDialog dialog = new AlertDialog.Builder(this)
+ .setView(editText)
+ .setPositiveButton("Dismiss", (d, which) -> d.dismiss())
+ .create();
+ dialog.show();
+ }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..bfb3285 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -165,6 +165,7 @@
],
proto: {
export_proto_headers: true,
+ type: "full",
},
defaults: ["aapt2_defaults"],
}