Merge "Remove the restricted user enforcement in getAppExclusionList" into tm-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d18a9c7..f5ee467 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1155,9 +1155,11 @@
package android.hardware.devicestate {
public final class DeviceStateManager {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
method @NonNull public int[] getSupportedStates();
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 30aa4db..bdd45e6 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,6 +16,7 @@
package android.hardware.devicestate;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -115,6 +116,52 @@
}
/**
+ * Submits a {@link DeviceStateRequest request} to override the base state of the device. This
+ * should only be used for testing, where you want to simulate the physical change to the
+ * device state.
+ * <p>
+ * By default, the request is kept active until one of the following occurs:
+ * <ul>
+ * <li>The physical state of the device changes</li>
+ * <li>The system deems the request can no longer be honored, for example if the requested
+ * state becomes unsupported.
+ * <li>A call to {@link #cancelBaseStateOverride}.
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be canceled.
+ * </ul>
+ *
+ * Submitting a base state override request may not cause any change in the presentation
+ * of the system if there is an emulated request made through {@link #requestState}, as the
+ * emulated override requests take priority.
+ *
+ * @throws IllegalArgumentException if the requested state is unsupported.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ *
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable DeviceStateRequest.Callback callback) {
+ mGlobal.requestBaseStateOverride(request, executor, callback);
+ }
+
+ /**
+ * Cancels the active {@link DeviceStateRequest} previously submitted with a call to
+ * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * <p>
+ * This method is noop if there is no base state request currently active.
+ *
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelBaseStateOverride() {
+ mGlobal.cancelBaseStateOverride();
+ }
+
+ /**
* Registers a callback to receive notifications about changes in device state.
*
* @param executor the executor to process notifications.
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index aba538f..738045d 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
@@ -81,6 +82,7 @@
@VisibleForTesting
public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
mDeviceStateManager = deviceStateManager;
+ registerCallbackIfNeededLocked();
}
/**
@@ -116,27 +118,22 @@
* DeviceStateRequest.Callback)
* @see DeviceStateRequest
*/
+ @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+ conditional = true)
public void requestState(@NonNull DeviceStateRequest request,
@Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
- if (callback == null && executor != null) {
- throw new IllegalArgumentException("Callback must be supplied with executor.");
- } else if (executor == null && callback != null) {
- throw new IllegalArgumentException("Executor must be supplied with callback.");
- }
-
+ DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
+ executor);
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
if (findRequestTokenLocked(request) != null) {
// This request has already been submitted.
return;
}
-
// Add the request wrapper to the mRequests array before requesting the state as the
// callback could be triggered immediately if the mDeviceStateManager IBinder is in the
// same process as this instance.
IBinder token = new Binder();
- mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+ mRequests.put(token, requestWrapper);
try {
mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
@@ -153,10 +150,10 @@
*
* @see DeviceStateManager#cancelStateRequest
*/
+ @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+ conditional = true)
public void cancelStateRequest() {
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
try {
mDeviceStateManager.cancelStateRequest();
} catch (RemoteException ex) {
@@ -166,6 +163,56 @@
}
/**
+ * Submits a {@link DeviceStateRequest request} to modify the base state of the device.
+ *
+ * @see DeviceStateManager#requestBaseStateOverride(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
+ @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
+ DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
+ executor);
+ synchronized (mLock) {
+ if (findRequestTokenLocked(request) != null) {
+ // This request has already been submitted.
+ return;
+ }
+ // Add the request wrapper to the mRequests array before requesting the state as the
+ // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+ // same process as this instance.
+ IBinder token = new Binder();
+ mRequests.put(token, requestWrapper);
+
+ try {
+ mDeviceStateManager.requestBaseStateOverride(token, request.getState(),
+ request.getFlags());
+ } catch (RemoteException ex) {
+ mRequests.remove(token);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ *
+ * @see DeviceStateManager#cancelBaseStateOverride
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelBaseStateOverride() {
+ synchronized (mLock) {
+ try {
+ mDeviceStateManager.cancelBaseStateOverride();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Registers a callback to receive notifications about changes in device state.
*
* @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback)
@@ -179,9 +226,6 @@
// This callback is already registered.
return;
}
-
- registerCallbackIfNeededLocked();
-
// Add the callback wrapper to the mCallbacks array after registering the callback as
// the callback could be triggered immediately if the mDeviceStateManager IBinder is in
// the same process as this instance.
@@ -357,6 +401,8 @@
DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
@Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ validateRequestWrapperParameters(callback, executor);
+
mRequest = request;
mCallback = callback;
mExecutor = executor;
@@ -377,5 +423,14 @@
mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest));
}
+
+ private void validateRequestWrapperParameters(
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ if (callback == null && executor != null) {
+ throw new IllegalArgumentException("Callback must be supplied with executor.");
+ } else if (executor == null && callback != null) {
+ throw new IllegalArgumentException("Executor must be supplied with callback.");
+ }
+ }
}
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index e450e42..7175eae 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -41,6 +41,10 @@
* previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
* call to this method.
*
+ * Requesting a state does not cancel a base state override made through
+ * {@link #requestBaseStateOverride}, but will still attempt to put the device into the
+ * supplied {@code state}.
+ *
* @param token the request token provided
* @param state the state of device the request is asking for
* @param flags any flags that correspond to the request
@@ -50,14 +54,53 @@
* @throws IllegalStateException if the supplied {@code token} has already been registered.
* @throws IllegalArgumentException if the supplied {@code state} is not supported.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
void requestState(IBinder token, int state, int flags);
/**
* Cancels the active request previously submitted with a call to
- * {@link #requestState(IBinder, int, int)}.
+ * {@link #requestState(IBinder, int, int)}. Will have no effect on any base state override that
+ * was previously requested with {@link #requestBaseStateOverride}.
*
* @throws IllegalStateException if a callback has not yet been registered for the calling
* process.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
void cancelStateRequest();
+
+ /**
+ * Requests that the device's base state be overridden to the supplied {@code state}. A callback
+ * <b>MUST</b> have been previously registered with
+ * {@link #registerCallback(IDeviceStateManagerCallback)} before a call to this method.
+ *
+ * This method should only be used for testing, when you want to simulate the device physically
+ * changing states. If you are looking to change device state for a feature, where the system
+ * should still be aware that the physical state is different than the emulated state, use
+ * {@link #requestState}.
+ *
+ * @param token the request token provided
+ * @param state the state of device the request is asking for
+ * @param flags any flags that correspond to the request
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ * @throws IllegalStateException if the supplied {@code token} has already been registered.
+ * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void requestBaseStateOverride(IBinder token, int state, int flags);
+
+ /**
+ * Cancels the active base state request previously submitted with a call to
+ * {@link #overrideBaseState(IBinder, int, int)}.
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void cancelBaseStateOverride();
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 33ea2e4..641d1a1 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -125,8 +125,15 @@
/** The container attaches work profile thumbnail for cross profile animation. */
public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13;
+ /**
+ * Whether the window is covered by an app starting window. This is different from
+ * {@link #FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT} which is only set on the Activity window
+ * that contains the starting window.
+ */
+ public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 14;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 15;
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
@@ -145,6 +152,7 @@
FLAG_WILL_IME_SHOWN,
FLAG_CROSS_PROFILE_OWNER_THUMBNAIL,
FLAG_CROSS_PROFILE_WORK_THUMBNAIL,
+ FLAG_IS_BEHIND_STARTING_WINDOW,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -351,6 +359,9 @@
if ((flags & FLAG_FILLS_TASK) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FILLS_TASK");
}
+ if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index 354eb62..be0b729 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -51,6 +51,7 @@
import android.util.TypedValue;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
@@ -69,6 +70,7 @@
private static final SynchronizedPool<SimpleIconFactory> sPool =
new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
+ private static boolean sPoolEnabled = true;
private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
private static final float BLUR_FACTOR = 1.5f / 48;
@@ -92,7 +94,7 @@
*/
@Deprecated
public static SimpleIconFactory obtain(Context ctx) {
- SimpleIconFactory instance = sPool.acquire();
+ SimpleIconFactory instance = sPoolEnabled ? sPool.acquire() : null;
if (instance == null) {
final ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
final int iconDpi = (am == null) ? 0 : am.getLauncherLargeIconDensity();
@@ -106,6 +108,17 @@
return instance;
}
+ /**
+ * Enables or disables SimpleIconFactory objects pooling. It is enabled in production, you
+ * could use this method in tests and disable the pooling to make the icon rendering more
+ * deterministic because some sizing parameters will not be cached. Please ensure that you
+ * reset this value back after finishing the test.
+ */
+ @VisibleForTesting
+ public static void setPoolEnabled(boolean poolEnabled) {
+ sPoolEnabled = poolEnabled;
+ }
+
private static int getAttrDimFromContext(Context ctx, @AttrRes int attrId, String errorMsg) {
final Resources res = ctx.getResources();
TypedValue outVal = new TypedValue();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 1235b60..a4da8de4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1522,7 +1522,8 @@
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT})
+ STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT,
+ SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1575,6 +1576,12 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80;
/**
+ * Some authentication is required because the trustagent either timed out or was disabled
+ * manually.
+ */
+ public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100;
+
+ /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 4c247427..9e39e13 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,11 +16,12 @@
package android.hardware.devicestate;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -36,7 +37,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
import java.util.HashSet;
import java.util.Set;
@@ -59,6 +59,7 @@
public void setUp() {
mService = new TestDeviceStateManagerService();
mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
+ assertFalse(mService.mCallbacks.isEmpty());
}
@Test
@@ -79,8 +80,8 @@
verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback2).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the supported states and verify callback
mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE });
@@ -88,8 +89,8 @@
verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE });
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the base state and verify callback
mService.setBaseState(OTHER_DEVICE_STATE);
@@ -98,8 +99,8 @@
verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback2).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the requested state and verify callback
DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
@@ -120,7 +121,7 @@
verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates()));
verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback);
@@ -130,29 +131,19 @@
}
@Test
- public void submittingRequestRegistersCallback() {
- assertTrue(mService.mCallbacks.isEmpty());
-
- DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
- mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
-
- assertFalse(mService.mCallbacks.isEmpty());
- }
-
- @Test
public void submitRequest() {
DeviceStateCallback callback = mock(DeviceStateCallback.class);
mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
ConcurrentUtils.DIRECT_EXECUTOR);
verify(callback).onStateChanged(eq(mService.getBaseState()));
- Mockito.reset(callback);
+ reset(callback);
DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.cancelStateRequest();
@@ -160,6 +151,69 @@
}
@Test
+ public void submitBaseStateOverrideRequest() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ reset(callback);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
+ null /* callback */);
+
+ verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelBaseStateOverride();
+
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ }
+
+ @Test
+ public void submitBaseAndEmulatedStateOverride() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ reset(callback);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
+ null /* callback */);
+
+ verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+ reset(callback);
+
+ DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder(
+ DEFAULT_DEVICE_STATE).build();
+
+ mDeviceStateManagerGlobal.requestState(secondRequest, null, null);
+
+ assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+ verify(callback).onStateChanged(eq(DEFAULT_DEVICE_STATE));
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelStateRequest();
+
+ verify(callback).onStateChanged(OTHER_DEVICE_STATE);
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelBaseStateOverride();
+
+ verify(callback).onBaseStateChanged(DEFAULT_DEVICE_STATE);
+ verify(callback).onStateChanged(DEFAULT_DEVICE_STATE);
+ }
+
+ @Test
public void verifyDeviceStateRequestCallbacksCalled() {
DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class);
@@ -169,7 +223,7 @@
callback /* callback */);
verify(callback).onRequestActivated(eq(request));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.cancelStateRequest();
@@ -203,13 +257,16 @@
private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
private int mBaseState = DEFAULT_DEVICE_STATE;
private Request mRequest;
+ private Request mBaseStateRequest;
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
private DeviceStateInfo getInfo() {
+ final int mergedBaseState = mBaseStateRequest == null
+ ? mBaseState : mBaseStateRequest.state;
final int mergedState = mRequest == null
- ? mBaseState : mRequest.state;
- return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
+ ? mergedBaseState : mRequest.state;
+ return new DeviceStateInfo(mSupportedStates, mergedBaseState, mergedState);
}
private void notifyDeviceStateInfoChanged() {
@@ -238,7 +295,7 @@
try {
callback.onDeviceStateInfoChanged(getInfo());
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
@@ -249,7 +306,7 @@
try {
callback.onRequestCanceled(mRequest.token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
}
@@ -262,7 +319,7 @@
try {
callback.onRequestActive(token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
}
@@ -275,7 +332,46 @@
try {
callback.onRequestCanceled(token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
+ }
+ }
+ notifyDeviceStateInfoChanged();
+ }
+
+ @Override
+ public void requestBaseStateOverride(IBinder token, int state, int flags) {
+ if (mBaseStateRequest != null) {
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(mBaseStateRequest.token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ final Request request = new Request(token, state, flags);
+ mBaseStateRequest = request;
+ notifyDeviceStateInfoChanged();
+
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestActive(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public void cancelBaseStateOverride() throws RemoteException {
+ IBinder token = mBaseStateRequest.token;
+ mBaseStateRequest = null;
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
notifyDeviceStateInfoChanged();
@@ -296,7 +392,7 @@
}
public int getBaseState() {
- return mBaseState;
+ return getInfo().baseState;
}
public int getMergedState() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 7e9c418..fb0a9db 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -41,7 +41,7 @@
// TODO(b/241126279) Introduce constants to better version functionality
@Override
public int getVendorApiLevel() {
- return 2;
+ return 1;
}
/**
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 93fc93f..f790787 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -18,14 +18,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"Închide"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Extinde"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesați ecranul împărțit"</string>
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesează ecranul împărțit"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string>
- <string name="pip_play" msgid="3496151081459417097">"Redați"</string>
- <string name="pip_pause" msgid="690688849510295232">"Întrerupeți"</string>
+ <string name="pip_play" msgid="3496151081459417097">"Redă"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Întrerupe"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
@@ -48,17 +48,17 @@
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"Folosirea modului cu o mână"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activați modul cu o mână"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activează modul cu o mână"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Părăsiți modul cu o mână"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setări pentru baloanele <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Suplimentar"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adaugă înapoi în stivă"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
- <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mutați în stânga sus"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mutați în dreapta sus"</string>
- <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mutați în stânga jos"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mutați în dreapta jos"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mută în stânga sus"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișați conversația în balon"</string>
@@ -70,7 +70,7 @@
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
<string name="restart_button_description" msgid="6712141648865547958">"Atingeți ca să reporniți aplicația pentru o vizualizare mai bună."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
@@ -80,7 +80,7 @@
<string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string>
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
- <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extindeți pentru mai multe informații"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extinde pentru mai multe informații"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizați"</string>
<string name="minimize_button_text" msgid="271592547935841753">"Minimizează"</string>
<string name="close_button_text" msgid="2913281996024033299">"Închide"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index d88cc00..d150261 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import android.animation.Animator;
import android.animation.ValueAnimator;
@@ -129,11 +130,19 @@
@NonNull
private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters(
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
+ boolean isChangeTransition = false;
for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getMode() == TRANSIT_CHANGE
- && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
- return createChangeAnimationAdapters(info, startTransaction);
+ if (change.hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)) {
+ // Skip the animation if the windows are behind an app starting window.
+ return new ArrayList<>();
}
+ if (!isChangeTransition && change.getMode() == TRANSIT_CHANGE
+ && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
+ isChangeTransition = true;
+ }
+ }
+ if (isChangeTransition) {
+ return createChangeAnimationAdapters(info, startTransaction);
}
if (Transitions.isClosingType(info.getType())) {
return createCloseAnimationAdapters(info);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 033d743..2dc4a04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -87,6 +87,10 @@
return mLoggerSessionId != null;
}
+ public boolean isEnterRequestedByDrag() {
+ return mEnterReason == ENTER_REASON_DRAG;
+ }
+
/**
* May be called before logEnter() to indicate that the session was started from a drag.
*/
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 c8dcf4a..c17f822 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
@@ -18,6 +18,8 @@
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -503,6 +505,12 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
+ // If split still not active, apply windows bounds first to avoid surface reset to
+ // wrong pos by SurfaceAnimator from wms.
+ if (!mMainStage.isActive() && mLogger.isEnterRequestedByDrag()) {
+ updateWindowBounds(mSplitLayout, wct);
+ }
+
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -1109,6 +1117,10 @@
private void addActivityOptions(Bundle opts, StageTaskListener stage) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
+ // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
+ // will be canceled.
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
}
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
@@ -1455,18 +1467,27 @@
}
}
} else if (isSideStage && hasChildren && !mMainStage.isActive()) {
- // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
- onSplitScreenEnter();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
- mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
- mMainStage.activate(wct, true /* includingTopTask */);
- updateWindowBounds(mSplitLayout, wct);
- wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ if (mLogger.isEnterRequestedByDrag()) {
+ prepareEnterSplitScreen(wct);
+ } else {
+ // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
+ onSplitScreenEnter();
+ mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
+ mMainStage.activate(wct, true /* includingTopTask */);
+ updateWindowBounds(mSplitLayout, wct);
+ wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
+ }
+
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
- mSplitLayout.flingDividerToCenter();
+ if (mLogger.isEnterRequestedByDrag()) {
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ } else {
+ mSplitLayout.flingDividerToCenter();
+ }
});
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index a7234c1..98b5912 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -18,7 +18,9 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -27,6 +29,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.animation.Animator;
import android.window.TransitionInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -76,4 +79,18 @@
verify(mController).onAnimationFinished(mTransition);
}
+
+ @Test
+ public void testChangesBehindStartingWindow() {
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createChange();
+ embeddingChange.setFlags(FLAG_IS_BEHIND_STARTING_WINDOW);
+ info.addChange(embeddingChange);
+ final Animator animator = mAnimRunner.createAnimator(
+ info, mStartTransaction, mFinishTransaction,
+ () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
+
+ // The animation should be empty when it is behind starting window.
+ assertEquals(0, animator.getDuration());
+ }
}
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 9240abf..8350870 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
@@ -16,7 +16,10 @@
package com.android.wm.shell.splitscreen;
+import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -28,11 +31,10 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -42,8 +44,10 @@
import android.app.ActivityManager;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Bundle;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -115,7 +119,6 @@
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, Optional.empty()));
- doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
@@ -303,4 +306,16 @@
verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
}
+
+ @Test
+ public void testAddActivityOptions_addsBackgroundActivitiesFlags() {
+ Bundle options = mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN,
+ SPLIT_POSITION_UNDEFINED, null /* options */, null /* wct */);
+
+ assertEquals(options.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, WindowContainerToken.class),
+ mMainStage.mRootTaskInfo.token);
+ assertTrue(options.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED));
+ assertTrue(options.getBoolean(
+ KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION));
+ }
}
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index eb04132..0c04167 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -23,12 +23,12 @@
<string name="summary_watch" msgid="3002344206574997652">"Această aplicație este necesară pentru a gestiona <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> va putea să interacționeze cu notificările dvs. și să vă acceseze permisiunile pentru Telefon, SMS, Agendă, Calendar, Jurnale de apeluri și Dispozitive din apropiere."</string>
<string name="permission_apps" msgid="6142133265286656158">"Aplicații"</string>
<string name="permission_apps_summary" msgid="798718816711515431">"Să redea în stream aplicațiile telefonului"</string>
- <string name="title_app_streaming" msgid="2270331024626446950">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string>
+ <string name="title_app_streaming" msgid="2270331024626446950">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string>
<string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
- <string name="title_computer" msgid="4693714143506569253">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string>
+ <string name="title_computer" msgid="4693714143506569253">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Notificări"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"Poate să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile"</string>
@@ -38,7 +38,7 @@
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
- <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permite"</string>
<string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
<string name="consent_back" msgid="2560683030046918882">"Înapoi"</string>
<string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferați permisiunile pentru aplicații pe ceas"</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index 1f15619..6b793fc 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -29,36 +29,36 @@
<string name="install_failed" msgid="5777824004474125469">"Aplicația nu a fost instalată."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalarea pachetului a fost blocată."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."</string>
- <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta dvs."</string>
- <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Aplicația nu este compatibilă cu televizorul dvs."</string>
- <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul dvs."</string>
+ <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta."</string>
+ <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Aplicația nu este compatibilă cu televizorul."</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul."</string>
<string name="install_failed_invalid_apk" msgid="8581007676422623930">"Aplicația nu a fost instalată deoarece pachetul este nevalid."</string>
- <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tableta dvs."</string>
- <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizorul dvs."</string>
- <string name="install_failed_msg" product="default" msgid="6484461562647915707">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefonul dvs."</string>
+ <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tabletă."</string>
+ <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizor."</string>
+ <string name="install_failed_msg" product="default" msgid="6484461562647915707">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefon."</string>
<string name="launch" msgid="3952550563999890101">"Deschide"</string>
<string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Administratorul nu permite instalarea aplicațiilor obținute din surse necunoscute"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplicațiile necunoscute nu pot fi instalate de acest utilizator"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Acest utilizator nu are permisiunea să instaleze aplicații"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <string name="manage_applications" msgid="5400164782453975580">"Gestionați aplicații"</string>
+ <string name="manage_applications" msgid="5400164782453975580">"Gestionează"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spațiu de stocare insuficient"</string>
- <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberați spațiu și încercați din nou."</string>
+ <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberează spațiu și încearcă din nou."</string>
<string name="app_not_found_dlg_title" msgid="5107924008597470285">"Aplicația nu a fost găsită"</string>
<string name="app_not_found_dlg_text" msgid="5219983779377811611">"Aplicația nu a fost găsită în lista de aplicații instalate."</string>
<string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nepermis"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Utilizatorul actual nu are permisiune pentru a face această dezinstalare."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"Eroare"</string>
<string name="generic_error_dlg_text" msgid="5287861443265795232">"Aplicația nu a putut fi dezinstalată."</string>
- <string name="uninstall_application_title" msgid="4045420072401428123">"Dezinstalați aplicația"</string>
- <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalați actualizarea"</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"Dezinstalează aplicația"</string>
+ <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalează actualizarea"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> face parte din următoarea aplicație:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"Doriți să dezinstalați această aplicație?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Doriți să dezinstalați această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
- <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalați această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"Dezinstalezi această aplicație?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dezinstalezi această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
+ <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalezi această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Doriți să dezinstalați această aplicație din profilul de serviciu?"</string>
- <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiești această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiești această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string>
<string name="uninstall_keep_data" msgid="7002379587465487550">"Păstrează <xliff:g id="SIZE">%1$s</xliff:g> din datele aplicației."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"Dezinstalări în curs"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Dezinstalări nereușite"</string>
@@ -71,10 +71,10 @@
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului pentru <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
<string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Aplicația este necesară unor utilizatori sau profiluri și a fost dezinstalată pentru alții"</string>
- <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Aplicația este necesară pentru profilul dvs. și nu poate fi dezinstalată."</string>
+ <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Aplicația este necesară pentru profilul tău și nu poate fi dezinstalată."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Aplicația este necesară administratorului dispozitivului și nu poate fi dezinstalată."</string>
- <string name="manage_device_administrators" msgid="3092696419363842816">"Gestionați aplicațiile de administrare dispozitiv"</string>
- <string name="manage_users" msgid="1243995386982560813">"Gestionați utilizatorii"</string>
+ <string name="manage_device_administrators" msgid="3092696419363842816">"Gestionează aplicațiile de administrare dispozitiv"</string>
+ <string name="manage_users" msgid="1243995386982560813">"Gestionează utilizatorii"</string>
<string name="uninstall_failed_msg" msgid="2176744834786696012">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi dezinstalată."</string>
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"A apărut o problemă la analizarea pachetului."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
@@ -84,10 +84,10 @@
<string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"Din motive de securitate, tableta dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Din motive de securitate, televizorul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string>
<string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Din motive de securitate, telefonul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
- <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuați"</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonul și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi aplicația, accepți că ești singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele tale cu caracter personal sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
+ <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuă"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Setări"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Se (dez)instalează aplicațiile Wear"</string>
<string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificare de aplicație instalată"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 4c35633..46cba25 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -641,7 +641,7 @@
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
<string name="data_connection_carrier_wifi" msgid="8932949159370130465">"W+"</string>
- <string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік деректер өшірулі"</string>
+ <string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік интернет өшірулі"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Деректерді пайдалануға реттелмеген."</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Телефон жоқ."</string>
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефон бір баған."</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 987b9c3..faa15c2 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -113,7 +113,7 @@
<item msgid="8887519571067543785">"96,0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="2284090879080331090">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="2284090879080331090">"Folosește selectarea sistemului (prestabilit)"</item>
<item msgid="1872276250541651186">"44,1 kHz"</item>
<item msgid="8736780630001704004">"48,0 kHz"</item>
<item msgid="7698585706868856888">"88,2 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 2ce7ccf..b3c7018 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -143,8 +143,8 @@
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"Folosește pentru profilul pentru conținut media audio"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Folosește pentru componenta audio a telefonului"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Folosește pentru transferul de fișiere"</string>
- <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizați pentru introducere date"</string>
- <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosiți pentru aparatele auditive"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Folosește pentru introducere date"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosește pentru aparatele auditive"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Folosește pentru LE_AUDIO"</string>
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Asociază"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"CONECTEAZĂ"</string>
@@ -206,7 +206,7 @@
<string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> nu este acceptată"</string>
<string name="tts_status_checking" msgid="8026559918948285013">"Se verifică…"</string>
<string name="tts_engine_settings_title" msgid="7849477533103566291">"Setări pentru <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
- <string name="tts_engine_settings_button" msgid="477155276199968948">"Lansați setările motorului"</string>
+ <string name="tts_engine_settings_button" msgid="477155276199968948">"Lansează setările motorului"</string>
<string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Motor preferat"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"Preferințe generale"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Resetează tonalitatea vorbirii"</string>
@@ -289,11 +289,11 @@
<string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string>
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectează versiunea AVRCP pentru Bluetooth"</string>
<string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string>
- <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectați versiunea MAP pentru Bluetooth"</string>
+ <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectează versiunea MAP pentru Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Declanșează codecul audio Bluetooth\nSelecție"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Rată de eșantionare audio Bluetooth"</string>
- <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Declanșați codecul audio Bluetooth\nSelecție: rată de eșantionare"</string>
+ <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Declanșează codecul audio Bluetooth\nSelecție: rată de eșantionare"</string>
<string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"O opțiune inactivă înseamnă incompatibilitate cu telefonul sau setul căști-microfon"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Biți audio Bluetooth per eșantion"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Declanșează codecul audio Bluetooth\nSelecție: biți per eșantion"</string>
@@ -309,7 +309,7 @@
<string name="private_dns_mode_provider" msgid="3619040641762557028">"Nume de gazdă al furnizorului de DNS privat"</string>
<string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Introdu numele de gazdă al furnizorului de DNS"</string>
<string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Nu s-a putut conecta"</string>
- <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
+ <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișează opțiunile pentru certificarea Ecran wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Mărește nivelul de înregistrare prin Wi‑Fi, afișează după SSID RSSI în Selectorul Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce descărcarea bateriei și îmbunătățește performanța rețelei"</string>
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Când acest mod este activat, adresa MAC a dispozitivului se poate schimba de fiecare dată când se conectează la o rețea care are activată randomizarea MAC."</string>
@@ -326,16 +326,16 @@
<string name="allow_mock_location" msgid="2102650981552527884">"Permite locațiile fictive"</string>
<string name="allow_mock_location_summary" msgid="179780881081354579">"Permite locațiile fictive"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"Activează inspectarea atributelor de vizualizare"</string>
- <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrați întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string>
+ <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrează întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Folosește accelerarea hardware pentru tethering, dacă este disponibilă"</string>
- <string name="adb_warning_title" msgid="7708653449506485728">"Permiteți remedierea erorilor prin USB?"</string>
- <string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Utilizați-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
+ <string name="adb_warning_title" msgid="7708653449506485728">"Permiți remedierea erorilor prin USB?"</string>
+ <string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Permiți remedierea erorilor wireless?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Remedierea erorilor wireless are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"Revoci accesul la remedierea erorilor prin USB de pe toate computerele pe care le-ai autorizat anterior?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Permiți setările pentru dezvoltare?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Aceste setări sunt destinate exclusiv utilizării pentru dezvoltare. Din cauza lor, este posibil ca dispozitivul tău și aplicațiile de pe acesta să nu mai funcționeze sau să funcționeze necorespunzător."</string>
- <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verificați aplicațiile prin USB"</string>
+ <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verifică aplicațiile prin USB"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verifică aplicațiile instalate utilizând ADB/ADT, pentru a detecta un comportament dăunător."</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Vor fi afișate dispozitivele Bluetooth fără nume (numai adresele MAC)"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Dezactivează funcția Bluetooth de volum absolut în cazul problemelor de volum apărute la dispozitivele la distanță, cum ar fi volumul mult prea ridicat sau lipsa de control asupra acestuia."</string>
@@ -393,7 +393,7 @@
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scară animație fereastră"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scară tranziție animații"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Scară durată Animator"</string>
- <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulați afișaje secundare"</string>
+ <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulează afișaje secundare"</string>
<string name="debug_applications_category" msgid="5394089406638954196">"Aplicații"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"Nu păstra activitățile"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Elimină activitățile imediat ce utilizatorul le închide"</string>
@@ -404,10 +404,10 @@
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Afișează avertisment pe ecran când o aplicație postează o notificare fără canal valid"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"Forțează accesul aplicațiilor la stocarea externă"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite scrierea oricărei aplicații eligibile în stocarea externă, indiferent de valorile manifestului"</string>
- <string name="force_resizable_activities" msgid="7143612144399959606">"Forțați redimensionarea activităților"</string>
- <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permiteți redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
+ <string name="force_resizable_activities" msgid="7143612144399959606">"Forțează redimensionarea activităților"</string>
+ <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
<string name="enable_freeform_support" msgid="7599125687603914253">"Activează ferestrele cu formă liberă"</string>
- <string name="enable_freeform_support_summary" msgid="1822862728719276331">"Activați compatibilitatea pentru ferestrele experimentale cu formă liberă."</string>
+ <string name="enable_freeform_support_summary" msgid="1822862728719276331">"Activează compatibilitatea pentru ferestrele experimentale cu formă liberă."</string>
<string name="desktop_mode" msgid="2389067840550544462">"Modul desktop"</string>
<string name="local_backup_password_title" msgid="4631017948933578709">"Parolă backup computer"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"În prezent, backupurile complete pe computer nu sunt protejate"</string>
@@ -449,7 +449,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (roșu-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (albastru-galben)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corecția culorii"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă doriți:<br/> <ol> <li>&nbsp;să vedeți mai precis culorile;</li> <li>&nbsp;să eliminați culorile pentru a vă concentra mai bine.</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă vrei:<br/> <ol> <li>&nbsp;să vezi mai precis culorile;</li> <li>&nbsp;să elimini culorile pentru a te concentra mai bine.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -573,10 +573,10 @@
<string name="user_add_user_title" msgid="5457079143694924885">"Adaugi un utilizator nou?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"Poți să permiți accesul la acest dispozitiv altor persoane creând utilizatori suplimentari. Fiecare utilizator are propriul spațiu, pe care îl poate personaliza cu aplicații, imagini de fundal etc. De asemenea, utilizatorii pot ajusta setările dispozitivului, cum ar fi setările pentru Wi-Fi, care îi afectează pe toți ceilalți utilizatori.\n\nDupă ce adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOricare dintre utilizatori poate actualiza aplicațiile pentru toți ceilalți utilizatori. Este posibil ca setările de accesibilitate și serviciile să nu se transfere la noul utilizator."</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"Când adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
- <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurați utilizatorul acum?"</string>
+ <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurezi utilizatorul acum?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Asigură-te că utilizatorul are posibilitatea de a prelua dispozitivul și de a-și configura spațiul"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Configurezi profilul acum?"</string>
- <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurați acum"</string>
+ <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurează acum"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"Nu acum"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"Adaugă"</string>
<string name="user_new_user_name" msgid="60979820612818840">"Utilizator nou"</string>
@@ -593,7 +593,7 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="user_add_user" msgid="7876449291500212468">"Adaugă un utilizator"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Șterge invitatul"</string>
<string name="guest_reset_guest" msgid="6110013010356013758">"Resetezi sesiunea pentru invitați"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Resetezi invitatul?"</string>
<string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Excludeți invitatul?"</string>
@@ -604,7 +604,7 @@
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string>
<string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieși din modul pentru invitați?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string>
- <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieși"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvezi activitatea invitatului?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvează activitatea din sesiunea actuală sau șterge aplicațiile și datele"</string>
<string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Șterge"</string>
@@ -613,11 +613,11 @@
<string name="guest_reset_button" msgid="2515069346223503479">"Resetează sesiunea pentru invitați"</string>
<string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieși din modul pentru invitați"</string>
<string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string>
- <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Poți să salvezi sau să ștergi activitatea la ieșire"</string>
<string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetează pentru a șterge acum activitatea din sesiune sau salvează ori șterge activitatea la ieșire"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fă o fotografie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Alege o imagine"</string>
- <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Selectează fotografia"</string>
<string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Prea multe încercări incorecte. Datele de pe acest dispozitiv vor fi șterse."</string>
<string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Prea multe încercări incorecte. Acest utilizator va fi șters."</string>
<string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Prea multe încercări incorecte. Acest profil de serviciu și datele sale vor fi șterse."</string>
@@ -662,7 +662,7 @@
<string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Alege aspectul tastaturii"</string>
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Prestabilit"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Activează ecranul"</string>
- <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permiteți activarea ecranului"</string>
+ <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permite activarea ecranului"</string>
<string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permite unei aplicații să activeze ecranul. Dacă acorzi permisiunea, aplicația poate să activeze oricând ecranul, fără intenția ta explicită."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Oprești difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Dacă difuzezi <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbi rezultatul, difuzarea actuală se va opri"</string>
diff --git a/packages/SimAppDialog/res/values-ro/strings.xml b/packages/SimAppDialog/res/values-ro/strings.xml
index 21663d1..0c733d7 100644
--- a/packages/SimAppDialog/res/values-ro/strings.xml
+++ b/packages/SimAppDialog/res/values-ro/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="8898068901680117589">"Sim App Dialog"</string>
- <string name="install_carrier_app_title" msgid="334729104862562585">"Activați serviciul mobil"</string>
+ <string name="install_carrier_app_title" msgid="334729104862562585">"Activează serviciul mobil"</string>
<string name="install_carrier_app_description" msgid="4014303558674923797">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalați aplicația <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="install_carrier_app_description_default" msgid="7356830245205847840">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalați aplicația operatorului"</string>
<string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Nu acum"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index ae8680a0..cad7159 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -37,7 +37,7 @@
<string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"Introdu un card SIM."</string>
<string name="keyguard_missing_sim_instructions_long" msgid="2712623293749378570">"Cardul SIM lipsește sau nu poate fi citit. Introdu un card SIM."</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="5842745213110966962">"Card SIM inutilizabil."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"Cardul dvs. SIM este dezactivat definitiv.\n Contactați furnizorul de servicii wireless pentru a obține un alt card SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"Cardul SIM e dezactivat definitiv.\n Contactează furnizorul de servicii wireless pentru a obține un alt card SIM."</string>
<string name="keyguard_sim_locked_message" msgid="4343544458476911044">"Cardul SIM este blocat."</string>
<string name="keyguard_sim_puk_locked_message" msgid="6253830777745450550">"Cardul SIM este blocat cu codul PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="2394023844117630429">"Se deblochează cardul SIM…"</string>
@@ -46,7 +46,7 @@
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Zona codului PIN pentru cardul SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"Zona codului PUK pentru cardul SIM"</string>
<string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Șterge"</string>
- <string name="disable_carrier_button_text" msgid="7153361131709275746">"Dezactivați cardul eSIM"</string>
+ <string name="disable_carrier_button_text" msgid="7153361131709275746">"Dezactivează cardul eSIM"</string>
<string name="error_disable_esim_title" msgid="3802652622784813119">"Nu se poate dezactiva cardul eSIM"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"Cardul eSIM nu poate fi dezactivat din cauza unei erori."</string>
<string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Introdu"</string>
@@ -56,24 +56,24 @@
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Reîncearcă peste o secundă.}few{Reîncearcă peste # secunde.}other{Reîncearcă peste # de secunde.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Introdu codul PIN al cardului SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Introdu codul PIN al cardului SIM pentru „<xliff:g id="CARRIER">%1$s</xliff:g>”."</string>
- <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivați cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string>
- <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Cardul SIM este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Cardul SIM „<xliff:g id="CARRIER">%1$s</xliff:g>\" este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivează cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Cardul SIM e acum dezactivat. Pentru a continua, introdu codul PUK. Pentru detalii, contactează operatorul."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Cardul SIM „<xliff:g id="CARRIER">%1$s</xliff:g>\" e acum dezactivat. Pentru a continua, introdu codul PUK. Pentru detalii, contactează operatorul."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Introdu codul PIN dorit"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirmați codul PIN dorit"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirmă codul PIN dorit"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="4251352015304070326">"Se deblochează cardul SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Introdu un cod PIN alcătuit din 4 până la 8 cifre."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Codul PUK trebuie să aibă minimum 8 cifre."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactați operatorul pentru a vă debloca dispozitivul."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Ai introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ai introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactează operatorul pentru a debloca dispozitivul."</string>
<string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codul PIN pentru cardul SIM este incorect. V-a mai rămas # încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.}few{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # încercări. }other{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # de încercări. }}"</string>
- <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactați operatorul."</string>
+ <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactează operatorul."</string>
<string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codul PUK pentru cardul SIM este incorect. V-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv.}few{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv.}other{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Deblocarea cu ajutorul codului PIN pentru cardul SIM nu a reușit!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Deblocarea cu ajutorul codului PUK pentru cardul SIM nu a reușit!"</string>
- <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Comutați metoda de introducere"</string>
+ <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Schimbă metoda de introducere"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mod Avion"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Modelul este necesar după repornirea dispozitivului"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Codul PIN este necesar după repornirea dispozitivului"</string>
diff --git a/packages/SystemUI/res-product/values-ro/strings.xml b/packages/SystemUI/res-product/values-ro/strings.xml
index b260f28..b4984aa 100644
--- a/packages/SystemUI/res-product/values-ro/strings.xml
+++ b/packages/SystemUI/res-product/values-ro/strings.xml
@@ -40,9 +40,9 @@
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
- <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Deblocați telefonul pentru mai multe opțiuni"</string>
- <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Deblocați tableta pentru mai multe opțiuni"</string>
- <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Deblocați dispozitivul pentru mai multe opțiuni"</string>
+ <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Deblochează telefonul pentru mai multe opțiuni"</string>
+ <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Deblochează tableta pentru mai multe opțiuni"</string>
+ <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Deblochează dispozitivul pentru mai multe opțiuni"</string>
<string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Se redă pe acest telefon"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Se redă pe această tabletă"</string>
</resources>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index acb47f7..2d67d95 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -14,8 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.dreams.complication.DoubleShadowTextClock
+<com.android.systemui.shared.shadow.DoubleShadowTextClock
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/time_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -25,4 +26,13 @@
android:format24Hour="@string/dream_time_complication_24_hr_time_format"
android:fontFeatureSettings="pnum, lnum"
android:letterSpacing="0.02"
- android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/>
+ android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
+ app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
+ app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
+ app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
+ app:keyShadowAlpha="0.3"
+ app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
+ app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
+ app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
+ app:ambientShadowAlpha="0.3"
+/>
diff --git a/packages/SystemUI/res/values-ro-ldrtl/strings.xml b/packages/SystemUI/res/values-ro-ldrtl/strings.xml
index e167b41..a7cd33c 100644
--- a/packages/SystemUI/res/values-ro-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ro-ldrtl/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trageți spre stânga pentru a comuta rapid între aplicații"</string>
+ <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trage spre stânga pentru a comuta rapid între aplicații"</string>
</resources>
diff --git a/packages/SystemUI/shared/res/values/attrs.xml b/packages/SystemUI/shared/res/values/attrs.xml
index f9d66ee..96a5840 100644
--- a/packages/SystemUI/shared/res/values/attrs.xml
+++ b/packages/SystemUI/shared/res/values/attrs.xml
@@ -25,4 +25,39 @@
<attr name="lockScreenWeight" format="integer" />
<attr name="chargeAnimationDelay" format="integer" />
</declare-styleable>
+
+ <declare-styleable name="DoubleShadowAttrDeclare">
+ <attr name="keyShadowBlur" format="dimension" />
+ <attr name="keyShadowOffsetX" format="dimension" />
+ <attr name="keyShadowOffsetY" format="dimension" />
+ <attr name="keyShadowAlpha" format="float" />
+ <attr name="ambientShadowBlur" format="dimension" />
+ <attr name="ambientShadowOffsetX" format="dimension" />
+ <attr name="ambientShadowOffsetY" format="dimension" />
+ <attr name="ambientShadowAlpha" format="float" />
+ </declare-styleable>
+
+ <declare-styleable name="DoubleShadowTextClock">
+ <attr name="keyShadowBlur" />
+ <attr name="keyShadowOffsetX" />
+ <attr name="keyShadowOffsetY" />
+ <attr name="keyShadowAlpha" />
+ <attr name="ambientShadowBlur" />
+ <attr name="ambientShadowOffsetX" />
+ <attr name="ambientShadowOffsetY" />
+ <attr name="ambientShadowAlpha" />
+ </declare-styleable>
+
+ <declare-styleable name="DoubleShadowTextView">
+ <attr name="keyShadowBlur" />
+ <attr name="keyShadowOffsetX" />
+ <attr name="keyShadowOffsetY" />
+ <attr name="keyShadowAlpha" />
+ <attr name="ambientShadowBlur" />
+ <attr name="ambientShadowOffsetX" />
+ <attr name="ambientShadowOffsetY" />
+ <attr name="ambientShadowAlpha" />
+ <attr name="drawableIconSize" format="dimension" />
+ <attr name="drawableIconInsetSize" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
new file mode 100644
index 0000000..3748eba
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.shadow
+
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.RenderEffect
+import android.graphics.RenderNode
+import android.graphics.Shader
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.InsetDrawable
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
+
+/** A component to draw an icon with two layers of shadows. */
+class DoubleShadowIconDrawable(
+ keyShadowInfo: ShadowInfo,
+ ambientShadowInfo: ShadowInfo,
+ iconDrawable: Drawable,
+ iconSize: Int,
+ val iconInsetSize: Int
+) : Drawable() {
+ private val mAmbientShadowInfo: ShadowInfo
+ private val mCanvasSize: Int
+ private val mKeyShadowInfo: ShadowInfo
+ private val mIconDrawable: InsetDrawable
+ private val mDoubleShadowNode: RenderNode
+
+ init {
+ mCanvasSize = iconSize + iconInsetSize * 2
+ mKeyShadowInfo = keyShadowInfo
+ mAmbientShadowInfo = ambientShadowInfo
+ setBounds(0, 0, mCanvasSize, mCanvasSize)
+ mIconDrawable = InsetDrawable(iconDrawable, iconInsetSize)
+ mIconDrawable.setBounds(0, 0, mCanvasSize, mCanvasSize)
+ mDoubleShadowNode = createShadowRenderNode()
+ }
+
+ private fun createShadowRenderNode(): RenderNode {
+ val renderNode = RenderNode("DoubleShadowNode")
+ renderNode.setPosition(0, 0, mCanvasSize, mCanvasSize)
+ // Create render effects
+ val ambientShadow =
+ createShadowRenderEffect(
+ mAmbientShadowInfo.blur,
+ mAmbientShadowInfo.offsetX,
+ mAmbientShadowInfo.offsetY,
+ mAmbientShadowInfo.alpha
+ )
+ val keyShadow =
+ createShadowRenderEffect(
+ mKeyShadowInfo.blur,
+ mKeyShadowInfo.offsetX,
+ mKeyShadowInfo.offsetY,
+ mKeyShadowInfo.alpha
+ )
+ val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN)
+ renderNode.setRenderEffect(blend)
+ return renderNode
+ }
+
+ private fun createShadowRenderEffect(
+ radius: Float,
+ offsetX: Float,
+ offsetY: Float,
+ alpha: Float
+ ): RenderEffect {
+ return RenderEffect.createColorFilterEffect(
+ PorterDuffColorFilter(Color.argb(alpha, 0f, 0f, 0f), PorterDuff.Mode.MULTIPLY),
+ RenderEffect.createOffsetEffect(
+ offsetX,
+ offsetY,
+ RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP)
+ )
+ )
+ }
+
+ override fun draw(canvas: Canvas) {
+ if (canvas.isHardwareAccelerated) {
+ if (!mDoubleShadowNode.hasDisplayList()) {
+ // Record render node if its display list is not recorded or discarded
+ // (which happens when it's no longer drawn by anything).
+ val recordingCanvas = mDoubleShadowNode.beginRecording()
+ mIconDrawable.draw(recordingCanvas)
+ mDoubleShadowNode.endRecording()
+ }
+ canvas.drawRenderNode(mDoubleShadowNode)
+ }
+ mIconDrawable.draw(canvas)
+ }
+
+ override fun getOpacity(): Int {
+ return PixelFormat.TRANSPARENT
+ }
+
+ override fun setAlpha(alpha: Int) {
+ mIconDrawable.alpha = alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ mIconDrawable.colorFilter = colorFilter
+ }
+
+ override fun setTint(color: Int) {
+ mIconDrawable.setTint(color)
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt
new file mode 100644
index 0000000..f2db129
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.shadow
+
+import android.content.Context
+import android.graphics.Canvas
+import android.util.AttributeSet
+import android.widget.TextClock
+import com.android.systemui.shared.R
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
+
+/** Extension of [TextClock] which draws two shadows on the text (ambient and key shadows) */
+class DoubleShadowTextClock
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextClock(context, attrs, defStyleAttr, defStyleRes) {
+ private val mAmbientShadowInfo: ShadowInfo
+ private val mKeyShadowInfo: ShadowInfo
+
+ init {
+ val attributes =
+ context.obtainStyledAttributes(
+ attrs,
+ R.styleable.DoubleShadowTextClock,
+ defStyleAttr,
+ defStyleRes
+ )
+ try {
+ val keyShadowBlur =
+ attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextClock_keyShadowBlur, 0)
+ val keyShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_keyShadowOffsetX,
+ 0
+ )
+ val keyShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_keyShadowOffsetY,
+ 0
+ )
+ val keyShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextClock_keyShadowAlpha, 0f)
+ mKeyShadowInfo =
+ ShadowInfo(
+ keyShadowBlur.toFloat(),
+ keyShadowOffsetX.toFloat(),
+ keyShadowOffsetY.toFloat(),
+ keyShadowAlpha
+ )
+ val ambientShadowBlur =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_ambientShadowBlur,
+ 0
+ )
+ val ambientShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_ambientShadowOffsetX,
+ 0
+ )
+ val ambientShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_ambientShadowOffsetY,
+ 0
+ )
+ val ambientShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextClock_ambientShadowAlpha, 0f)
+ mAmbientShadowInfo =
+ ShadowInfo(
+ ambientShadowBlur.toFloat(),
+ ambientShadowOffsetX.toFloat(),
+ ambientShadowOffsetY.toFloat(),
+ ambientShadowAlpha
+ )
+ } finally {
+ attributes.recycle()
+ }
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt
rename to packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt
index b1dc5a2..eaac93d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt
@@ -14,31 +14,33 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.complication
+package com.android.systemui.shared.shadow
import android.graphics.Canvas
+import android.graphics.Color
import android.widget.TextView
-import androidx.annotation.ColorInt
-class DoubleShadowTextHelper
-constructor(
- private val keyShadowInfo: ShadowInfo,
- private val ambientShadowInfo: ShadowInfo,
-) {
+object DoubleShadowTextHelper {
data class ShadowInfo(
val blur: Float,
val offsetX: Float = 0f,
val offsetY: Float = 0f,
- @ColorInt val color: Int
+ val alpha: Float
)
- fun applyShadows(view: TextView, canvas: Canvas, onDrawCallback: () -> Unit) {
+ fun applyShadows(
+ keyShadowInfo: ShadowInfo,
+ ambientShadowInfo: ShadowInfo,
+ view: TextView,
+ canvas: Canvas,
+ onDrawCallback: () -> Unit
+ ) {
// We enhance the shadow by drawing the shadow twice
view.paint.setShadowLayer(
ambientShadowInfo.blur,
ambientShadowInfo.offsetX,
ambientShadowInfo.offsetY,
- ambientShadowInfo.color
+ Color.argb(ambientShadowInfo.alpha, 0f, 0f, 0f)
)
onDrawCallback()
canvas.save()
@@ -53,7 +55,7 @@
keyShadowInfo.blur,
keyShadowInfo.offsetX,
keyShadowInfo.offsetY,
- keyShadowInfo.color
+ Color.argb(keyShadowInfo.alpha, 0f, 0f, 0f)
)
onDrawCallback()
canvas.restore()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
new file mode 100644
index 0000000..25d2721
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.shadow
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.shared.R
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
+
+/** Extension of [TextView] which draws two shadows on the text (ambient and key shadows} */
+class DoubleShadowTextView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+ private val mKeyShadowInfo: ShadowInfo
+ private val mAmbientShadowInfo: ShadowInfo
+
+ init {
+ val attributes =
+ context.obtainStyledAttributes(
+ attrs,
+ R.styleable.DoubleShadowTextView,
+ defStyleAttr,
+ defStyleRes
+ )
+ val drawableSize: Int
+ val drawableInsetSize: Int
+ try {
+ val keyShadowBlur =
+ attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextView_keyShadowBlur, 0)
+ val keyShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_keyShadowOffsetX,
+ 0
+ )
+ val keyShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_keyShadowOffsetY,
+ 0
+ )
+ val keyShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextView_keyShadowAlpha, 0f)
+ mKeyShadowInfo =
+ ShadowInfo(
+ keyShadowBlur.toFloat(),
+ keyShadowOffsetX.toFloat(),
+ keyShadowOffsetY.toFloat(),
+ keyShadowAlpha
+ )
+ val ambientShadowBlur =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_ambientShadowBlur,
+ 0
+ )
+ val ambientShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_ambientShadowOffsetX,
+ 0
+ )
+ val ambientShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_ambientShadowOffsetY,
+ 0
+ )
+ val ambientShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextView_ambientShadowAlpha, 0f)
+ mAmbientShadowInfo =
+ ShadowInfo(
+ ambientShadowBlur.toFloat(),
+ ambientShadowOffsetX.toFloat(),
+ ambientShadowOffsetY.toFloat(),
+ ambientShadowAlpha
+ )
+ drawableSize =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_drawableIconSize,
+ 0
+ )
+ drawableInsetSize =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_drawableIconInsetSize,
+ 0
+ )
+ } finally {
+ attributes.recycle()
+ }
+
+ val drawables = arrayOf<Drawable?>(null, null, null, null)
+ for ((index, drawable) in compoundDrawablesRelative.withIndex()) {
+ if (drawable == null) continue
+ drawables[index] =
+ DoubleShadowIconDrawable(
+ mKeyShadowInfo,
+ mAmbientShadowInfo,
+ drawable,
+ drawableSize,
+ drawableInsetSize
+ )
+ }
+ setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3])
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 2cc5ccdc..1e5c53d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -24,6 +24,7 @@
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.animation.Animator;
@@ -106,6 +107,8 @@
return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_password;
+ case PROMPT_REASON_TRUSTAGENT_EXPIRED:
+ return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 9871645..5b22324 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -330,6 +330,9 @@
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
break;
+ case PROMPT_REASON_TRUSTAGENT_EXPIRED:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
case PROMPT_REASON_NONE:
break;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c46e33d..0a91150 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -22,6 +22,7 @@
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.animation.Animator;
@@ -123,6 +124,8 @@
return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_pin;
+ case PROMPT_REASON_TRUSTAGENT_EXPIRED:
+ return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index ac00e94..9d0a8ac 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -61,6 +61,12 @@
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
+ * Some auth is required because the trustagent expired either from timeout or manually by
+ * the user
+ */
+ int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8;
+
+ /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 8e4ba5f..aae92ad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -350,18 +350,14 @@
@Override
public void onTryAgainPressed(long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onTryAgainPressed: Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onTryAgainPressed");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onTryAgainPressed");
return;
}
try {
- mReceiver.onTryAgainPressed();
+ receiver.onTryAgainPressed();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when handling try again", e);
}
@@ -369,18 +365,14 @@
@Override
public void onDeviceCredentialPressed(long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onDeviceCredentialPressed");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onDeviceCredentialPressed");
return;
}
try {
- mReceiver.onDeviceCredentialPressed();
+ receiver.onDeviceCredentialPressed();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when handling credential button", e);
}
@@ -388,18 +380,14 @@
@Override
public void onSystemEvent(int event, long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onSystemEvent");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onSystemEvent");
return;
}
try {
- mReceiver.onSystemEvent(event);
+ receiver.onSystemEvent(event);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when sending system event", e);
}
@@ -407,23 +395,46 @@
@Override
public void onDialogAnimatedIn(long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onDialogAnimatedIn: Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onDialogAnimatedIn");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onDialogAnimatedIn");
return;
}
try {
- mReceiver.onDialogAnimatedIn();
+ receiver.onDialogAnimatedIn();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e);
}
}
+ @Nullable
+ private IBiometricSysuiReceiver getCurrentReceiver(long requestId) {
+ if (!isRequestIdValid(requestId)) {
+ return null;
+ }
+
+ if (mReceiver == null) {
+ Log.w(TAG, "getCurrentReceiver: Receiver is null");
+ }
+
+ return mReceiver;
+ }
+
+ private boolean isRequestIdValid(long requestId) {
+ if (mCurrentDialog == null) {
+ Log.w(TAG, "shouldNotifyReceiver: dialog already gone");
+ return false;
+ }
+
+ if (requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "shouldNotifyReceiver: requestId doesn't match");
+ return false;
+ }
+
+ return true;
+ }
+
@Override
public void onDismissed(@DismissedReason int reason,
@Nullable byte[] credentialAttestation, long requestId) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
deleted file mode 100644
index 789ebc5..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.complication;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.widget.TextClock;
-
-import com.android.systemui.R;
-import com.android.systemui.dreams.complication.DoubleShadowTextHelper.ShadowInfo;
-
-import kotlin.Unit;
-
-/**
- * Extension of {@link TextClock} which draws two shadows on the text (ambient and key shadows)
- */
-public class DoubleShadowTextClock extends TextClock {
- private final DoubleShadowTextHelper mShadowHelper;
-
- public DoubleShadowTextClock(Context context) {
- this(context, null);
- }
-
- public DoubleShadowTextClock(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DoubleShadowTextClock(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- final Resources resources = context.getResources();
- final ShadowInfo keyShadowInfo = new ShadowInfo(
- resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_radius),
- resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dx),
- resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dy),
- resources.getColor(R.color.dream_overlay_clock_key_text_shadow_color));
-
- final ShadowInfo ambientShadowInfo = new ShadowInfo(
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_clock_ambient_text_shadow_radius),
- resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dx),
- resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dy),
- resources.getColor(R.color.dream_overlay_clock_ambient_text_shadow_color));
- mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- mShadowHelper.applyShadows(this, canvas, () -> {
- super.onDraw(canvas);
- return Unit.INSTANCE;
- });
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java
deleted file mode 100644
index cf7e312..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.complication;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-import kotlin.Unit;
-
-/**
- * Extension of {@link TextView} which draws two shadows on the text (ambient and key shadows}
- */
-public class DoubleShadowTextView extends TextView {
- private final DoubleShadowTextHelper mShadowHelper;
-
- public DoubleShadowTextView(Context context) {
- this(context, null);
- }
-
- public DoubleShadowTextView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DoubleShadowTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- final Resources resources = context.getResources();
- final DoubleShadowTextHelper.ShadowInfo
- keyShadowInfo = new DoubleShadowTextHelper.ShadowInfo(
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_status_bar_key_text_shadow_radius),
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_status_bar_key_text_shadow_dx),
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_status_bar_key_text_shadow_dy),
- resources.getColor(R.color.dream_overlay_status_bar_key_text_shadow_color));
-
- final DoubleShadowTextHelper.ShadowInfo
- ambientShadowInfo = new DoubleShadowTextHelper.ShadowInfo(
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius),
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx),
- resources.getDimensionPixelSize(
- R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy),
- resources.getColor(R.color.dream_overlay_status_bar_ambient_text_shadow_color));
- mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- mShadowHelper.applyShadows(this, canvas, () -> {
- super.onDraw(canvas);
- return Unit.INSTANCE;
- });
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index be1d162..4214240 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -24,6 +24,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -802,6 +803,9 @@
} else if (trustAgentsEnabled
&& (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+ } else if (trustAgentsEnabled
+ && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
} else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
|| mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 1011a6d..d7e86b6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2230,7 +2230,8 @@
if (cancel) {
collapse(false /* delayed */, 1.0f /* speedUpFactor */);
} else {
- maybeVibrateOnOpening();
+ // Window never will receive touch events that typically trigger haptic on open.
+ maybeVibrateOnOpening(false /* openingWithTouch */);
fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
}
onTrackingStopped(false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index c3f1e57..b4ce95c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -96,6 +96,7 @@
private float mMinExpandHeight;
private boolean mPanelUpdateWhenAnimatorEnds;
private final boolean mVibrateOnOpening;
+ private boolean mHasVibratedOnOpen = false;
protected boolean mIsLaunchAnimationRunning;
private int mFixedDuration = NO_FIXED_DURATION;
protected float mOverExpansion;
@@ -353,8 +354,8 @@
private void startOpening(MotionEvent event) {
updatePanelExpansionAndVisibility();
- maybeVibrateOnOpening();
-
+ // Reset at start so haptic can be triggered as soon as panel starts to open.
+ mHasVibratedOnOpen = false;
//TODO: keyguard opens QS a different way; log that too?
// Log the position of the swipe that opened the panel
@@ -368,9 +369,18 @@
.log(LockscreenUiEvent.LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND);
}
- protected void maybeVibrateOnOpening() {
+ /**
+ * Maybe vibrate as panel is opened.
+ *
+ * @param openingWithTouch Whether the panel is being opened with touch. If the panel is instead
+ * being opened programmatically (such as by the open panel gesture), we always play haptic.
+ */
+ protected void maybeVibrateOnOpening(boolean openingWithTouch) {
if (mVibrateOnOpening) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ if (!openingWithTouch || !mHasVibratedOnOpen) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ mHasVibratedOnOpen = true;
+ }
}
}
@@ -1371,6 +1381,9 @@
break;
case MotionEvent.ACTION_MOVE:
addMovement(event);
+ if (!isFullyCollapsed()) {
+ maybeVibrateOnOpening(true /* openingWithTouch */);
+ }
float h = y - mInitialExpandY;
// If the panel was collapsed when touching, we only need to check for the
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 61d8568..e97654c 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -44,6 +44,7 @@
import static com.android.server.am.ActivityManagerService.getKsmInfo;
import static com.android.server.am.ActivityManagerService.stringifyKBSize;
import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
@@ -1047,17 +1048,7 @@
}
trimMemoryUiHiddenIfNecessaryLSP(app);
if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) {
- if (trimMemoryLevel < curLevel[0] && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ,
- "Trimming memory of " + app.processName
- + " to " + curLevel[0]);
- }
- thread.scheduleTrimMemory(curLevel[0]);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, curLevel[0], "Trimming memory of ");
profile.setTrimMemoryLevel(curLevel[0]);
step[0]++;
if (step[0] >= actualFactor) {
@@ -1073,31 +1064,11 @@
}
} else if (curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
&& !app.isKilledByAm()) {
- if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
- && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ,
- "Trimming memory of heavy-weight " + app.processName
- + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- }
- thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
+ "Trimming memory of heavy-weight ");
profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
} else {
- if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
- + " to " + fgTrimLevel);
- }
- thread.scheduleTrimMemory(fgTrimLevel);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, fgTrimLevel, "Trimming memory of fg ");
profile.setTrimMemoryLevel(fgTrimLevel);
}
});
@@ -1128,22 +1099,28 @@
// If this application is now in the background and it
// had done UI, then give it the special trim level to
// have it free UI resources.
- final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- IApplicationThread thread;
- if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
- + app.processName + " to " + level);
- }
- thread.scheduleTrimMemory(level);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN,
+ "Trimming memory of bg-ui ");
app.mProfile.setPendingUiClean(false);
}
}
+ @GuardedBy({"mService", "mProcLock"})
+ private void scheduleTrimMemoryLSP(ProcessRecord app, int level, String msg) {
+ IApplicationThread thread;
+ if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ, msg + app.processName + " to " + level);
+ }
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
+ OOM_ADJ_REASON_NONE);
+ thread.scheduleTrimMemory(level);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
@GuardedBy("mProcLock")
long getLowRamTimeSinceIdleLPr(long now) {
return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8e00ccf..44c8e18 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -21,8 +21,11 @@
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_UNKNOWN;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -106,6 +109,8 @@
private final BinderService mBinderService;
@NonNull
private final OverrideRequestController mOverrideRequestController;
+ @NonNull
+ private final DeviceStateProviderListener mDeviceStateProviderListener;
@VisibleForTesting
@NonNull
public ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -139,6 +144,12 @@
@NonNull
private Optional<OverrideRequest> mActiveOverride = Optional.empty();
+ // The current active base state override request. When set the device state specified here will
+ // replace the value in mBaseState.
+ @GuardedBy("mLock")
+ @NonNull
+ private Optional<OverrideRequest> mActiveBaseStateOverride = Optional.empty();
+
// List of processes registered to receive notifications about changes to device state and
// request status indexed by process id.
@GuardedBy("mLock")
@@ -177,7 +188,8 @@
mOverrideRequestController = new OverrideRequestController(
this::onOverrideRequestStatusChangedLocked);
mDeviceStatePolicy = policy;
- mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
+ mDeviceStateProviderListener = new DeviceStateProviderListener();
+ mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
mBinderService = new BinderService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
}
@@ -257,6 +269,21 @@
}
}
+ /**
+ * Returns the current override base state, or {@link Optional#empty()} if no override state is
+ * requested. If an override base state is present, the returned state will be the same as
+ * the base state returned from {@link #getBaseState()}.
+ */
+ @NonNull
+ Optional<DeviceState> getOverrideBaseState() {
+ synchronized (mLock) {
+ if (mActiveBaseStateOverride.isPresent()) {
+ return getStateLocked(mActiveBaseStateOverride.get().getRequestedState());
+ }
+ return Optional.empty();
+ }
+ }
+
/** Returns the list of currently supported device states. */
DeviceState[] getSupportedStates() {
synchronized (mLock) {
@@ -366,6 +393,7 @@
}
final DeviceState baseState = baseStateOptional.get();
+
if (mBaseState.isPresent() && mBaseState.get().equals(baseState)) {
// Base state hasn't changed. Nothing to do.
return;
@@ -375,7 +403,7 @@
if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
mOverrideRequestController.cancelOverrideRequest();
}
- mOverrideRequestController.handleBaseStateChanged();
+ mOverrideRequestController.handleBaseStateChanged(identifier);
updatePendingStateLocked();
if (!mPendingState.isPresent()) {
@@ -528,16 +556,41 @@
}
}
+ @GuardedBy("mLock")
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
@OverrideRequestController.RequestStatus int status) {
- if (status == STATUS_ACTIVE) {
- mActiveOverride = Optional.of(request);
- } else if (status == STATUS_CANCELED) {
- if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
- mActiveOverride = Optional.empty();
+ if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ enableBaseStateRequestLocked(request);
+ return;
+ case STATUS_CANCELED:
+ if (mActiveBaseStateOverride.isPresent()
+ && mActiveBaseStateOverride.get() == request) {
+ mActiveBaseStateOverride = Optional.empty();
+ }
+ break;
+ case STATUS_UNKNOWN: // same as default
+ default:
+ throw new IllegalArgumentException("Unknown request status: " + status);
+ }
+ } else if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_EMULATED_STATE) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ mActiveOverride = Optional.of(request);
+ break;
+ case STATUS_CANCELED:
+ if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
+ mActiveOverride = Optional.empty();
+ }
+ break;
+ case STATUS_UNKNOWN: // same as default
+ default:
+ throw new IllegalArgumentException("Unknown request status: " + status);
}
} else {
- throw new IllegalArgumentException("Unknown request status: " + status);
+ throw new IllegalArgumentException(
+ "Unknown OverrideRest type: " + request.getRequestType());
}
boolean updatedPendingState = updatePendingStateLocked();
@@ -564,6 +617,18 @@
mHandler.post(this::notifyPolicyIfNeeded);
}
+ /**
+ * Sets the new base state of the device and notifies the process that made the base state
+ * override request that the request is now active.
+ */
+ @GuardedBy("mLock")
+ private void enableBaseStateRequestLocked(OverrideRequest request) {
+ setBaseState(request.getRequestedState());
+ mActiveBaseStateOverride = Optional.of(request);
+ ProcessRecord processRecord = mProcessRecords.get(request.getPid());
+ processRecord.notifyRequestActiveAsync(request.getToken());
+ }
+
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
synchronized (mLock) {
if (mProcessRecords.contains(pid)) {
@@ -606,7 +671,8 @@
+ " has no registered callback.");
}
- if (mOverrideRequestController.hasRequest(token)) {
+ if (mOverrideRequestController.hasRequest(token,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE)) {
throw new IllegalStateException("Request has already been made for the supplied"
+ " token: " + token);
}
@@ -617,7 +683,8 @@
+ " is not supported.");
}
- OverrideRequest request = new OverrideRequest(token, callingPid, state, flags);
+ OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mOverrideRequestController.addRequest(request);
}
}
@@ -633,6 +700,44 @@
}
}
+ private void requestBaseStateOverrideInternal(int state, int flags, int callingPid,
+ @NonNull IBinder token) {
+ synchronized (mLock) {
+ final Optional<DeviceState> deviceState = getStateLocked(state);
+ if (!deviceState.isPresent()) {
+ throw new IllegalArgumentException("Requested state: " + state
+ + " is not supported.");
+ }
+
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ if (mOverrideRequestController.hasRequest(token,
+ OVERRIDE_REQUEST_TYPE_BASE_STATE)) {
+ throw new IllegalStateException("Request has already been made for the supplied"
+ + " token: " + token);
+ }
+
+ OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
+ OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ mOverrideRequestController.addBaseStateRequest(request);
+ }
+ }
+
+ private void cancelBaseStateOverrideInternal(int callingPid) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+ setBaseState(mDeviceStateProviderListener.mCurrentBaseState);
+ }
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
@@ -729,6 +834,8 @@
}
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
+
@Override
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
if (newDeviceStates.length == 0) {
@@ -743,7 +850,7 @@
if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
-
+ mCurrentBaseState = identifier;
setBaseState(identifier);
}
}
@@ -895,6 +1002,38 @@
}
@Override // Binder call
+ public void requestBaseStateOverride(IBinder token, int state, int flags) {
+ final int callingPid = Binder.getCallingPid();
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to control base state of device.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ requestBaseStateOverrideInternal(state, flags, callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelBaseStateOverride() {
+ final int callingPid = Binder.getCallingPid();
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to control base state of device.");
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ cancelBaseStateOverrideInternal(callingPid);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 659ee75..8c6068d 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,8 @@
package com.android.server.devicestate;
-import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.Binder;
@@ -39,6 +36,8 @@
public class DeviceStateManagerShellCommand extends ShellCommand {
@Nullable
private static DeviceStateRequest sLastRequest;
+ @Nullable
+ private static DeviceStateRequest sLastBaseStateRequest;
private final DeviceStateManagerService mService;
private final DeviceStateManager mClient;
@@ -58,6 +57,8 @@
switch (cmd) {
case "state":
return runState(pw);
+ case "base-state":
+ return runBaseState(pw);
case "print-state":
return runPrintState(pw);
case "print-states":
@@ -89,10 +90,6 @@
return 0;
}
- final Context context = mService.getContext();
- context.enforceCallingOrSelfPermission(
- CONTROL_DEVICE_STATE,
- "Permission required to request device state.");
final long callingIdentity = Binder.clearCallingIdentity();
try {
if ("reset".equals(nextArg)) {
@@ -127,6 +124,47 @@
return 0;
}
+ private int runBaseState(PrintWriter pw) {
+ final String nextArg = getNextArg();
+ if (nextArg == null) {
+ printAllStates(pw);
+ return 0;
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(nextArg)) {
+ if (sLastBaseStateRequest != null) {
+ mClient.cancelBaseStateOverride();
+ sLastBaseStateRequest = null;
+ }
+ } else {
+ int requestedState = Integer.parseInt(nextArg);
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+ mClient.requestBaseStateOverride(request, null, null);
+
+ sLastBaseStateRequest = request;
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ getErrPrintWriter().println("-------------------");
+ getErrPrintWriter().println("Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
+ return 0;
+ }
+
private int runPrintState(PrintWriter pw) {
Optional<DeviceState> deviceState = mService.getCommittedState();
if (deviceState.isPresent()) {
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java
index 35a4c84..325e384 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequest.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java
@@ -16,9 +16,13 @@
package com.android.server.devicestate;
+import android.annotation.IntDef;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.IBinder;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A request to override the state managed by {@link DeviceStateManagerService}.
*
@@ -30,13 +34,47 @@
private final int mRequestedState;
@DeviceStateRequest.RequestFlags
private final int mFlags;
+ @OverrideRequestType
+ private final int mRequestType;
+
+ /**
+ * Denotes that the request is meant to override the emulated state of the device. This will
+ * not change the base (physical) state of the device.
+ *
+ * This request type should be used if you are looking to emulate a device state for a feature
+ * but want the system to be aware of the physical state of the device.
+ */
+ public static final int OVERRIDE_REQUEST_TYPE_EMULATED_STATE = 0;
+
+ /**
+ * Denotes that the request is meant to override the base (physical) state of the device.
+ * Overriding the base state may not change the emulated state of the device if there is also an
+ * override request active for that property.
+ *
+ * This request type should only be used for testing, where you want to simulate the physical
+ * state of the device changing.
+ */
+ public static final int OVERRIDE_REQUEST_TYPE_BASE_STATE = 1;
+
+ /**
+ * Flags for signifying the type of {@link OverrideRequest}.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "REQUEST_TYPE_" }, value = {
+ OVERRIDE_REQUEST_TYPE_BASE_STATE,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OverrideRequestType {}
OverrideRequest(IBinder token, int pid, int requestedState,
- @DeviceStateRequest.RequestFlags int flags) {
+ @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) {
mToken = token;
mPid = pid;
mRequestedState = requestedState;
mFlags = flags;
+ mRequestType = requestType;
}
IBinder getToken() {
@@ -55,4 +93,9 @@
int getFlags() {
return mFlags;
}
+
+ @OverrideRequestType
+ int getRequestType() {
+ return mRequestType;
+ }
}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 01f5a09..e39c732 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -75,6 +75,8 @@
// Handle to the current override request, null if none.
private OverrideRequest mRequest;
+ // Handle to the current base state override request, null if none.
+ private OverrideRequest mBaseStateRequest;
private boolean mStickyRequestsAllowed;
// The current request has outlived their process.
@@ -111,13 +113,23 @@
}
}
+ void addBaseStateRequest(@NonNull OverrideRequest request) {
+ OverrideRequest previousRequest = mBaseStateRequest;
+ mBaseStateRequest = request;
+ mListener.onStatusChanged(request, STATUS_ACTIVE);
+
+ if (previousRequest != null) {
+ cancelRequestLocked(previousRequest);
+ }
+ }
+
/**
* Cancels the request with the specified {@code token} and notifies the listener of all changes
* to request status as a result of this operation.
*/
void cancelRequest(@NonNull OverrideRequest request) {
// Either don't have a current request or attempting to cancel an already cancelled request
- if (!hasRequest(request.getToken())) {
+ if (!hasRequest(request.getToken(), request.getRequestType())) {
return;
}
cancelCurrentRequestLocked();
@@ -144,11 +156,24 @@
}
/**
+ * Cancels the current base state override request, this could be due to the physical
+ * configuration of the device changing.
+ */
+ void cancelBaseStateOverrideRequest() {
+ cancelCurrentBaseStateRequestLocked();
+ }
+
+ /**
* Returns {@code true} if this controller is current managing a request with the specified
* {@code token}, {@code false} otherwise.
*/
- boolean hasRequest(@NonNull IBinder token) {
- return mRequest != null && token == mRequest.getToken();
+ boolean hasRequest(@NonNull IBinder token,
+ @OverrideRequest.OverrideRequestType int requestType) {
+ if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) {
+ return mBaseStateRequest != null && token == mBaseStateRequest.getToken();
+ } else {
+ return mRequest != null && token == mRequest.getToken();
+ }
}
/**
@@ -157,11 +182,11 @@
* operation.
*/
void handleProcessDied(int pid) {
- if (mRequest == null) {
- return;
+ if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) {
+ cancelCurrentBaseStateRequestLocked();
}
- if (mRequest.getPid() == pid) {
+ if (mRequest != null && mRequest.getPid() == pid) {
if (mStickyRequestsAllowed) {
// Do not cancel the requests now because sticky requests are allowed. These
// requests will be cancelled on a call to cancelStickyRequests().
@@ -176,7 +201,10 @@
* Notifies the controller that the base state has changed. The controller will notify the
* listener of all changes to request status as a result of this change.
*/
- void handleBaseStateChanged() {
+ void handleBaseStateChanged(int state) {
+ if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
+ cancelBaseStateOverrideRequest();
+ }
if (mRequest == null) {
return;
}
@@ -192,11 +220,12 @@
* notify the listener of all changes to request status as a result of this change.
*/
void handleNewSupportedStates(int[] newSupportedStates) {
- if (mRequest == null) {
- return;
+ if (mBaseStateRequest != null && !contains(newSupportedStates,
+ mBaseStateRequest.getRequestedState())) {
+ cancelCurrentBaseStateRequestLocked();
}
- if (!contains(newSupportedStates, mRequest.getRequestedState())) {
+ if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
cancelCurrentRequestLocked();
}
}
@@ -228,10 +257,23 @@
return;
}
mStickyRequest = false;
- mListener.onStatusChanged(mRequest, STATUS_CANCELED);
+ cancelRequestLocked(mRequest);
mRequest = null;
}
+ /**
+ * Handles cancelling {@code mBaseStateRequest}.
+ * Notifies the listener of the canceled status as well.
+ */
+ private void cancelCurrentBaseStateRequestLocked() {
+ if (mBaseStateRequest == null) {
+ Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
+ return;
+ }
+ cancelRequestLocked(mBaseStateRequest);
+ mBaseStateRequest = null;
+ }
+
private static boolean contains(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 7170773..f888ff6 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -687,7 +687,7 @@
*/
public void lockUser(int userId) {
mLockPatternUtils.requireStrongAuth(
- StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+ StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, userId);
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
} catch (RemoteException e) {
@@ -2084,7 +2084,7 @@
if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
mLockPatternUtils.requireStrongAuth(
- mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId);
+ mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId);
}
maybeLockScreen(mUserId);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 488fe67..147c9cb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -48,6 +48,7 @@
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
@@ -1894,14 +1895,12 @@
// Whether this is in a Task with embedded activity.
flags |= FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
}
- final Rect taskBounds = parentTask.getBounds();
- final Rect startBounds = mAbsoluteBounds;
- final Rect endBounds = wc.getBounds();
- if (taskBounds.width() == startBounds.width()
- && taskBounds.height() == startBounds.height()
- && taskBounds.width() == endBounds.width()
- && taskBounds.height() == endBounds.height()) {
- // Whether the container fills the Task bounds before and after the transition.
+ if (parentTask.forAllActivities(ActivityRecord::hasStartingWindow)) {
+ // The starting window should cover all windows inside the leaf Task.
+ flags |= FLAG_IS_BEHIND_STARTING_WINDOW;
+ }
+ if (isWindowFillingTask(wc, parentTask)) {
+ // Whether the container fills its parent Task bounds.
flags |= FLAG_FILLS_TASK;
}
}
@@ -1923,6 +1922,22 @@
}
return flags;
}
+
+ /** Whether the container fills its parent Task bounds before and after the transition. */
+ private boolean isWindowFillingTask(@NonNull WindowContainer wc, @NonNull Task parentTask) {
+ final Rect taskBounds = parentTask.getBounds();
+ final int taskWidth = taskBounds.width();
+ final int taskHeight = taskBounds.height();
+ final Rect startBounds = mAbsoluteBounds;
+ final Rect endBounds = wc.getBounds();
+ // Treat it as filling the task if it is not visible.
+ final boolean isInvisibleOrFillingTaskBeforeTransition = !mVisible
+ || (taskWidth == startBounds.width() && taskHeight == startBounds.height());
+ final boolean isInVisibleOrFillingTaskAfterTransition = !wc.isVisibleRequested()
+ || (taskWidth == endBounds.width() && taskHeight == endBounds.height());
+ return isInvisibleOrFillingTaskBeforeTransition
+ && isInVisibleOrFillingTaskAfterTransition;
+ }
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 038cbc0..94f88ab 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -587,6 +587,157 @@
});
}
+ @Test
+ public void requestBaseStateOverride() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ flushHandler();
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestBaseStateOverride(token,
+ OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ // Flush the handler twice. The first flush ensures the request is added and the policy is
+ // notified, while the second flush ensures the callback is notified once the change is
+ // committed.
+ flushHandler(2 /* count */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mService.getBinderService().cancelBaseStateOverride();
+ flushHandler();
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertFalse(mService.getOverrideBaseState().isPresent());
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestBaseStateOverride_cancelledByBaseStateUpdate() throws RemoteException {
+ final DeviceState testDeviceState = new DeviceState(2, "TEST", 0);
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ mProvider.notifySupportedDeviceStates(
+ new DeviceState[]{DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE, testDeviceState });
+ flushHandler();
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestBaseStateOverride(token,
+ OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ // Flush the handler twice. The first flush ensures the request is added and the policy is
+ // notified, while the second flush ensures the callback is notified once the change is
+ // committed.
+ flushHandler(2 /* count */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.setState(testDeviceState.getIdentifier());
+ flushHandler();
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set to the new base state once the override is cleared.
+ assertEquals(mService.getCommittedState(), Optional.of(testDeviceState));
+ assertEquals(mSysPropSetter.getValue(),
+ testDeviceState.getIdentifier() + ":" + testDeviceState.getName());
+ assertEquals(mService.getBaseState(), Optional.of(testDeviceState));
+ assertFalse(mService.getOverrideBaseState().isPresent());
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ testDeviceState.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ testDeviceState.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ testDeviceState.getIdentifier());
+ }
+
+ @Test
+ public void requestBaseState_unsupportedState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token,
+ UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestBaseState_invalidState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token, INVALID_DEVICE_STATE,
+ 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestBaseState_beforeRegisteringCallback() {
+ assertThrows(IllegalStateException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token,
+ DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ });
+ }
+
private static void assertArrayEquals(int[] expected, int[] actual) {
Assert.assertTrue(Arrays.equals(expected, actual));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index 2297c91..430504c 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.devicestate;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -57,7 +59,7 @@
@Test
public void addRequest() {
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(request));
mController.addRequest(request);
@@ -67,14 +69,14 @@
@Test
public void addRequest_cancelExistingRequestThroughNewRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(firstRequest));
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(secondRequest));
mController.addRequest(secondRequest);
@@ -85,7 +87,7 @@
@Test
public void addRequest_cancelActiveRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mController.addRequest(firstRequest);
@@ -97,30 +99,90 @@
}
@Test
- public void handleBaseStateChanged() {
- OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */,
- DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */);
+ public void addBaseStateRequest() {
+ OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(request));
- mController.addRequest(firstRequest);
+ mController.addBaseStateRequest(request);
+ assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE);
+ }
+
+ @Test
+ public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(firstRequest));
+
+ mController.addBaseStateRequest(firstRequest);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(secondRequest));
+
+ mController.addBaseStateRequest(secondRequest);
+ assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ }
+
+ @Test
+ public void addBaseStateRequest_cancelActiveBaseStateRequest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+
+ mController.addBaseStateRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- mController.handleBaseStateChanged();
+ mController.cancelBaseStateOverrideRequest();
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@Test
+ public void handleBaseStateChanged() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */,
+ DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+
+ mController.addRequest(firstRequest);
+
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ mController.addBaseStateRequest(baseStateRequest);
+
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
+ mController.handleBaseStateChanged(1);
+
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
+ }
+
+ @Test
public void handleProcessDied() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
@@ -128,13 +190,20 @@
mController.setStickyRequestsAllowed(true);
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
mController.cancelStickyRequest();
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
@@ -143,22 +212,31 @@
@Test
public void handleNewSupportedStates() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- mController.handleNewSupportedStates(new int[]{ 0, 1 });
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
- mController.handleNewSupportedStates(new int[]{ 0 });
+ mController.handleNewSupportedStates(new int[]{0, 1});
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
+ mController.handleNewSupportedStates(new int[]{0});
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
public void cancelOverrideRequestsTest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 8cf32ba..45d8e22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -34,6 +34,7 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -1074,6 +1075,39 @@
}
@Test
+ public void testIsBehindStartingWindowChange() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity0 = createActivityRecord(task);
+ final ActivityRecord activity1 = createActivityRecord(task);
+ doReturn(true).when(activity1).hasStartingWindow();
+
+ // Start states.
+ changes.put(activity0, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(activity1, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
+ // End states.
+ activity0.mVisibleRequested = false;
+ activity1.mVisibleRequested = true;
+
+ participants.add(activity0);
+ participants.add(activity1);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting
+ // window should cover the whole Task.
+ assertEquals(2, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
+ assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
+
+ }
+
+ @Test
public void testFlagInTaskWithEmbeddedActivity() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
@@ -1120,7 +1154,7 @@
}
@Test
- public void testFlagFillsTask() {
+ public void testFlagFillsTask_embeddingNotFillingTask() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
final ArraySet<WindowContainer> participants = transition.mParticipants;
@@ -1165,6 +1199,67 @@
}
@Test
+ public void testFlagFillsTask_openActivityFillingTask() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ // Set to multi-windowing mode in order to set bounds.
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect taskBounds = new Rect(0, 0, 500, 1000);
+ task.setBounds(taskBounds);
+ final ActivityRecord activity = createActivityRecord(task);
+ // Start states: set bounds to make sure the start bounds is ignored if it is not visible.
+ activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
+ activity.mVisibleRequested = false;
+ changes.put(activity, new Transition.ChangeInfo(activity));
+ // End states: reset bounds to fill Task.
+ activity.getConfiguration().windowConfiguration.setBounds(taskBounds);
+ activity.mVisibleRequested = true;
+
+ participants.add(activity);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // Opening activity that is filling Task after transition should have the flag.
+ assertEquals(1, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
+ }
+
+ @Test
+ public void testFlagFillsTask_closeActivityFillingTask() {
+ final Transition transition = createTestTransition(TRANSIT_CLOSE);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ // Set to multi-windowing mode in order to set bounds.
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect taskBounds = new Rect(0, 0, 500, 1000);
+ task.setBounds(taskBounds);
+ final ActivityRecord activity = createActivityRecord(task);
+ // Start states: fills Task without override.
+ activity.mVisibleRequested = true;
+ changes.put(activity, new Transition.ChangeInfo(activity));
+ // End states: set bounds to make sure the start bounds is ignored if it is not visible.
+ activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
+ activity.mVisibleRequested = false;
+
+ participants.add(activity);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // Closing activity that is filling Task before transition should have the flag.
+ assertEquals(1, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
+ }
+
+ @Test
public void testIncludeEmbeddedActivityReparent() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
final Task task = createTask(mDisplayContent);