Merge "Throw when setting RLP with privileged routers" into main
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3baa92c..cda9ae3 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -119,6 +119,7 @@
method @NonNull public android.os.IBinder getProcessToken();
method @NonNull public android.os.UserHandle getUser();
field public static final String PAC_PROXY_SERVICE = "pac_proxy";
+ field public static final String REMOTE_AUTH_SERVICE = "remote_auth";
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 58c2548..a09d7dc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -203,6 +203,7 @@
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
import android.window.SplashScreenView;
+import android.window.WindowContextInfo;
import android.window.WindowProviderService;
import android.window.WindowTokenClientController;
@@ -6248,10 +6249,9 @@
}
@Override
- public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
- @NonNull Configuration configuration, int displayId) {
- WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
- configuration, displayId);
+ public void handleWindowContextInfoChanged(@NonNull IBinder clientToken,
+ @NonNull WindowContextInfo info) {
+ WindowTokenClientController.getInstance().onWindowContextInfoChanged(clientToken, info);
}
@Override
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f7a43f4..6753cb8 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -28,6 +28,7 @@
import android.util.MergedConfiguration;
import android.view.SurfaceControl;
import android.window.SplashScreenView.SplashScreenViewParcelable;
+import android.window.WindowContextInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
@@ -163,9 +164,9 @@
public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
Configuration overrideConfig, int displayId);
- /** Deliver {@link android.window.WindowContext} configuration change. */
- public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
- @NonNull Configuration configuration, int displayId);
+ /** Deliver {@link android.window.WindowContextInfo} change. */
+ public abstract void handleWindowContextInfoChanged(@NonNull IBinder clientToken,
+ @NonNull WindowContextInfo info);
/** Deliver {@link android.window.WindowContext} window removal event. */
public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
deleted file mode 100644
index 3ac642f..0000000
--- a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static android.view.Display.INVALID_DISPLAY;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ClientTransactionHandler;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.os.Parcel;
-
-import java.util.Objects;
-
-/**
- * {@link android.window.WindowContext} configuration change message.
- * @hide
- */
-public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
-
- @Nullable
- private IBinder mClientToken;
- @Nullable
- private Configuration mConfiguration;
- private int mDisplayId;
-
- @Override
- public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
- @NonNull PendingTransactionActions pendingActions) {
- client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
- }
-
- // ObjectPoolItem implementation
-
- private WindowContextConfigurationChangeItem() {}
-
- /** Obtains an instance initialized with provided params. */
- public static WindowContextConfigurationChangeItem obtain(
- @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
- WindowContextConfigurationChangeItem instance =
- ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
- if (instance == null) {
- instance = new WindowContextConfigurationChangeItem();
- }
- instance.mClientToken = requireNonNull(clientToken);
- instance.mConfiguration = requireNonNull(config);
- instance.mDisplayId = displayId;
-
- return instance;
- }
-
- @Override
- public void recycle() {
- mClientToken = null;
- mConfiguration = null;
- mDisplayId = INVALID_DISPLAY;
- ObjectPool.recycle(this);
- }
-
- // Parcelable implementation
-
- /** Writes to Parcel. */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeStrongBinder(mClientToken);
- dest.writeTypedObject(mConfiguration, flags);
- dest.writeInt(mDisplayId);
- }
-
- /** Reads from Parcel. */
- private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
- mClientToken = in.readStrongBinder();
- mConfiguration = in.readTypedObject(Configuration.CREATOR);
- mDisplayId = in.readInt();
- }
-
- public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
- new Creator<>() {
- public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
- return new WindowContextConfigurationChangeItem(in);
- }
-
- public WindowContextConfigurationChangeItem[] newArray(int size) {
- return new WindowContextConfigurationChangeItem[size];
- }
- };
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
- return Objects.equals(mClientToken, other.mClientToken)
- && Objects.equals(mConfiguration, other.mConfiguration)
- && mDisplayId == other.mDisplayId;
- }
-
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + Objects.hashCode(mClientToken);
- result = 31 * result + Objects.hashCode(mConfiguration);
- result = 31 * result + mDisplayId;
- return result;
- }
-
- @Override
- public String toString() {
- return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
- + ", config=" + mConfiguration
- + ", displayId=" + mDisplayId
- + "}";
- }
-}
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
new file mode 100644
index 0000000..74721d5
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.window.WindowContextInfo;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextInfoChangeItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+ @Nullable
+ private WindowContextInfo mInfo;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextInfoChanged(mClientToken, mInfo);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextInfoChangeItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextInfoChangeItem obtain(
+ @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+ WindowContextInfoChangeItem instance =
+ ObjectPool.obtain(WindowContextInfoChangeItem.class);
+ if (instance == null) {
+ instance = new WindowContextInfoChangeItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+ instance.mInfo = new WindowContextInfo(config, displayId);
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ mInfo = null;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ dest.writeTypedObject(mInfo, flags);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextInfoChangeItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ mInfo = in.readTypedObject(WindowContextInfo.CREATOR);
+ }
+
+ public static final @NonNull Creator<WindowContextInfoChangeItem> CREATOR =
+ new Creator<>() {
+ public WindowContextInfoChangeItem createFromParcel(Parcel in) {
+ return new WindowContextInfoChangeItem(in);
+ }
+
+ public WindowContextInfoChangeItem[] newArray(int size) {
+ return new WindowContextInfoChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextInfoChangeItem other = (WindowContextInfoChangeItem) o;
+ return Objects.equals(mClientToken, other.mClientToken)
+ && Objects.equals(mInfo, other.mInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ result = 31 * result + Objects.hashCode(mInfo);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextInfoChangeItem{clientToken=" + mClientToken
+ + ", info=" + mInfo
+ + "}";
+ }
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b2cd7e9..9253998 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1985,7 +1985,7 @@
* Open a raw file descriptor to access (potentially type transformed)
* data from a "content:" URI. This interacts with the underlying
* {@link ContentProvider#openTypedAssetFile} method of the provider
- * associated with the given URI, to retrieve retrieve any appropriate
+ * associated with the given URI, to retrieve any appropriate
* data stream for the data stored there.
*
* <p>Unlike {@link #openAssetFileDescriptor}, this function only works
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2a6d84b..c11a8fc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6341,6 +6341,18 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.remoteauth.RemoteAuthManager} to discover,
+ * register and authenticate via remote authenticator devices.
+ *
+ * @see #getSystemService(String)
+ * @see android.remoteauth.RemoteAuthManager
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final String REMOTE_AUTH_SERVICE = "remote_auth";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.ambientcontext.AmbientContextManager}.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index 3e0ab90..518744e 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.util.Pools.SimplePool;
+import android.util.Slog;
import androidx.annotation.StyleableRes;
@@ -41,6 +42,7 @@
public static final int MAX_ATTR_LEN_PATH = 4000;
public static final int MAX_ATTR_LEN_DATA_VALUE = 4000;
+ private static final String TAG = "PackageParsing";
protected static final String TAG_ACTION = "action";
protected static final String TAG_ACTIVITY = "activity";
protected static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
@@ -715,23 +717,126 @@
mChildTagMask |= 1 << idx;
}
+ private boolean isComponentNameAttr(String name) {
+ switch (mTag) {
+ case TAG_ACTIVITY:
+ switch (name) {
+ case TAG_ATTR_NAME:
+ case TAG_ATTR_PARENT_ACTIVITY_NAME:
+ return true;
+ default:
+ return false;
+ }
+ case TAG_ACTIVITY_ALIAS:
+ switch (name) {
+ case TAG_ATTR_TARGET_ACTIVITY:
+ return true;
+ default:
+ return false;
+ }
+ case TAG_APPLICATION:
+ switch (name) {
+ case TAG_ATTR_BACKUP_AGENT:
+ case TAG_ATTR_NAME:
+ return true;
+ default:
+ return false;
+ }
+ case TAG_INSTRUMENTATION:
+ case TAG_PROVIDER:
+ case TAG_RECEIVER:
+ case TAG_SERVICE:
+ case TAG_USES_LIBRARY:
+ switch (name) {
+ case TAG_ATTR_NAME:
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+
+ private boolean isComponentNameAttr(@StyleableRes int index) {
+ switch (mTag) {
+ case TAG_ACTIVITY:
+ return index == R.styleable.AndroidManifestActivity_name
+ || index == R.styleable.AndroidManifestActivity_parentActivityName;
+ case TAG_ACTIVITY_ALIAS:
+ return index == R.styleable.AndroidManifestActivityAlias_targetActivity;
+ case TAG_APPLICATION:
+ return index == R.styleable.AndroidManifestApplication_backupAgent
+ || index == R.styleable.AndroidManifestApplication_name;
+ case TAG_INSTRUMENTATION:
+ return index == R.styleable.AndroidManifestInstrumentation_name;
+ case TAG_PROVIDER:
+ return index == R.styleable.AndroidManifestProvider_name;
+ case TAG_RECEIVER:
+ return index == R.styleable.AndroidManifestReceiver_name;
+ case TAG_SERVICE:
+ return index == R.styleable.AndroidManifestService_name;
+ case TAG_USES_LIBRARY:
+ return index == R.styleable.AndroidManifestUsesLibrary_name;
+ default:
+ return false;
+ }
+ }
+
boolean hasChild(String tag) {
return (mChildTagMask & (1 << getCounterIdx(tag))) != 0;
}
+ void validateComponentName(CharSequence name) {
+ int i = 0;
+ if (name.charAt(0) == '.') {
+ i = 1;
+ }
+ boolean isStart = true;
+ for (; i < name.length(); i++) {
+ if (name.charAt(i) == '.') {
+ if (isStart) {
+ break;
+ }
+ isStart = true;
+ } else {
+ if (isStart) {
+ if (Character.isJavaIdentifierStart(name.charAt(i))) {
+ isStart = false;
+ } else {
+ break;
+ }
+ } else if (!Character.isJavaIdentifierPart(name.charAt(i))) {
+ break;
+ }
+ }
+ }
+ if ((i < name.length()) || (name.charAt(name.length() - 1) == '.')) {
+ Slog.e(TAG, name + " is not a valid Java class name");
+ throw new SecurityException(name + " is not a valid Java class name");
+ }
+ }
+
void validateStrAttr(String attrName, String attrValue) {
if (attrValue != null && attrValue.length() > getAttrStrMaxLen(attrName)) {
throw new SecurityException("String length limit exceeded for attribute " + attrName
+ " in " + mTag);
}
+ if (isComponentNameAttr(attrName)) {
+ validateComponentName(attrValue);
+ }
}
void validateResStrAttr(@StyleableRes int index, CharSequence stringValue) {
if (stringValue != null && stringValue.length() > getResStrMaxLen(index)) {
throw new SecurityException("String length limit exceeded for attribute in " + mTag);
}
+ if (isComponentNameAttr(index)) {
+ validateComponentName(stringValue);
+ }
}
+
void seen(@NonNull Element element) {
TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
if (counter != null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index ae700a0..e06699b 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -266,7 +266,7 @@
List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
ArrayList<OutputConfiguration> outputList = new ArrayList<>();
for (CameraOutputConfig output : outputConfigs) {
- Surface outputSurface = initializeSurfrace(output);
+ Surface outputSurface = initializeSurface(output);
if (outputSurface == null) {
continue;
}
@@ -279,7 +279,7 @@
if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) {
cameraOutput.enableSurfaceSharing();
for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) {
- Surface sharedSurface = initializeSurfrace(sharedOutputConfig);
+ Surface sharedSurface = initializeSurface(sharedOutputConfig);
if (sharedSurface == null) {
continue;
}
@@ -1159,7 +1159,7 @@
return ret;
}
- private Surface initializeSurfrace(CameraOutputConfig output) {
+ private Surface initializeSurface(CameraOutputConfig output) {
switch(output.type) {
case CameraOutputConfig.TYPE_SURFACE:
if (output.surface == null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 1db4808..5d25681 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -2110,10 +2110,10 @@
HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap, Integer jpegOrientation,
Byte jpegQuality) {
ArrayList<CaptureBundle> ret = new ArrayList<>();
- for (Integer stagetId : captureMap.keySet()) {
- Pair<Image, TotalCaptureResult> entry = captureMap.get(stagetId);
+ for (Integer stageId : captureMap.keySet()) {
+ Pair<Image, TotalCaptureResult> entry = captureMap.get(stageId);
CaptureBundle bundle = new CaptureBundle();
- bundle.stage = stagetId;
+ bundle.stage = stageId;
bundle.captureImage = initializeParcelImage(entry.first);
bundle.sequenceId = entry.second.getSequenceId();
bundle.captureResult = entry.second.getNativeMetadata();
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7b5dd55..795eb4a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1019,8 +1019,9 @@
if (!mOnPreparedStylusHwCalled) {
// prepare hasn't been called by Stylus HOVER.
onPrepareStylusHandwriting();
- mOnPreparedStylusHwCalled = true;
}
+ // reset flag as it's not relevant after onStartStylusHandwriting().
+ mOnPreparedStylusHwCalled = false;
if (onStartStylusHandwriting()) {
cancelStylusWindowIdleTimeout();
mPrivOps.onStylusHandwritingReady(requestId, Process.myPid());
@@ -3089,7 +3090,8 @@
mInputStarted = false;
mStartedInputConnection = null;
mCurCompletions = null;
- if (mInkWindow != null) {
+ if (!mOnPreparedStylusHwCalled) {
+ // If IME didn't prepare to show InkWindow for current handwriting session.
finishStylusHandwriting();
}
// Back callback is typically unregistered in {@link #hideWindow()}, but it's possible
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index db1a741..f32a1f8 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -24,7 +24,9 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
+import java.util.StringJoiner;
/**
* A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -151,6 +153,13 @@
public abstract boolean hasVibrator(int vibratorId);
/**
+ * Returns a compact version of the {@link #toString()} result for debugging purposes.
+ *
+ * @hide
+ */
+ public abstract String toDebugString();
+
+ /**
* Adapts a {@link VibrationEffect} to a specific device vibrator using the ID.
*
* <p>This can be used for adapting effects to the capabilities of the specific device vibrator
@@ -437,6 +446,13 @@
return "Mono{mEffect=" + mEffect + '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ // Simplify vibration string, use the single effect to represent it.
+ return mEffect.toDebugString();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -619,6 +635,17 @@
return "Stereo{mEffects=" + mEffects + '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ StringJoiner sj = new StringJoiner(",", "Stereo{", "}");
+ for (int i = 0; i < mEffects.size(); i++) {
+ sj.add(String.format(Locale.ROOT, "vibrator(id=%d): %s",
+ mEffects.keyAt(i), mEffects.valueAt(i).toDebugString()));
+ }
+ return sj.toString();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -833,6 +860,17 @@
return "Sequential{mEffects=" + mEffects + ", mDelays=" + mDelays + '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ StringJoiner sj = new StringJoiner(",", "Sequential{", "}");
+ for (int i = 0; i < mEffects.size(); i++) {
+ sj.add(String.format(Locale.ROOT, "delayMs=%d, effect=%s",
+ mDelays.get(i), mEffects.get(i).toDebugString()));
+ }
+ return sj.toString();
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index b2d62eb..98f9dff 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -336,10 +336,11 @@
@Override
public String toString() {
- return "VibrationAttributes:"
- + " Usage=" + usageToString()
- + " Audio Usage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
- + " Flags=" + mFlags;
+ return "VibrationAttributes{"
+ + "mUsage=" + usageToString()
+ + ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
+ + ", mFlags=" + mFlags
+ + '}';
}
/** @hide */
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c3d7540..b24b45d 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -44,7 +44,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
+import java.util.StringJoiner;
/**
* A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -629,6 +631,13 @@
return MathUtils.constrain(a * fx, 0f, 1f);
}
+ /**
+ * Returns a compact version of the {@link #toString()} result for debugging purposes.
+ *
+ * @hide
+ */
+ public abstract String toDebugString();
+
/** @hide */
public static String effectIdToString(int effectId) {
switch (effectId) {
@@ -925,6 +934,23 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ if (mSegments.size() == 1 && mRepeatIndex < 0) {
+ // Simplify effect string, use the single segment to represent it.
+ return mSegments.get(0).toDebugString();
+ }
+ StringJoiner sj = new StringJoiner(",", "[", "]");
+ for (int i = 0; i < mSegments.size(); i++) {
+ sj.add(mSegments.get(i).toDebugString());
+ }
+ if (mRepeatIndex >= 0) {
+ return String.format(Locale.ROOT, "%s, repeat=%d", sj, mRepeatIndex);
+ }
+ return sj.toString();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -1250,23 +1276,23 @@
public static String primitiveToString(@PrimitiveType int id) {
switch (id) {
case PRIMITIVE_NOOP:
- return "PRIMITIVE_NOOP";
+ return "NOOP";
case PRIMITIVE_CLICK:
- return "PRIMITIVE_CLICK";
+ return "CLICK";
case PRIMITIVE_THUD:
- return "PRIMITIVE_THUD";
+ return "THUD";
case PRIMITIVE_SPIN:
- return "PRIMITIVE_SPIN";
+ return "SPIN";
case PRIMITIVE_QUICK_RISE:
- return "PRIMITIVE_QUICK_RISE";
+ return "QUICK_RISE";
case PRIMITIVE_SLOW_RISE:
- return "PRIMITIVE_SLOW_RISE";
+ return "SLOW_RISE";
case PRIMITIVE_QUICK_FALL:
- return "PRIMITIVE_QUICK_FALL";
+ return "QUICK_FALL";
case PRIMITIVE_TICK:
- return "PRIMITIVE_TICK";
+ return "TICK";
case PRIMITIVE_LOW_TICK:
- return "PRIMITIVE_LOW_TICK";
+ return "LOW_TICK";
default:
return Integer.toString(id);
}
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 71ec096..02e6856 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
+import android.util.IndentingPrintWriter;
import android.util.MathUtils;
import android.util.Range;
import android.util.SparseBooleanArray;
@@ -207,6 +208,25 @@
+ '}';
}
+ /** @hide */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VibratorInfo:");
+ pw.increaseIndent();
+ pw.println("id = " + mId);
+ pw.println("capabilities = " + Arrays.toString(getCapabilitiesNames()));
+ pw.println("capabilitiesFlags = " + Long.toBinaryString(mCapabilities));
+ pw.println("supportedEffects = " + Arrays.toString(getSupportedEffectsNames()));
+ pw.println("supportedPrimitives = " + Arrays.toString(getSupportedPrimitivesNames()));
+ pw.println("supportedBraking = " + Arrays.toString(getSupportedBrakingNames()));
+ pw.println("primitiveDelayMax = " + mPrimitiveDelayMax);
+ pw.println("compositionSizeMax = " + mCompositionSizeMax);
+ pw.println("pwlePrimitiveDurationMax = " + mPwlePrimitiveDurationMax);
+ pw.println("pwleSizeMax = " + mPwleSizeMax);
+ pw.println("q-factor = " + mQFactor);
+ pw.println("frequencyProfile = " + mFrequencyProfile);
+ pw.decreaseIndent();
+ }
+
/** Return the id of this vibrator. */
public int getId() {
return mId;
@@ -370,7 +390,7 @@
* Gets the resonant frequency of the vibrator.
*
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
- * this vibrator is a composite of multiple physical devices.
+ * this vibrator is a composite of multiple physical devices.
*/
public float getResonantFrequencyHz() {
return mFrequencyProfile.mResonantFrequencyHz;
@@ -380,7 +400,7 @@
* Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
*
* @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
- * this vibrator is a composite of multiple physical devices.
+ * this vibrator is a composite of multiple physical devices.
*/
public float getQFactor() {
return mQFactor;
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 462e5ad..42b6c2da 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -203,6 +203,15 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Prebaked=%s(%s, %s fallback)",
+ VibrationEffect.effectIdToString(mEffectId),
+ VibrationEffect.effectStrengthToString(mEffectStrength),
+ mFallback ? "with" : "no");
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 815a1dc..c52a09c 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -140,6 +140,13 @@
+ '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Primitive=%s(scale=%.2f, delay=%dms)",
+ VibrationEffect.Composition.primitiveToString(mPrimitiveId), mScale, mDelay);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index a5dee6c..e997bcd 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -179,6 +179,17 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Ramp=%dms(amplitude=%.2f%s to %.2f%s)",
+ mDuration,
+ mStartAmplitude,
+ Float.compare(mStartFrequencyHz, 0) == 0 ? "" : " @ " + mStartFrequencyHz + "Hz",
+ mEndAmplitude,
+ Float.compare(mEndFrequencyHz, 0) == 0 ? "" : " @ " + mEndFrequencyHz + "Hz");
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 54a44a8..a585aa8 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -161,6 +161,13 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Step=%dms(amplitude=%.2f%s)", mDuration, mAmplitude,
+ Float.compare(mFrequencyHz, 0) == 0 ? "" : " @ " + mFrequencyHz + "Hz");
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index 4790d81..bde334a 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -32,6 +32,9 @@
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
+import android.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
/**
* List of device-specific internal vibration configuration loaded from platform config.xml.
@@ -191,4 +194,18 @@
+ ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+ "}";
}
+
+ /**
+ * Write current settings into given {@link PrintWriter}, skipping the default settings.
+ *
+ * @hide
+ */
+ public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) {
+ pw.println("VibrationConfig:");
+ pw.increaseIndent();
+ pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
+ pw.println("rampStepDurationMs = " + mRampStepDurationMs);
+ pw.println("rampDownDurationMs = " + mRampDownDurationMs);
+ pw.decreaseIndent();
+ }
}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 9a19ed4..3b286a7 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -116,6 +116,13 @@
public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength);
/**
+ * Returns a compact version of the {@link #toString()} result for debugging purposes.
+ *
+ * @hide
+ */
+ public abstract String toDebugString();
+
+ /**
* Checks the given frequency argument is valid to represent a vibration effect frequency in
* hertz, i.e. a finite non-negative value.
*
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c1474eb..d3b7a5b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -71,6 +71,7 @@
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;
+import android.window.WindowContextInfo;
/**
* System private interface to the window manager.
@@ -858,10 +859,10 @@
* @param displayId The display associated with the window context
* @param options A bundle used to pass window-related options and choose the right DisplayArea
*
- * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
- * attached to the DisplayArea successfully. {@code null}, otherwise.
+ * @return the {@link WindowContextInfo} of the DisplayArea if the WindowContext is attached to
+ * the DisplayArea successfully. {@code null}, otherwise.
*/
- @nullable Configuration attachWindowContextToDisplayArea(in IApplicationThread appThread,
+ @nullable WindowContextInfo attachWindowContextToDisplayArea(in IApplicationThread appThread,
IBinder clientToken, int type, int displayId, in @nullable Bundle options);
/**
@@ -879,13 +880,15 @@
* the WindowContext's token}
* @param token the WindowToken to attach
*
+ * @return the {@link WindowContextInfo} of the WindowToken if the WindowContext is attached to
+ * the WindowToken successfully. {@code null}, otherwise.
* @throws IllegalArgumentException if the {@code clientToken} have not been attached to
* the server or the WindowContext's type doesn't match WindowToken {@code token}'s type.
*
* @see #attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
*/
- void attachWindowContextToWindowToken(in IApplicationThread appThread, IBinder clientToken,
- IBinder token);
+ @nullable WindowContextInfo attachWindowContextToWindowToken(in IApplicationThread appThread,
+ IBinder clientToken, IBinder token);
/**
* Attaches a {@code clientToken} to associate with DisplayContent.
@@ -899,11 +902,11 @@
* the WindowContext's token}
* @param displayId The display associated with the window context
*
- * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
- * attached to the DisplayContent successfully. {@code null}, otherwise.
+ * @return the {@link WindowContextInfo} of the DisplayContent if the WindowContext is attached
+ * to the DisplayContent successfully. {@code null}, otherwise.
* @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
*/
- @nullable Configuration attachWindowContextToDisplayContent(in IApplicationThread appThread,
+ @nullable WindowContextInfo attachWindowContextToDisplayContent(in IApplicationThread appThread,
IBinder clientToken, int displayId);
/**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 17afd55..4ecfc40 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -844,12 +844,12 @@
public boolean onStateChanged(InsetsState state) {
boolean stateChanged = false;
if (!CAPTION_ON_SHELL) {
- stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
- false /* excludeInvisibleIme */)
+ stateChanged = !mState.equals(state, true /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */)
|| captionInsetsUnchanged();
} else {
- stateChanged = !mState.equals(state, false /* excludingCaptionInsets */,
- false /* excludeInvisibleIme */);
+ stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */);
}
if (!stateChanged && mLastDispatchedState.equals(state)) {
return false;
@@ -862,8 +862,8 @@
applyLocalVisibilityOverride();
updateCompatSysUiVisibility();
- if (!mState.equals(lastState, false /* excludingCaptionInsets */,
- true /* excludeInvisibleIme */)) {
+ if (!mState.equals(lastState, false /* excludesCaptionBar */,
+ true /* excludesInvisibleIme */)) {
if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
mHost.notifyInsetsChanged();
if (lastState.getDisplayFrame().equals(mState.getDisplayFrame())) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c13b9ab..af24140 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -380,11 +380,17 @@
@InternalInsetsSide @Nullable SparseIntArray idSideMap,
@Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
int index = indexOf(type);
- Insets existing = typeInsetsMap[index];
- if (existing == null) {
- typeInsetsMap[index] = insets;
- } else {
- typeInsetsMap[index] = Insets.max(existing, insets);
+
+ // Don't put Insets.NONE into typeInsetsMap. Otherwise, two WindowInsets can be considered
+ // as non-equal while they provide the same insets of each type from WindowInsets#getInsets
+ // if one WindowInsets has Insets.NONE for a type and the other has null for the same type.
+ if (!Insets.NONE.equals(insets)) {
+ Insets existing = typeInsetsMap[index];
+ if (existing == null) {
+ typeInsetsMap[index] = insets;
+ } else {
+ typeInsetsMap[index] = Insets.max(existing, insets);
+ }
}
if (typeVisibilityMap != null) {
@@ -696,15 +702,14 @@
* An equals method can exclude the caption insets. This is useful because we assemble the
* caption insets information on the client side, and when we communicate with server, it's
* excluded.
- * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
- * ignore the caption insets source value.
- * @param excludeInvisibleImeFrames If {@link WindowInsets.Type#ime()} frames should be ignored
- * when IME is not visible.
+ * @param excludesCaptionBar If {@link Type#captionBar()}} should be ignored.
+ * @param excludesInvisibleIme If {@link WindowInsets.Type#ime()} should be ignored when IME is
+ * not visible.
* @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
*/
@VisibleForTesting
- public boolean equals(@Nullable Object o, boolean excludingCaptionInsets,
- boolean excludeInvisibleImeFrames) {
+ public boolean equals(@Nullable Object o, boolean excludesCaptionBar,
+ boolean excludesInvisibleIme) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
@@ -721,29 +726,35 @@
final SparseArray<InsetsSource> thisSources = mSources;
final SparseArray<InsetsSource> thatSources = state.mSources;
- if (!excludingCaptionInsets && !excludeInvisibleImeFrames) {
+ if (!excludesCaptionBar && !excludesInvisibleIme) {
return thisSources.contentEquals(thatSources);
} else {
final int thisSize = thisSources.size();
final int thatSize = thatSources.size();
int thisIndex = 0;
int thatIndex = 0;
- while (thisIndex < thisSize && thatIndex < thatSize) {
+ while (thisIndex < thisSize || thatIndex < thatSize) {
+ InsetsSource thisSource = thisIndex < thisSize
+ ? thisSources.valueAt(thisIndex)
+ : null;
+
// Seek to the next non-excluding source of ours.
- InsetsSource thisSource = thisSources.valueAt(thisIndex);
while (thisSource != null
- && (excludingCaptionInsets && thisSource.getType() == captionBar()
- || excludeInvisibleImeFrames && thisSource.getType() == ime()
+ && (excludesCaptionBar && thisSource.getType() == captionBar()
+ || excludesInvisibleIme && thisSource.getType() == ime()
&& !thisSource.isVisible())) {
thisIndex++;
thisSource = thisIndex < thisSize ? thisSources.valueAt(thisIndex) : null;
}
+ InsetsSource thatSource = thatIndex < thatSize
+ ? thatSources.valueAt(thatIndex)
+ : null;
+
// Seek to the next non-excluding source of theirs.
- InsetsSource thatSource = thatSources.valueAt(thatIndex);
while (thatSource != null
- && (excludingCaptionInsets && thatSource.getType() == captionBar()
- || excludeInvisibleImeFrames && thatSource.getType() == ime()
+ && (excludesCaptionBar && thatSource.getType() == captionBar()
+ || excludesInvisibleIme && thatSource.getType() == ime()
&& !thatSource.isVisible())) {
thatIndex++;
thatSource = thatIndex < thatSize ? thatSources.valueAt(thatIndex) : null;
@@ -756,7 +767,7 @@
thisIndex++;
thatIndex++;
}
- return thisIndex >= thisSize && thatIndex >= thatSize;
+ return true;
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 01a99b9..1b1098d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2539,7 +2539,7 @@
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
final float x = event.getXDispatchLocation(0);
- final float y = event.getXDispatchLocation(0);
+ final float y = event.getYDispatchLocation(0);
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 36eef53..c9ac245 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -137,12 +137,14 @@
* @see WindowProviderService#attachToWindowToken(IBinder))
* @see IWindowManager#attachWindowContextToWindowToken
*/
- public void attachToWindowToken(IBinder windowToken) {
+ public void attachToWindowToken(@NonNull IBinder windowToken) {
if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
}
- getWindowTokenClientController().attachToWindowToken(mToken, windowToken);
+ if (!getWindowTokenClientController().attachToWindowToken(mToken, windowToken)) {
+ Log.e(TAG, "attachToWindowToken fail");
+ }
}
/** Detaches the window context from the node it's currently associated with. */
diff --git a/core/java/android/window/WindowContextInfo.aidl b/core/java/android/window/WindowContextInfo.aidl
new file mode 100644
index 0000000..360431c
--- /dev/null
+++ b/core/java/android/window/WindowContextInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/** @hide */
+parcelable WindowContextInfo;
\ No newline at end of file
diff --git a/core/java/android/window/WindowContextInfo.java b/core/java/android/window/WindowContextInfo.java
new file mode 100644
index 0000000..3c21cd4
--- /dev/null
+++ b/core/java/android/window/WindowContextInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Stores information about a particular window that a {@link WindowContext} is attached to.
+ * @hide
+ */
+public class WindowContextInfo implements Parcelable {
+
+ /**
+ * Configuration of the window.
+ */
+ @NonNull
+ private final Configuration mConfiguration;
+
+ /**
+ * The display id that the window is attached to.
+ */
+ private final int mDisplayId;
+
+ public WindowContextInfo(@NonNull Configuration configuration, int displayId) {
+ mConfiguration = requireNonNull(configuration);
+ mDisplayId = displayId;
+ }
+
+ @NonNull
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mConfiguration, flags);
+ dest.writeInt(mDisplayId);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextInfo(@NonNull Parcel in) {
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mDisplayId = in.readInt();
+ }
+
+ public static final @NonNull Creator<WindowContextInfo> CREATOR = new Creator<>() {
+ public WindowContextInfo createFromParcel(Parcel in) {
+ return new WindowContextInfo(in);
+ }
+
+ public WindowContextInfo[] newArray(int size) {
+ return new WindowContextInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextInfo other = (WindowContextInfo) o;
+ return Objects.equals(mConfiguration, other.mConfiguration)
+ && mDisplayId == other.mDisplayId;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + mDisplayId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextInfo{config=" + mConfiguration
+ + ", displayId=" + mDisplayId
+ + "}";
+ }
+}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 7458563..6a32529 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -99,6 +99,13 @@
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
// TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
+ postOnConfigurationChanged(newConfig, newDisplayId);
+ }
+
+ /**
+ * Posts an {@link #onConfigurationChanged} to the main thread.
+ */
+ public void postOnConfigurationChanged(@NonNull Configuration newConfig, int newDisplayId) {
mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
}
@@ -162,7 +169,6 @@
windowContext.dispatchConfigurationChanged(newConfig);
}
-
if (shouldReportConfigChange && diff != 0
&& context instanceof WindowProviderService) {
final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 14b9df6..7a84123 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -22,10 +22,9 @@
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IApplicationThread;
-import android.app.servertransaction.WindowContextConfigurationChangeItem;
+import android.app.servertransaction.WindowContextInfoChangeItem;
import android.app.servertransaction.WindowContextWindowRemovalItem;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -94,17 +93,17 @@
*/
public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
@WindowType int type, int displayId, @Nullable Bundle options) {
- final Configuration configuration;
+ final WindowContextInfo info;
try {
- configuration = getWindowManagerService().attachWindowContextToDisplayArea(
+ info = getWindowManagerService().attachWindowContextToDisplayArea(
mAppThread, client, type, displayId, options);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (configuration == null) {
+ if (info == null) {
return false;
}
- onWindowContextTokenAttached(client, displayId, configuration);
+ onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
return true;
}
@@ -121,16 +120,16 @@
if (wms == null) {
return false;
}
- final Configuration configuration;
+ final WindowContextInfo info;
try {
- configuration = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
+ info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (configuration == null) {
+ if (info == null) {
return false;
}
- onWindowContextTokenAttached(client, displayId, configuration);
+ onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
return true;
}
@@ -139,19 +138,23 @@
*
* @param client The {@link WindowTokenClient} to attach.
* @param windowToken the window token to associated with
+ * @return {@code true} if attaching successfully.
*/
- public void attachToWindowToken(@NonNull WindowTokenClient client,
+ public boolean attachToWindowToken(@NonNull WindowTokenClient client,
@NonNull IBinder windowToken) {
+ final WindowContextInfo info;
try {
- getWindowManagerService().attachWindowContextToWindowToken(
+ info = getWindowManagerService().attachWindowContextToWindowToken(
mAppThread, client, windowToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- // We don't report configuration change for now.
- synchronized (mLock) {
- mWindowTokenClientMap.put(client.asBinder(), client);
+ if (info == null) {
+ return false;
}
+ // We currently report configuration for WindowToken after attached.
+ onWindowContextTokenAttached(client, info, true /* shouldReportConfigChange */);
+ return true;
}
/** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
@@ -168,21 +171,30 @@
}
}
- private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
- @NonNull Configuration configuration) {
+ private void onWindowContextTokenAttached(@NonNull WindowTokenClient client,
+ @NonNull WindowContextInfo info, boolean shouldReportConfigChange) {
synchronized (mLock) {
mWindowTokenClientMap.put(client.asBinder(), client);
}
- client.onConfigurationChanged(configuration, displayId,
- false /* shouldReportConfigChange */);
+ if (shouldReportConfigChange) {
+ // Should trigger an #onConfigurationChanged callback to the WindowContext. Post the
+ // dispatch in the next loop to prevent the callback from being dispatched before
+ // #onCreate or WindowContext creation..
+ client.postOnConfigurationChanged(info.getConfiguration(), info.getDisplayId());
+ } else {
+ // Apply the config change directly in case users get stale values after WindowContext
+ // creation.
+ client.onConfigurationChanged(info.getConfiguration(), info.getDisplayId(),
+ false /* shouldReportConfigChange */);
+ }
}
- /** Called when receives {@link WindowContextConfigurationChangeItem}. */
- public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
- @NonNull Configuration configuration, int displayId) {
+ /** Called when receives {@link WindowContextInfoChangeItem}. */
+ public void onWindowContextInfoChanged(@NonNull IBinder clientToken,
+ @NonNull WindowContextInfo info) {
final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
if (windowTokenClient != null) {
- windowTokenClient.onConfigurationChanged(configuration, displayId);
+ windowTokenClient.onConfigurationChanged(info.getConfiguration(), info.getDisplayId());
}
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 325ebbe..8e619a8 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -609,4 +609,6 @@
optional bool animation_in_progress = 1;
optional int32 last_back_type = 2;
optional bool show_wallpaper = 3;
+ optional string main_open_activity = 4;
+ optional bool animation_running = 5;
}
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 54dff08..64be87b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3938,8 +3938,14 @@
<!-- Flag indicating device support for EAP SIM, AKA, AKA' -->
<bool name="config_eap_sim_based_auth_supported">true</bool>
+ <!-- How long history of recent vibrations should be kept for the dumpsys. -->
+ <integer name="config_recentVibrationsDumpSizeLimit">20</integer>
+
<!-- How long history of previous vibrations should be kept for the dumpsys. -->
- <integer name="config_previousVibrationsDumpLimit">50</integer>
+ <integer name="config_previousVibrationsDumpSizeLimit">50</integer>
+
+ <!-- How close vibration request should be when they're aggregated for dumpsys, in ms. -->
+ <integer name="config_previousVibrationsDumpAggregationTimeMillisLimit">1000</integer>
<!-- The default vibration strength, must be between 1 and 255 inclusive. -->
<integer name="config_defaultVibrationAmplitude">255</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9da0f17..3aae3ca 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2038,7 +2038,9 @@
<java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" />
<java-symbol type="integer" name="config_notificationServiceArchiveSize" />
<java-symbol type="dimen" name="config_rotaryEncoderAxisScrollTickInterval" />
- <java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
+ <java-symbol type="integer" name="config_recentVibrationsDumpSizeLimit" />
+ <java-symbol type="integer" name="config_previousVibrationsDumpSizeLimit" />
+ <java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8da6d74..c904d96 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -64,6 +64,7 @@
import android.util.MergedConfiguration;
import android.view.Display;
import android.view.View;
+import android.window.WindowContextInfo;
import android.window.WindowTokenClientController;
import androidx.test.filters.MediumTest;
@@ -753,13 +754,12 @@
WindowTokenClientController.overrideForTesting(windowTokenClientController);
final IBinder clientToken = mock(IBinder.class);
final Configuration configuration = new Configuration();
+ final WindowContextInfo info = new WindowContextInfo(configuration, DEFAULT_DISPLAY);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
- .handleWindowContextConfigurationChanged(
- clientToken, configuration, DEFAULT_DISPLAY));
+ .handleWindowContextInfoChanged(clientToken, info));
- verify(windowTokenClientController).onWindowContextConfigurationChanged(
- clientToken, configuration, DEFAULT_DISPLAY);
+ verify(windowTokenClientController).onWindowContextInfoChanged(clientToken, info);
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
similarity index 79%
rename from core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
rename to core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
index 7811e1a..37a517e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
@@ -23,6 +23,7 @@
import android.app.ClientTransactionHandler;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.window.WindowContextInfo;
import org.junit.Before;
import org.junit.Test;
@@ -30,12 +31,12 @@
import org.mockito.MockitoAnnotations;
/**
- * Tests for {@link WindowContextConfigurationChangeItem}.
+ * Tests for {@link WindowContextInfoChangeItem}.
*
* Build/Install/Run:
- * atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ * atest FrameworksCoreTests:WindowContextInfoChangeItemTest
*/
-public class WindowContextConfigurationChangeItemTest {
+public class WindowContextInfoChangeItemTest {
@Mock
private ClientTransactionHandler mHandler;
@@ -55,11 +56,11 @@
@Test
public void testExecute() {
- final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+ final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
.obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
item.execute(mHandler, mToken, mPendingActions);
- verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
- DEFAULT_DISPLAY);
+ verify(mHandler).handleWindowContextInfoChanged(mClientToken,
+ new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fde1a6d..b06cd39 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -43,6 +43,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -290,6 +291,18 @@
}
@Test
+ public void testCalculateInsets_emptyIme() {
+ WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false, false,
+ SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ mState.getOrCreateSource(ID_IME, ime());
+ WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false, false,
+ SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(Insets.NONE, insets1.getInsets(ime()));
+ assertEquals(Insets.NONE, insets2.getInsets(ime()));
+ assertEquals(insets1, insets2);
+ }
+
+ @Test
public void testStripForDispatch() {
mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
.setFrame(new Rect(0, 0, 100, 100))
@@ -304,6 +317,73 @@
}
@Test
+ public void testEquals() {
+ final InsetsState state1 = new InsetsState();
+ final InsetsState state2 = new InsetsState();
+ assertTrue(state1.equals(state2));
+
+ state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+ assertFalse(state1.equals(state2));
+
+ state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+ assertTrue(state1.equals(state2));
+
+ state2.addSource(new InsetsSource(ID_NAVIGATION_BAR, navigationBars()));
+ assertFalse(state1.equals(state2));
+ }
+
+ @Test
+ public void testEquals_excludesCaptionBar() {
+ final InsetsState state1 = new InsetsState();
+ final InsetsState state2 = new InsetsState();
+
+ state1.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 5));
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ assertTrue(state1.equals(
+ state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+
+ state2.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 10));
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ assertTrue(state1.equals(
+ state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+
+ state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+ state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ assertTrue(state1.equals(
+ state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ }
+
+ @Test
+ public void testEquals_excludesInvisibleIme() {
+ final InsetsState state1 = new InsetsState();
+ final InsetsState state2 = new InsetsState();
+
+ final InsetsSource imeSource1 = new InsetsSource(ID_IME, ime()).setVisible(true);
+ state1.addSource(imeSource1);
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+
+ imeSource1.setVisible(false);
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ assertTrue(state1.equals(
+ state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+
+ final InsetsSource imeSource2 = new InsetsSource(ID_IME, ime()).setFrame(0, 0, 0, 10);
+ state2.addSource(imeSource2);
+ assertFalse(state1.equals(
+ state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+ assertTrue(state1.equals(
+ state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+ }
+
+ @Test
public void testEquals_differentRect() {
mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
.setFrame(new Rect(0, 0, 100, 100));
@@ -404,17 +484,6 @@
}
@Test
- public void testEquals_excludeInvisibleIme() {
- mState.getOrCreateSource(ID_IME, ime())
- .setFrame(new Rect(0, 0, 100, 100))
- .setVisible(false);
- mState2.getOrCreateSource(ID_IME, ime())
- .setFrame(new Rect(0, 0, 100, 200))
- .setVisible(false);
- assertTrue(mState2.equals(mState, true, true /* excludeInvisibleIme */));
- }
-
- @Test
public void testParcelUnparcel() {
mState.getOrCreateSource(ID_IME, ime())
.setFrame(new Rect(0, 0, 100, 100))
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index b37c8fd..bce3f3e 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -49,11 +49,11 @@
public void testDispatchMouseEventsUnderCursor() {
final Context context = getInstrumentation().getContext();
final TestView viewGroup = new TestView(context, 0 /* left */, 0 /* top */,
- 200 /* right */, 200 /* bottom */);
+ 200 /* right */, 100 /* bottom */);
final TestView viewA = spy(new TestView(context, 0 /* left */, 0 /* top */,
- 100 /* right */, 200 /* bottom */));
+ 100 /* right */, 100 /* bottom */));
final TestView viewB = spy(new TestView(context, 100 /* left */, 0 /* top */,
- 200 /* right */, 200 /* bottom */));
+ 200 /* right */, 100 /* bottom */));
viewGroup.addView(viewA);
viewGroup.addView(viewB);
@@ -73,10 +73,10 @@
MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[2];
coords[0] = new MotionEvent.PointerCoords();
coords[0].x = 80;
- coords[0].y = 100;
+ coords[0].y = 50;
coords[1] = new MotionEvent.PointerCoords();
coords[1].x = 240;
- coords[1].y = 100;
+ coords[1].y = 50;
MotionEvent event;
// Make sure the down event is active with a pointer which coordinate is different from the
@@ -91,6 +91,10 @@
viewGroup.onResolvePointerIcon(event, 0 /* pointerIndex */);
verify(viewB).onResolvePointerIcon(event, 0);
+ event.setAction(MotionEvent.ACTION_SCROLL);
+ viewGroup.dispatchGenericMotionEvent(event);
+ verify(viewB).dispatchGenericMotionEvent(event);
+
event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */,
@@ -102,8 +106,13 @@
viewGroup.onResolvePointerIcon(event, 1 /* pointerIndex */);
verify(viewB).onResolvePointerIcon(event, 1);
+ event.setAction(MotionEvent.ACTION_SCROLL);
+ viewGroup.dispatchGenericMotionEvent(event);
+ verify(viewB).dispatchGenericMotionEvent(event);
+
verify(viewA, never()).dispatchTouchEvent(any());
verify(viewA, never()).onResolvePointerIcon(any(), anyInt());
+ verify(viewA, never()).dispatchGenericMotionEvent(any());
}
/**
diff --git a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
index c4f7b3b..7bd6f05 100644
--- a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
@@ -64,6 +64,7 @@
// Can't mock final class.
private final Configuration mConfiguration = new Configuration();
+ private WindowContextInfo mWindowContextInfo;
private WindowTokenClientController mController;
@Before
@@ -72,6 +73,7 @@
doReturn(mClientToken).when(mWindowTokenClient).asBinder();
mController = spy(WindowTokenClientController.createInstanceForTesting());
doReturn(mWindowManagerService).when(mController).getWindowManagerService();
+ mWindowContextInfo = new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY);
}
@Test
@@ -86,7 +88,7 @@
TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToDisplayArea(
any(), any(), anyInt(), anyInt(), any());
assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
@@ -109,7 +111,7 @@
verify(mWindowManagerService, never()).detachWindowContext(any());
- doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToDisplayArea(
any(), any(), anyInt(), anyInt(), any());
mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
DEFAULT_DISPLAY, null /* options */);
@@ -129,8 +131,8 @@
DEFAULT_DISPLAY);
verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayContent(
- any(), any(), anyInt());
+ doReturn(mWindowContextInfo).when(mWindowManagerService)
+ .attachWindowContextToDisplayContent(any(), any(), anyInt());
assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
@@ -150,8 +152,8 @@
verify(mWindowManagerService, never()).detachWindowContext(any());
- doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayContent(
- any(), any(), anyInt());
+ doReturn(mWindowContextInfo).when(mWindowManagerService)
+ .attachWindowContextToDisplayContent(any(), any(), anyInt());
mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
mController.detachIfNeeded(mWindowTokenClient);
@@ -160,11 +162,20 @@
@Test
public void testAttachToWindowToken() throws RemoteException {
- mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken(
+ any(), any(), any());
+ assertFalse(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
verify(mWindowManagerService).attachWindowContextToWindowToken(
ActivityThread.currentActivityThread().getApplicationThread(), mWindowTokenClient,
mWindowToken);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mWindowContextInfo).when(mWindowManagerService)
+ .attachWindowContextToWindowToken(any(), any(), any());
+
+ assertTrue(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
+ verify(mWindowTokenClient).postOnConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
}
@Test
@@ -173,6 +184,15 @@
verify(mWindowManagerService, never()).detachWindowContext(any());
+ doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken(
+ any(), any(), any());
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContext(any());
+
+ doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToWindowToken(
+ any(), any(), any());
mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
mController.detachIfNeeded(mWindowTokenClient);
@@ -180,28 +200,43 @@
}
@Test
- public void testOnWindowContextConfigurationChanged() {
- mController.onWindowContextConfigurationChanged(
- mClientToken, mConfiguration, DEFAULT_DISPLAY);
+ public void testOnWindowContextInfoChanged() throws RemoteException {
+ doReturn(mWindowContextInfo).when(mWindowManagerService)
+ .attachWindowContextToWindowToken(any(), any(), any());
+
+ // No invoke if not attached.
+ mController.onWindowContextInfoChanged(mClientToken, mWindowContextInfo);
verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
- mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ // Invoke postOnConfigurationChanged when attached
+ assertTrue(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
- mController.onWindowContextConfigurationChanged(
- mClientToken, mConfiguration, DEFAULT_DISPLAY);
+ verify(mWindowTokenClient).postOnConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
- verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+ // Invoke onConfigurationChanged when onWindowContextInfoChanged
+ mController.onWindowContextInfoChanged(
+ mClientToken, new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY + 1));
+
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY + 1);
}
@Test
- public void testOnWindowContextWindowRemoved() {
+ public void testOnWindowContextWindowRemoved() throws RemoteException {
+ doReturn(mWindowContextInfo).when(mWindowManagerService)
+ .attachWindowContextToWindowToken(any(), any(), any());
+
+ // No invoke if not attached.
mController.onWindowContextWindowRemoved(mClientToken);
verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+ // No invoke if not onWindowTokenRemoved.
mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+ // Invoke onWindowTokenRemoved when onWindowContextWindowRemoved
mController.onWindowContextWindowRemoved(mClientToken);
verify(mWindowTokenClient).onWindowTokenRemoved();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index e69825f2..b0afa68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1053,6 +1053,20 @@
mBubbleData.setExpanded(false /* expanded */);
}
+ /**
+ * Update expanded state when a single bubble is dragged in Launcher.
+ * Will be called only when bubble bar is expanded.
+ * @param bubbleKey key of the bubble to collapse/expand
+ * @param isBeingDragged whether the bubble is being dragged
+ */
+ public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
+ if (mBubbleData.getSelectedBubble() != null
+ && mBubbleData.getSelectedBubble().getKey().equals(bubbleKey)) {
+ // Should collapse/expand only if equals to selected bubble.
+ mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ !isBeingDragged);
+ }
+ }
+
@VisibleForTesting
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
@@ -1438,6 +1452,17 @@
}
}
+ /**
+ * Removes all the bubbles.
+ * <p>
+ * Must be called from the main thread.
+ */
+ @VisibleForTesting
+ @MainThread
+ public void removeAllBubbles(@Bubbles.DismissReason int reason) {
+ mBubbleData.dismissAll(reason);
+ }
+
private void onEntryAdded(BubbleEntry entry) {
if (canLaunchInTaskView(mContext, entry)) {
updateBubble(entry);
@@ -2099,14 +2124,25 @@
}
@Override
- public void removeBubble(String key, int reason) {
- // TODO (b/271466616) allow removals from launcher
+ public void removeBubble(String key) {
+ mMainExecutor.execute(
+ () -> mController.removeBubble(key, Bubbles.DISMISS_USER_GESTURE));
+ }
+
+ @Override
+ public void removeAllBubbles() {
+ mMainExecutor.execute(() -> mController.removeAllBubbles(Bubbles.DISMISS_USER_GESTURE));
}
@Override
public void collapseBubbles() {
mMainExecutor.execute(() -> mController.collapseStack());
}
+
+ @Override
+ public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
+ mMainExecutor.execute(() -> mController.onBubbleDrag(bubbleKey, isBeingDragged));
+ }
}
private class BubblesImpl implements Bubbles {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 282db9e..f58b121 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -108,8 +108,10 @@
public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
+ // LINT.IfChange
public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
+ // LINT.ThenChange(com/android/launcher3/taskbar/bubbles/BubbleDismissController.java)
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 351319f..4dda068 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -31,8 +31,12 @@
oneway void showBubble(in String key, in int bubbleBarOffsetX, in int bubbleBarOffsetY) = 3;
- oneway void removeBubble(in String key, in int reason) = 4;
+ oneway void removeBubble(in String key) = 4;
- oneway void collapseBubbles() = 5;
+ oneway void removeAllBubbles() = 5;
+
+ oneway void collapseBubbles() = 6;
+
+ oneway void onBubbleDrag(in String key, in boolean isBeingDragged) = 7;
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index b3602b3..689323b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -215,6 +215,14 @@
mExpandedViewAlphaAnimator.reverse();
}
+ /**
+ * Cancel current animations
+ */
+ public void cancelAnimations() {
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
+ mExpandedViewAlphaAnimator.cancel();
+ }
+
private void updateExpandedView() {
if (mExpandedBubble == null || mExpandedBubble.getBubbleBarExpandedView() == null) {
Log.w(TAG, "Trying to update the expanded view without a bubble");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 8ead18b..bc04bfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -150,6 +150,12 @@
mExpandedView = null;
}
if (mExpandedView == null) {
+ if (expandedView.getParent() != null) {
+ // Expanded view might be animating collapse and is still attached
+ // Cancel current animations and remove from parent
+ mAnimationHelper.cancelAnimations();
+ removeView(expandedView);
+ }
mExpandedBubble = b;
mExpandedView = expandedView;
boolean isOverflowExpanded = b.getKey().equals(BubbleOverflow.KEY);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 4193ffa..ac8e4d4 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -64,6 +64,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -454,6 +455,7 @@
assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
+ @Ignore // TODO(b/291800179): Diagnose flakiness and re-enable.
@Test
public void testRouterRelease_managerGetRoutingSessions() throws Exception {
CountDownLatch transferLatch = new CountDownLatch(1);
diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING
index 6a5d2c0..fd394fc 100644
--- a/native/android/TEST_MAPPING
+++ b/native/android/TEST_MAPPING
@@ -12,6 +12,15 @@
}
],
"file_patterns": ["permission_manager.cpp"]
+ },
+ {
+ "name": "CtsOsTestCases",
+ "options": [
+ {
+ "include-filter": "android.os.cts.PerformanceHintManagerTest"
+ }
+ ],
+ "file_patterns": ["performance_hint.cpp"]
}
]
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
index 8639f47..0d1475a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
@@ -16,6 +16,8 @@
package com.android.packageinstaller;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
@@ -186,7 +188,9 @@
int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
- context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
+ Intent intentToStart = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ intentToStart.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intentToStart);
return;
}
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index 5d6c343..accaa67 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -25,10 +25,12 @@
<color name="settingslib_color_blue100">#d2e3fc</color>
<color name="settingslib_color_blue50">#e8f0fe</color>
<color name="settingslib_color_green600">#1e8e3e</color>
+ <color name="settingslib_color_green500">#34A853</color>
<color name="settingslib_color_green400">#5bb974</color>
<color name="settingslib_color_green100">#ceead6</color>
<color name="settingslib_color_green50">#e6f4ea</color>
<color name="settingslib_color_red600">#d93025</color>
+ <color name="settingslib_color_red500">#B3261E</color>
<color name="settingslib_color_red400">#ee675c</color>
<color name="settingslib_color_red100">#fad2cf</color>
<color name="settingslib_color_red50">#fce8e6</color>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 5e2c437..f166a18 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -42,9 +42,6 @@
".grey600",
R.color.settingslib_color_grey400);
map.put(
- ".grey700",
- R.color.settingslib_color_grey500);
- map.put(
".grey800",
R.color.settingslib_color_grey300);
map.put(
@@ -62,6 +59,12 @@
map.put(
".green400",
R.color.settingslib_color_green600);
+ map.put(
+ ".green200",
+ R.color.settingslib_color_green500);
+ map.put(
+ ".red200",
+ R.color.settingslib_color_red500);
DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 5ed450a..c26cd12 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -25,8 +25,8 @@
import android.os.Bundle;
import android.provider.Browser;
import android.provider.Settings;
-import android.widget.TextView;
import android.view.View;
+import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
@@ -55,6 +55,10 @@
((TextView) findViewById(R.id.action_bar_title)).setText(
getResources().getString(R.string.accessibility_menu_settings_name)
);
+ actionBar.setDisplayOptions(
+ ActionBar.DISPLAY_TITLE_MULTIPLE_LINES
+ | ActionBar.DISPLAY_SHOW_TITLE
+ | ActionBar.DISPLAY_HOME_AS_UP);
}
/**
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index d43276c..46f5971 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -190,6 +190,10 @@
/** Flag denoting transit clock are enabled in wallpaper picker. */
const val FLAG_NAME_PAGE_TRANSITIONS = "wallpaper_picker_page_transitions"
+ /** Flag denoting whether preview loading animation is enabled. */
+ const val FLAG_NAME_WALLPAPER_PICKER_PREVIEW_ANIMATION =
+ "wallpaper_picker_preview_animation"
+
object Columns {
/** String. Unique ID for the flag. */
const val NAME = "name"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 4d3a742..2c224f62 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -423,6 +423,7 @@
* Refresh clock. Called in response to TIME_TICK broadcasts.
*/
void refresh() {
+ mLogBuffer.log(TAG, LogLevel.INFO, "refresh");
if (mSmartspaceController != null) {
mSmartspaceController.requestSmartspaceUpdate();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 61addab..8a0c9ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -1198,6 +1198,8 @@
});
mPopup.show();
});
+
+ mUserSwitcherViewGroup.setAlpha(0f);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index a04d13b..8e92941 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,6 +20,7 @@
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
import android.animation.ValueAnimator;
@@ -51,6 +52,10 @@
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.shared.model.ScreenModel;
+import com.android.systemui.keyguard.shared.model.ScreenState;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -62,6 +67,9 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
+import kotlin.coroutines.CoroutineContext;
+import kotlin.coroutines.EmptyCoroutineContext;
+
import java.io.PrintWriter;
import javax.inject.Inject;
@@ -91,6 +99,7 @@
private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
private final Rect mClipBounds = new Rect();
+ private final KeyguardInteractor mKeyguardInteractor;
private Boolean mStatusViewCentered = true;
@@ -122,6 +131,7 @@
KeyguardLogger logger,
FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
+ KeyguardInteractor keyguardInteractor,
DumpManager dumpManager) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
@@ -134,12 +144,34 @@
mInteractionJankMonitor = interactionJankMonitor;
mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
+ mKeyguardInteractor = keyguardInteractor;
}
@Override
public void onInit() {
mKeyguardClockSwitchController.init();
mDumpManager.registerDumpable(this);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ startCoroutines(EmptyCoroutineContext.INSTANCE);
+ }
+ }
+
+ void startCoroutines(CoroutineContext context) {
+ collectFlow(mView, mKeyguardInteractor.getDozeTimeTick(),
+ (Long millis) -> {
+ dozeTimeTick();
+ }, context);
+
+ collectFlow(mView, mKeyguardInteractor.getScreenModel(),
+ (ScreenModel model) -> {
+ if (model.getState() == ScreenState.SCREEN_TURNING_ON) {
+ dozeTimeTick();
+ }
+ }, context);
+ }
+
+ public KeyguardStatusView getView() {
+ return mView;
}
@Override
@@ -308,6 +340,7 @@
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onTimeChanged() {
+ Slog.v(TAG, "onTimeChanged");
refreshTime();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index a1b15f44..d82f458 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -122,9 +122,7 @@
if (shouldAnimateIconViewForTransition(lastState, newState)) {
iconView.playAnimation()
}
- if (isSideFps) {
- LottieColorUtils.applyDynamicColors(context, iconView)
- }
+ LottieColorUtils.applyDynamicColors(context, iconView)
}
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 34e934b..d9ec5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -120,6 +120,8 @@
viewModel.isShowing.collect { isShowing ->
view.visibility = if (isShowing) View.VISIBLE else View.INVISIBLE
if (isShowing) {
+ // Reset security container because these views are not reinflated.
+ securityContainerController.reset()
securityContainerController.reinflateViewFlipper {
// Reset Security Container entirely.
securityContainerController.onBouncerVisibilityChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 1eba667..83bec66 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -124,8 +124,7 @@
}
private fun updateServices(newServices: List<ControlsServiceInfo>) {
- if (featureFlags.isEnabled(Flags.USE_APP_PANELS) &&
- activityTaskManagerProxy.supportsMultiWindow(context)) {
+ if (activityTaskManagerProxy.supportsMultiWindow(context)) {
val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
newServices.forEach {
it.resolvePanelActivity(allowAllApps) }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 3713811..a7e9efd8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -34,12 +34,9 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
-import com.android.systemui.controls.settings.ControlsSettingsDialogManager
import com.android.systemui.controls.settings.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -60,8 +57,6 @@
private val controlsMetricsLogger: ControlsMetricsLogger,
private val vibrator: VibratorHelper,
private val controlsSettingsRepository: ControlsSettingsRepository,
- private val controlsSettingsDialogManager: ControlsSettingsDialogManager,
- private val featureFlags: FeatureFlags,
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
@@ -77,9 +72,6 @@
}
override fun closeDialogs() {
- if (!featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
- controlsSettingsDialogManager.closeDialog()
- }
val isActivityFinishing =
(activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
if (isActivityFinishing == true) {
@@ -169,7 +161,6 @@
override fun runPendingAction(controlId: String) {
if (isLocked) return
if (pendingAction?.controlId == controlId) {
- showSettingsDialogIfNeeded(pendingAction!!)
pendingAction?.invoke()
pendingAction = null
}
@@ -208,7 +199,6 @@
true
}, { pendingAction = null }, true /* afterKeyguardGone */)
} else {
- showSettingsDialogIfNeeded(action)
action.invoke()
}
}
@@ -243,15 +233,6 @@
}
}
- private fun showSettingsDialogIfNeeded(action: Action) {
- if (action.authIsRequired) {
- return
- }
- if (!featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
- controlsSettingsDialogManager.maybeShowDialog(activityContext) {}
- }
- }
-
@VisibleForTesting
fun createAction(
controlId: String,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 557dcf4..8341964 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -36,7 +36,6 @@
import com.android.systemui.controls.management.ControlsAnimations
import com.android.systemui.controls.settings.ControlsSettingsDialogManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -66,9 +65,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lastConfiguration.setTo(resources.configuration)
- if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
- window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
- }
+ window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
setContentView(R.layout.controls_fullscreen)
@@ -77,7 +74,7 @@
requireViewById(R.id.control_detail_root),
window,
intent,
- !featureFlags.isEnabled(Flags.USE_APP_PANELS)
+ false
)
)
@@ -114,7 +111,7 @@
parent = requireViewById(R.id.control_detail_root)
parent.alpha = 0f
- if (featureFlags.isEnabled(Flags.USE_APP_PANELS) && !keyguardStateController.isUnlocked) {
+ if (!keyguardStateController.isUnlocked) {
controlsSettingsDialogManager.maybeShowDialog(this) {
uiController.show(parent, { finishOrReturnToDream() }, this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 1b9be96..7e4f501 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -283,6 +283,15 @@
// TODO(b/291767565): Tracking bug.
@JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
+ /** Enables preview loading animation in the wallpaper picker. */
+ // TODO(b/274443705): Tracking Bug
+ @JvmField
+ val WALLPAPER_PICKER_PREVIEW_ANIMATION =
+ unreleasedFlag(
+ 244,
+ "wallpaper_picker_preview_animation"
+ )
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -617,8 +626,6 @@
@JvmField val NOTE_TASKS = releasedFlag(1900, "keycode_flag")
// 2000 - device controls
- @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels")
-
@JvmField val APP_PANELS_ALL_APPS_ALLOWED = releasedFlag(2001, "app_panels_all_apps_allowed")
@JvmField
@@ -705,7 +712,7 @@
// TODO(b/285059790) : Tracking Bug
@JvmField
val LOCKSCREEN_WALLPAPER_DREAM_ENABLED =
- unreleasedFlag(3000, name = "enable_lockscreen_wallpaper_dream")
+ unreleasedFlag(3000, name = "enable_lockscreen_wallpaper_dream", teamfood = true)
// TODO(b/283084712): Tracking Bug
@JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 82d0c8b..2686233 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -100,6 +100,7 @@
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -173,6 +174,8 @@
import dagger.Lazy;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@@ -257,6 +260,22 @@
private static final int SYSTEM_READY = 18;
private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19;
+ /** Enum for reasons behind updating wakeAndUnlock state. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ value = {
+ WakeAndUnlockUpdateReason.HIDE,
+ WakeAndUnlockUpdateReason.SHOW,
+ WakeAndUnlockUpdateReason.FULFILL,
+ WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK,
+ })
+ @interface WakeAndUnlockUpdateReason {
+ int HIDE = 0;
+ int SHOW = 1;
+ int FULFILL = 2;
+ int WAKE_AND_UNLOCK = 3;
+ }
+
/**
* The default amount of time we stay awake (used for all key input)
*/
@@ -808,7 +827,7 @@
// dreaming. It's time to wake up.
if (mUnlockingAndWakingFromDream) {
Log.d(TAG, "waking from dream after unlock");
- mUnlockingAndWakingFromDream = false;
+ setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.FULFILL);
if (mKeyguardStateController.isShowing()) {
Log.d(TAG, "keyguard showing after keyguardGone, dismiss");
@@ -2693,7 +2712,7 @@
mKeyguardExitAnimationRunner = null;
mWakeAndUnlocking = false;
- mUnlockingAndWakingFromDream = false;
+ setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.SHOW);
setPendingLock(false);
// Force if we we're showing in the middle of hiding, to ensure we end up in the correct
@@ -2799,6 +2818,51 @@
tryKeyguardDone();
};
+ private void setUnlockAndWakeFromDream(boolean updatedValue,
+ @WakeAndUnlockUpdateReason int reason) {
+ if (updatedValue == mUnlockingAndWakingFromDream) {
+ return;
+ }
+
+ final String reasonDescription;
+
+ switch(reason) {
+ case WakeAndUnlockUpdateReason.FULFILL:
+ reasonDescription = "fulfilling existing request";
+ break;
+ case WakeAndUnlockUpdateReason.HIDE:
+ reasonDescription = "hiding keyguard";
+ break;
+ case WakeAndUnlockUpdateReason.SHOW:
+ reasonDescription = "showing keyguard";
+ break;
+ case WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK:
+ reasonDescription = "waking to unlock";
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " + reason);
+ }
+
+ final boolean unsetUnfulfilled = !updatedValue
+ && reason != WakeAndUnlockUpdateReason.FULFILL;
+
+ mUnlockingAndWakingFromDream = updatedValue;
+
+ final String description;
+
+ if (unsetUnfulfilled) {
+ description = "Interrupting request to wake and unlock";
+ } else if (mUnlockingAndWakingFromDream) {
+ description = "Initiating request to wake and unlock";
+ } else {
+ description = "Fulfilling request to wake and unlock";
+ }
+
+ Log.d(TAG, String.format(
+ "Updating waking and unlocking request to %b. description:[%s]. reason:[%s]",
+ mUnlockingAndWakingFromDream, description, reasonDescription));
+ }
+
/**
* Handle message sent by {@link #hideLocked()}
* @see #HIDE
@@ -2816,8 +2880,11 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
- mUnlockingAndWakingFromDream = mStatusBarStateController.isDreaming()
- && !mStatusBarStateController.isDozing();
+ // If waking and unlocking, waking from dream has been set properly.
+ if (!mWakeAndUnlocking) {
+ setUnlockAndWakeFromDream(mStatusBarStateController.isDreaming()
+ && mPM.isInteractive(), WakeAndUnlockUpdateReason.HIDE);
+ }
if ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream) {
if (mUnlockingAndWakingFromDream) {
@@ -3319,9 +3386,14 @@
}
}
- public void onWakeAndUnlocking() {
+ /**
+ * Informs the keyguard view mediator that the device is waking and unlocking.
+ * @param fromDream Whether waking and unlocking is happening over an interactive dream.
+ */
+ public void onWakeAndUnlocking(boolean fromDream) {
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
+ setUnlockAndWakeFromDream(fromDream, WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK);
mKeyguardViewControllerLazy.get().notifyKeyguardAuthenticated(/* primaryAuth */ false);
userActivity();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 9ee9902..f1b3441 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -47,16 +47,15 @@
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
@@ -177,7 +176,7 @@
val keyguardRootViewVisibility: Flow<KeyguardRootViewVisibilityState>
/** Receive an event for doze time tick */
- val dozeTimeTick: Flow<Unit>
+ val dozeTimeTick: Flow<Long>
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
@@ -248,6 +247,7 @@
private val dreamOverlayCallbackController: DreamOverlayCallbackController,
@Main private val mainDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
+ private val systemClock: SystemClock,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -398,11 +398,11 @@
_isDozing.value = isDozing
}
- private val _dozeTimeTick = MutableSharedFlow<Unit>()
- override val dozeTimeTick = _dozeTimeTick.asSharedFlow()
+ private val _dozeTimeTick = MutableStateFlow<Long>(0)
+ override val dozeTimeTick = _dozeTimeTick.asStateFlow()
override fun dozeTimeTick() {
- _dozeTimeTick.tryEmit(Unit)
+ _dozeTimeTick.value = systemClock.uptimeMillis()
}
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 252982f..ac936b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -24,14 +24,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
@@ -46,24 +43,17 @@
private val burnInHelperWrapper: BurnInHelperWrapper,
@Application private val scope: CoroutineScope,
private val configurationRepository: ConfigurationRepository,
- private val systemClock: SystemClock,
+ private val keyguardInteractor: KeyguardInteractor,
) {
- private val _dozeTimeTick = MutableStateFlow<Long>(0)
- val dozeTimeTick: StateFlow<Long> = _dozeTimeTick.asStateFlow()
-
val udfpsBurnInXOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_x, isXAxis = true)
val udfpsBurnInYOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_y, isXAxis = false)
val udfpsBurnInProgress: StateFlow<Float> =
- dozeTimeTick
+ keyguardInteractor.dozeTimeTick
.mapLatest { burnInHelperWrapper.burnInProgressOffset() }
.stateIn(scope, SharingStarted.Lazily, burnInHelperWrapper.burnInProgressOffset())
- fun dozeTimeTick() {
- _dozeTimeTick.value = systemClock.uptimeMillis()
- }
-
/**
* Use for max burn-in offsets that are NOT specified in pixels. This flow will recalculate the
* max burn-in offset on any configuration changes. If the max burn-in offset is specified in
@@ -77,7 +67,9 @@
.flatMapLatest {
val maxBurnInOffsetPixels =
context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
- dozeTimeTick.mapLatest { calculateOffset(maxBurnInOffsetPixels, isXAxis) }
+ keyguardInteractor.dozeTimeTick.mapLatest {
+ calculateOffset(maxBurnInOffsetPixels, isXAxis)
+ }
}
.stateIn(
scope,
@@ -102,7 +94,9 @@
.flatMapLatest { scale ->
val maxBurnInOffsetPixels =
context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
- dozeTimeTick.mapLatest { calculateOffset(maxBurnInOffsetPixels, isXAxis, scale) }
+ keyguardInteractor.dozeTimeTick.mapLatest {
+ calculateOffset(maxBurnInOffsetPixels, isXAxis, scale)
+ }
}
.stateIn(
scope,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
new file mode 100644
index 0000000..dac6ef5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 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.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.app.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class FromDreamingLockscreenHostedTransitionInteractor
+@Inject
+constructor(
+ override val transitionRepository: KeyguardTransitionRepository,
+ override val transitionInteractor: KeyguardTransitionInteractor,
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+) :
+ TransitionInteractor(
+ fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ ) {
+
+ override fun start() {
+ listenForDreamingLockscreenHostedToLockscreen()
+ listenForDreamingLockscreenHostedToGone()
+ listenForDreamingLockscreenHostedToDozing()
+ listenForDreamingLockscreenHostedToOccluded()
+ listenForDreamingLockscreenHostedToPrimaryBouncer()
+ }
+
+ private fun listenForDreamingLockscreenHostedToLockscreen() {
+ scope.launch {
+ keyguardInteractor.isActiveDreamLockscreenHosted
+ // Add a slight delay to prevent transitioning to lockscreen from happening too soon
+ // as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
+ .onEach { delay(50) }
+ .sample(
+ combine(
+ keyguardInteractor.dozeTransitionModel,
+ transitionInteractor.startedKeyguardTransitionStep,
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect {
+ (isActiveDreamLockscreenHosted, dozeTransitionModel, lastStartedTransition) ->
+ if (
+ !isActiveDreamLockscreenHosted &&
+ DozeStateModel.isDozeOff(dozeTransitionModel.to) &&
+ lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ ) {
+ startTransitionTo(KeyguardState.LOCKSCREEN)
+ }
+ }
+ }
+ }
+
+ private fun listenForDreamingLockscreenHostedToOccluded() {
+ scope.launch {
+ keyguardInteractor.isActiveDreamLockscreenHosted
+ .sample(
+ combine(
+ keyguardInteractor.isKeyguardOccluded,
+ transitionInteractor.startedKeyguardTransitionStep,
+ ::Pair,
+ ),
+ ::toTriple
+ )
+ .collect { (isActiveDreamLockscreenHosted, isOccluded, lastStartedTransition) ->
+ if (
+ isOccluded &&
+ !isActiveDreamLockscreenHosted &&
+ lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ ) {
+ startTransitionTo(KeyguardState.OCCLUDED)
+ }
+ }
+ }
+ }
+
+ private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
+ scope.launch {
+ keyguardInteractor.primaryBouncerShowing
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isBouncerShowing, lastStartedTransitionStep) ->
+ if (
+ isBouncerShowing &&
+ lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ ) {
+ startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ }
+ }
+ }
+ }
+
+ private fun listenForDreamingLockscreenHostedToGone() {
+ scope.launch {
+ keyguardInteractor.biometricUnlockState
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (biometricUnlockState, lastStartedTransitionStep) ->
+ if (
+ lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED &&
+ biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ ) {
+ startTransitionTo(KeyguardState.GONE)
+ }
+ }
+ }
+ }
+
+ private fun listenForDreamingLockscreenHostedToDozing() {
+ scope.launch {
+ combine(
+ keyguardInteractor.dozeTransitionModel,
+ transitionInteractor.startedKeyguardTransitionStep,
+ ::Pair
+ )
+ .collect { (dozeTransitionModel, lastStartedTransitionStep) ->
+ if (
+ dozeTransitionModel.to == DozeStateModel.DOZE &&
+ lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ ) {
+ startTransitionTo(KeyguardState.DOZING)
+ }
+ }
+ }
+ }
+
+ override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
+ return ValueAnimator().apply {
+ interpolator = Interpolators.LINEAR
+ duration =
+ if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
+ else DEFAULT_DURATION.inWholeMilliseconds
+ }
+ }
+
+ companion object {
+ private val DEFAULT_DURATION = 500.milliseconds
+ val TO_LOCKSCREEN_DURATION = 1167.milliseconds
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 98d7434..954ff6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -94,11 +94,16 @@
private fun listenForDreamingToGone() {
scope.launch {
- keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
- if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
- startTransitionTo(KeyguardState.GONE)
+ keyguardInteractor.biometricUnlockState
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (biometricUnlockState, lastStartedTransitionStep) ->
+ if (
+ lastStartedTransitionStep.to == KeyguardState.DREAMING &&
+ biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ ) {
+ startTransitionTo(KeyguardState.GONE)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index f82633f..2b08b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -48,6 +48,7 @@
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
listenForGoneToLockscreen()
+ listenForGoneToDreamingLockscreenHosted()
}
// Primarily for when the user chooses to lock down the device
@@ -63,12 +64,35 @@
}
}
+ private fun listenForGoneToDreamingLockscreenHosted() {
+ scope.launch {
+ keyguardInteractor.isActiveDreamLockscreenHosted
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isActiveDreamLockscreenHosted, lastStartedStep) ->
+ if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) {
+ startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ }
+ }
+ }
+ }
+
private fun listenForGoneToDreaming() {
scope.launch {
keyguardInteractor.isAbleToDream
- .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
- .collect { (isAbleToDream, lastStartedStep) ->
- if (isAbleToDream && lastStartedStep.to == KeyguardState.GONE) {
+ .sample(
+ combine(
+ transitionInteractor.startedKeyguardTransitionStep,
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect { (isAbleToDream, lastStartedStep, isActiveDreamLockscreenHosted) ->
+ if (
+ isAbleToDream &&
+ lastStartedStep.to == KeyguardState.GONE &&
+ !isActiveDreamLockscreenHosted
+ ) {
startTransitionTo(KeyguardState.DREAMING)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index ed1bf3e..6b28b27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -70,17 +70,27 @@
combine(
transitionInteractor.startedKeyguardTransitionStep,
transitionInteractor.finishedKeyguardState,
- ::Pair
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ ::Triple
),
- ::toTriple
+ ::toQuad
)
- .collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) ->
+ .collect {
+ (
+ isAbleToDream,
+ lastStartedTransition,
+ finishedKeyguardState,
+ isActiveDreamLockscreenHosted) ->
val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
val isTransitionInterruptible =
lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
!invalidFromStates.contains(lastStartedTransition.from)
if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) {
- startTransitionTo(KeyguardState.DREAMING)
+ if (isActiveDreamLockscreenHosted) {
+ startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ } else {
+ startTransitionTo(KeyguardState.DREAMING)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index e1754f5..9142d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -27,6 +27,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
+import com.android.systemui.util.kotlin.Utils.Companion.toQuint
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -52,6 +54,7 @@
listenForPrimaryBouncerToGone()
listenForPrimaryBouncerToAodOrDozing()
listenForPrimaryBouncerToLockscreenOrOccluded()
+ listenForPrimaryBouncerToDreamingLockscreenHosted()
}
private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -62,17 +65,24 @@
keyguardInteractor.wakefulnessModel,
transitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
- ::Triple
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ ::toQuad
),
- ::toQuad
+ ::toQuint
)
- .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, occluded)
- ->
+ .collect {
+ (
+ isBouncerShowing,
+ wakefulnessState,
+ lastStartedTransitionStep,
+ occluded,
+ isActiveDreamLockscreenHosted) ->
if (
!isBouncerShowing &&
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
(wakefulnessState.state == WakefulnessState.AWAKE ||
- wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE)
+ wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE) &&
+ !isActiveDreamLockscreenHosted
) {
startTransitionTo(
if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
@@ -111,6 +121,30 @@
}
}
+ private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
+ scope.launch {
+ keyguardInteractor.primaryBouncerShowing
+ .sample(
+ combine(
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ transitionInteractor.startedKeyguardTransitionStep,
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect {
+ (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
+ if (
+ !isBouncerShowing &&
+ isActiveDreamLockscreenHosted &&
+ lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+ ) {
+ startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ }
+ }
+ }
+ }
+
private fun listenForPrimaryBouncerToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index cc15916..53d3c07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -34,9 +34,9 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
+import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -87,7 +87,7 @@
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
/** Receive an event for doze time tick */
- val dozeTimeTick: Flow<Unit> = repository.dozeTimeTick
+ val dozeTimeTick: Flow<Long> = repository.dozeTimeTick
/** Whether Always-on Display mode is available. */
val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
/** Doze transition information. */
@@ -100,7 +100,7 @@
/** Whether the system is dreaming with an overlay active */
val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
/** Whether the system is dreaming and the active dream is hosted in lockscreen */
- val isActiveDreamLockscreenHosted: Flow<Boolean> = repository.isActiveDreamLockscreenHosted
+ val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
/** Event for when the camera gesture is detected */
val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
val callback =
@@ -122,6 +122,9 @@
/** The device wake/sleep state */
val wakefulnessModel: StateFlow<WakefulnessModel> = repository.wakefulness
+ /** The device screen state */
+ val screenModel: StateFlow<ScreenModel> = repository.screenModel
+
/**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence.
@@ -237,8 +240,16 @@
repository.setQuickSettingsVisible(isVisible)
}
- fun setKeyguardRootVisibility(statusBarState: Int, goingToFullShade: Boolean, isOcclusionTransitionRunning: Boolean) {
- repository.setKeyguardVisibility(statusBarState, goingToFullShade, isOcclusionTransitionRunning)
+ fun setKeyguardRootVisibility(
+ statusBarState: Int,
+ goingToFullShade: Boolean,
+ isOcclusionTransitionRunning: Boolean
+ ) {
+ repository.setKeyguardVisibility(
+ statusBarState,
+ goingToFullShade,
+ isOcclusionTransitionRunning
+ )
}
fun setClockPosition(x: Int, y: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index a486843..9c796f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -412,7 +412,11 @@
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_PAGE_TRANSITIONS,
value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PAGE_TRANSITIONS)
- )
+ ),
+ KeyguardPickerFlag(
+ name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_PICKER_PREVIEW_ANIMATION,
+ value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PREVIEW_ANIMATION)
+ ),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index efc1bd0..ba7b987 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -45,6 +45,7 @@
is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
+ is FromDreamingLockscreenHostedTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 45bf20d..8c4c7ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
@@ -89,12 +90,20 @@
val dreamingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING, LOCKSCREEN)
+ /** DREAMING_LOCKSCREEN_HOSTED->LOCKSCREEN transition information. */
+ val dreamingLockscreenHostedToLockscreenTransition: Flow<TransitionStep> =
+ repository.transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)
+
/** GONE->AOD transition information. */
val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
/** GONE->DREAMING transition information. */
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
+ /** GONE->DREAMING_LOCKSCREEN_HOSTED transition information. */
+ val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
+ repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
+
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
@@ -102,6 +111,10 @@
val lockscreenToDreamingTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING)
+ /** LOCKSCREEN->DREAMING_LOCKSCREEN_HOSTED transition information. */
+ val lockscreenToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
+ repository.transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)
+
/** LOCKSCREEN->OCCLUDED transition information. */
val lockscreenToOccludedTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, OCCLUDED)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 4244e55..6115d90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -92,6 +92,7 @@
KeyguardState.DOZING -> false
KeyguardState.AOD -> false
KeyguardState.DREAMING -> true
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
KeyguardState.ALTERNATE_BOUNCER -> true
KeyguardState.PRIMARY_BOUNCER -> true
KeyguardState.LOCKSCREEN -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index d9690b7..56f5529 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -50,6 +50,12 @@
@Binds
@IntoSet
+ abstract fun fromDreamingLockscreenHosted(
+ impl: FromDreamingLockscreenHostedTransitionInteractor
+ ): TransitionInteractor
+
+ @Binds
+ @IntoSet
abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 87b4321..1e20cdb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -35,6 +35,12 @@
* parties to present their own UI over keyguard, like a screensaver.
*/
DREAMING,
+ /*
+ * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
+ * It is a special version of DREAMING state but not DOZING. The active dream will be windowless
+ * and hosted in the lockscreen.
+ */
+ DREAMING_LOCKSCREEN_HOSTED,
/**
* The device has entered a special low-power mode within SystemUI, also called the Always-on
* Display (AOD). A minimal UI is presented to show critical information. If the device is in
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
new file mode 100644
index 0000000..113f01c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down GONE->DREAMING_LOCKSCREEN_HOSTED transition into discrete steps for corresponding
+ * views to consume.
+ */
+@SysUISingleton
+class GoneToDreamingLockscreenHostedTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_DREAMING_DURATION,
+ transitionFlow = interactor.goneToDreamingLockscreenHostedTransition,
+ )
+
+ /** Lockscreen views alpha - hide immediately */
+ val lockscreenAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 1.milliseconds,
+ onStep = { 0f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index b307f1b..dd58607 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -112,6 +112,7 @@
KeyguardState.OFF,
KeyguardState.DOZING,
KeyguardState.DREAMING,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
KeyguardState.AOD,
KeyguardState.PRIMARY_BOUNCER,
KeyguardState.GONE,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 773f35e..2ea63c2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -140,6 +140,7 @@
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
@@ -597,6 +598,9 @@
private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
+ private final GoneToDreamingLockscreenHostedTransitionViewModel
+ mGoneToDreamingLockscreenHostedTransitionViewModel;
+
private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -605,6 +609,7 @@
private final CoroutineDispatcher mMainDispatcher;
private boolean mIsAnyMultiShadeExpanded;
private boolean mIsOcclusionTransitionRunning = false;
+ private boolean mIsGoneToDreamingLockscreenHostedTransitionRunning;
private int mDreamingToLockscreenTransitionTranslationY;
private int mOccludedToLockscreenTransitionTranslationY;
private int mLockscreenToDreamingTransitionTranslationY;
@@ -652,6 +657,25 @@
step.getTransitionState() == TransitionState.RUNNING;
};
+ private final Consumer<TransitionStep> mGoneToDreamingLockscreenHostedTransition =
+ (TransitionStep step) -> {
+ mIsOcclusionTransitionRunning =
+ step.getTransitionState() == TransitionState.RUNNING;
+ mIsGoneToDreamingLockscreenHostedTransitionRunning = mIsOcclusionTransitionRunning;
+ };
+
+ private final Consumer<TransitionStep> mLockscreenToDreamingLockscreenHostedTransition =
+ (TransitionStep step) -> {
+ mIsOcclusionTransitionRunning =
+ step.getTransitionState() == TransitionState.RUNNING;
+ };
+
+ private final Consumer<TransitionStep> mDreamingLockscreenHostedToLockscreenTransition =
+ (TransitionStep step) -> {
+ mIsOcclusionTransitionRunning =
+ step.getTransitionState() == TransitionState.RUNNING;
+ };
+
private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
@@ -734,6 +758,8 @@
OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
+ GoneToDreamingLockscreenHostedTransitionViewModel
+ goneToDreamingLockscreenHostedTransitionViewModel,
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
@Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -761,6 +787,8 @@
mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
+ mGoneToDreamingLockscreenHostedTransitionViewModel =
+ goneToDreamingLockscreenHostedTransitionViewModel;
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mKeyguardInteractor = keyguardInteractor;
@@ -1091,6 +1119,24 @@
mDreamingToLockscreenTransitionTranslationY),
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+ // Gone -> Dreaming hosted in lockscreen
+ collectFlow(mView, mKeyguardTransitionInteractor
+ .getGoneToDreamingLockscreenHostedTransition(),
+ mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
+ collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController),
+ mMainDispatcher);
+
+ // Lockscreen -> Dreaming hosted in lockscreen
+ collectFlow(mView, mKeyguardTransitionInteractor
+ .getLockscreenToDreamingLockscreenHostedTransition(),
+ mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
+
+ // Dreaming hosted in lockscreen -> Lockscreen
+ collectFlow(mView, mKeyguardTransitionInteractor
+ .getDreamingLockscreenHostedToLockscreenTransition(),
+ mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
+
// Occluded->Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
mOccludedToLockscreenTransition, mMainDispatcher);
@@ -1496,13 +1542,18 @@
mAnimateNextPositionUpdate = false;
}
+ private boolean shouldAnimateKeyguardStatusViewAlignment() {
+ // Do not animate when transitioning from Gone->DreamingLockscreenHosted
+ return !mIsGoneToDreamingLockscreenHostedTransitionRunning;
+ }
+
private void updateClockAppearance() {
int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
shouldAnimateClockChange);
- updateKeyguardStatusViewAlignment(/* animate= */true);
+ updateKeyguardStatusViewAlignment(/* animate= */shouldAnimateKeyguardStatusViewAlignment());
int userSwitcherHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
if (mKeyguardUserSwitcherController != null) {
@@ -1625,6 +1676,10 @@
// overlap.
return true;
}
+ if (isActiveDreamLockscreenHosted()) {
+ // Dreaming hosted in lockscreen, no "visible" notifications. Safe to center the clock.
+ return true;
+ }
if (mNotificationListContainer.hasPulsingNotifications()) {
// Pulsing notification appears on the right. Move clock left to avoid overlap.
return false;
@@ -1653,6 +1708,11 @@
return mDozing && mDozeParameters.getAlwaysOn();
}
+
+ private boolean isActiveDreamLockscreenHosted() {
+ return mKeyguardInteractor.isActiveDreamLockscreenHosted().getValue();
+ }
+
private boolean hasVisibleNotifications() {
return mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0
@@ -2820,7 +2880,9 @@
@Override
public void onScreenTurningOn() {
- mKeyguardStatusViewController.dozeTimeTick();
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ mKeyguardStatusViewController.dozeTimeTick();
+ }
}
private void onMiddleClicked() {
@@ -3070,10 +3132,11 @@
}
}
- @Override
public void dozeTimeTick() {
mLockIconViewController.dozeTimeTick();
- mKeyguardStatusViewController.dozeTimeTick();
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ mKeyguardStatusViewController.dozeTimeTick();
+ }
if (mInterpolatedDarkAmount > 0) {
positionClockAndNotifications();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 31d4ab9..ea3a8f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -91,6 +91,7 @@
NotificationSectionHeadersModule.class,
NotificationListViewModelModule.class,
ActivatableNotificationViewModelModule.class,
+ NotificationMemoryModule.class,
})
public interface NotificationsModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 035bdf1..f6ccaa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -471,7 +471,7 @@
};
final boolean wakingFromDream = mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM
- && !mStatusBarStateController.isDozing();
+ && mPowerManager.isInteractive();
if (mMode != MODE_NONE && !wakingFromDream) {
wakeUp.run();
@@ -509,7 +509,7 @@
// later to awaken.
}
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
- mKeyguardViewMediator.onWakeAndUnlocking();
+ mKeyguardViewMediator.onWakeAndUnlocking(wakingFromDream);
Trace.endSection();
break;
case MODE_ONLY_WAKE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index ed9722e..801cdbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -38,7 +38,6 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeViewController;
@@ -94,7 +93,6 @@
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final AuthController mAuthController;
private final NotificationIconAreaController mNotificationIconAreaController;
- private final BurnInInteractor mBurnInInteractor;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private ShadeViewController mNotificationPanel;
private View mAmbientIndicationContainer;
@@ -118,8 +116,7 @@
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
NotificationIconAreaController notificationIconAreaController,
- DozeInteractor dozeInteractor,
- BurnInInteractor burnInInteractor) {
+ DozeInteractor dozeInteractor) {
super();
mDozeLog = dozeLog;
mPowerManager = powerManager;
@@ -138,7 +135,6 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
- mBurnInInteractor = burnInInteractor;
mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener);
mDozeInteractor = dozeInteractor;
}
@@ -317,7 +313,6 @@
if (mAmbientIndicationContainer instanceof DozeReceiver) {
((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
}
- mBurnInInteractor.dozeTimeTick();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index 06cc96e..d696979 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -46,6 +46,7 @@
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
+ setClipChildren(false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index d39a53d..53f4837 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -44,6 +44,7 @@
import android.media.session.MediaSession.Token;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
@@ -57,6 +58,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
+import androidx.annotation.NonNull;
import androidx.lifecycle.Observer;
import com.android.internal.annotations.GuardedBy;
@@ -65,6 +67,7 @@
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController;
@@ -131,7 +134,7 @@
private final Receiver mReceiver = new Receiver();
private final RingerModeObservers mRingerModeObservers;
private final MediaSessions mMediaSessions;
- private final CaptioningManager mCaptioningManager;
+ private CaptioningManager mCaptioningManager;
private final KeyguardManager mKeyguardManager;
private final ActivityManager mActivityManager;
private final UserTracker mUserTracker;
@@ -179,11 +182,11 @@
AccessibilityManager accessibilityManager,
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle,
- CaptioningManager captioningManager,
KeyguardManager keyguardManager,
ActivityManager activityManager,
UserTracker userTracker,
- DumpManager dumpManager
+ DumpManager dumpManager,
+ @Main Handler mainHandler
) {
mContext = context.getApplicationContext();
mPackageManager = packageManager;
@@ -209,10 +212,12 @@
mVibrator = vibrator;
mHasVibrator = mVibrator.hasVibrator();
mAudioService = iAudioService;
- mCaptioningManager = captioningManager;
mKeyguardManager = keyguardManager;
mActivityManager = activityManager;
mUserTracker = userTracker;
+ mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mainHandler));
+ createCaptioningManagerServiceByUserContext(mUserTracker.getUserContext());
+
dumpManager.registerDumpable("VolumeDialogControllerImpl", this);
boolean accessibilityVolumeStreamActive = accessibilityManager
@@ -316,6 +321,25 @@
mWorker.sendEmptyMessage(W.GET_STATE);
}
+ /**
+ * We met issues about the wrong state of System Caption in multi-user mode.
+ * It happened in the usage of CaptioningManager Service from SysUI process
+ * that is a global system process of User 0.
+ * Therefore, we have to add callback on UserTracker that allows us to get the Context of
+ * active User and then get the corresponding CaptioningManager Service for further usages.
+ */
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ createCaptioningManagerServiceByUserContext(userContext);
+ }
+ };
+
+ private void createCaptioningManagerServiceByUserContext(@NonNull Context userContext) {
+ mCaptioningManager = userContext.getSystemService(CaptioningManager.class);
+ }
+
public boolean areCaptionsEnabled() {
return mCaptioningManager.isSystemAudioCaptioningEnabled();
}
@@ -719,7 +743,7 @@
* This method will never be called if the CSD (Computed Sound Dose) feature is
* not enabled. See com.android.android.server.audio.SoundDoseHelper for the state of
* the feature.
- * @param warning the type of warning to display, values are one of
+ * @param csdWarning the type of warning to display, values are one of
* {@link android.media.AudioManager#CSD_WARNING_DOSE_REACHED_1X},
* {@link android.media.AudioManager#CSD_WARNING_DOSE_REPEATED_5X},
* {@link android.media.AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE},
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
new file mode 100644
index 0000000..ba3dbf0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
+
+ @Mock protected KeyguardStatusView mKeyguardStatusView;
+ @Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
+ @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
+ @Mock protected KeyguardStateController mKeyguardStateController;
+ @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock protected ConfigurationController mConfigurationController;
+ @Mock protected DozeParameters mDozeParameters;
+ @Mock protected ScreenOffAnimationController mScreenOffAnimationController;
+ @Mock protected KeyguardLogger mKeyguardLogger;
+ @Mock protected KeyguardStatusViewController mControllerMock;
+ @Mock protected FeatureFlags mFeatureFlags;
+ @Mock protected InteractionJankMonitor mInteractionJankMonitor;
+ @Mock protected ViewTreeObserver mViewTreeObserver;
+ @Mock protected DumpManager mDumpManager;
+ protected FakeKeyguardRepository mFakeKeyguardRepository;
+
+ protected KeyguardStatusViewController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ KeyguardInteractorFactory.WithDependencies deps = KeyguardInteractorFactory.create();
+ mFakeKeyguardRepository = deps.getRepository();
+
+ mController = new KeyguardStatusViewController(
+ mKeyguardStatusView,
+ mKeyguardSliceViewController,
+ mKeyguardClockSwitchController,
+ mKeyguardStateController,
+ mKeyguardUpdateMonitor,
+ mConfigurationController,
+ mDozeParameters,
+ mScreenOffAnimationController,
+ mKeyguardLogger,
+ mFeatureFlags,
+ mInteractionJankMonitor,
+ deps.getKeyguardInteractor(),
+ mDumpManager) {
+ @Override
+ void setProperty(
+ AnimatableProperty property,
+ float value,
+ boolean animate) {
+ // Route into the mock version for verification
+ mControllerMock.setProperty(property, value, animate);
+ }
+ };
+
+ when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+ }
+
+ protected void givenViewAttached() {
+ ArgumentCaptor<View.OnAttachStateChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ verify(mKeyguardStatusView, atLeast(1)).addOnAttachStateChangeListener(captor.capture());
+
+ for (View.OnAttachStateChangeListener listener : captor.getAllValues()) {
+ listener.onViewAttachedToWindow(mKeyguardStatusView);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 7114c22..20d9ef1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -23,80 +23,21 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ClockConfig;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner.class)
-public class KeyguardStatusViewControllerTest extends SysuiTestCase {
-
- @Mock private KeyguardStatusView mKeyguardStatusView;
- @Mock private KeyguardSliceViewController mKeyguardSliceViewController;
- @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController;
- @Mock private KeyguardStateController mKeyguardStateController;
- @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock private ConfigurationController mConfigurationController;
- @Mock private DozeParameters mDozeParameters;
- @Mock private ScreenOffAnimationController mScreenOffAnimationController;
- @Mock private KeyguardLogger mKeyguardLogger;
- @Mock private KeyguardStatusViewController mControllerMock;
- @Mock private FeatureFlags mFeatureFlags;
- @Mock private InteractionJankMonitor mInteractionJankMonitor;
-
- @Mock private DumpManager mDumpManager;
-
- @Captor
- private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
-
- private KeyguardStatusViewController mController;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mController = new KeyguardStatusViewController(
- mKeyguardStatusView,
- mKeyguardSliceViewController,
- mKeyguardClockSwitchController,
- mKeyguardStateController,
- mKeyguardUpdateMonitor,
- mConfigurationController,
- mDozeParameters,
- mScreenOffAnimationController,
- mKeyguardLogger,
- mFeatureFlags,
- mInteractionJankMonitor,
- mDumpManager) {
- @Override
- void setProperty(
- AnimatableProperty property,
- float value,
- boolean animate) {
- // Route into the mock version for verification
- mControllerMock.setProperty(property, value, animate);
- }
- };
- }
+public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControllerBaseTest {
@Test
public void dozeTimeTick_updatesSlice() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
new file mode 100644
index 0000000..2b9797e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 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.keyguard
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class KeyguardStatusViewControllerWithCoroutinesTest : KeyguardStatusViewControllerBaseTest() {
+
+ @Test
+ fun dozeTimeTickUpdatesSlices() = runTest {
+ mController.startCoroutines(coroutineContext)
+ givenViewAttached()
+ runCurrent()
+ clearInvocations(mKeyguardSliceViewController)
+
+ mFakeKeyguardRepository.dozeTimeTick()
+ runCurrent()
+ verify(mKeyguardSliceViewController).refresh()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun onScreenTurningOnUpdatesSlices() = runTest {
+ mController.startCoroutines(coroutineContext)
+ givenViewAttached()
+ runCurrent()
+ clearInvocations(mKeyguardSliceViewController)
+
+ mFakeKeyguardRepository.setScreenModel(ScreenModel(ScreenState.SCREEN_ON))
+ runCurrent()
+ verify(mKeyguardSliceViewController, never()).refresh()
+
+ // Should only be called during a 'turning on' event
+ mFakeKeyguardRepository.setScreenModel(ScreenModel(ScreenState.SCREEN_TURNING_ON))
+ runCurrent()
+ verify(mKeyguardSliceViewController).refresh()
+
+ coroutineContext.cancelChildren()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 38372a3..692d794 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -24,7 +24,6 @@
import com.android.systemui.controls.settings.ControlsSettingsDialogManager
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -36,7 +35,6 @@
import org.mockito.Answers
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
@@ -44,6 +42,7 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import java.util.Optional
@@ -102,14 +101,11 @@
metricsLogger,
vibratorHelper,
controlsSettingsRepository,
- controlsSettingsDialogManager,
- featureFlags
))
coordinator.activityContext = mContext
`when`(cvh.cws.ci.controlId).thenReturn(ID)
`when`(cvh.cws.control?.isAuthRequired()).thenReturn(true)
- `when`(featureFlags.isEnabled(Flags.USE_APP_PANELS)).thenReturn(false)
action = spy(coordinator.Action(ID, {}, false, true))
doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean())
@@ -155,13 +151,11 @@
coordinator.toggle(cvh, "", true)
verify(coordinator).bouncerOrRun(action)
- verify(controlsSettingsDialogManager).maybeShowDialog(any(), any())
verify(action).invoke()
}
@Test
fun testToggleWhenLockedDoesNotTriggerDialog_featureFlagEnabled() {
- `when`(featureFlags.isEnabled(Flags.USE_APP_PANELS)).thenReturn(true)
action = spy(coordinator.Action(ID, {}, false, false))
doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index ee213f7..b1061ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -37,7 +37,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.APP_PANELS_ALL_APPS_ALLOWED
-import com.android.systemui.flags.Flags.USE_APP_PANELS
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.ActivityTaskManagerProxy
import com.android.systemui.util.concurrency.FakeExecutor
@@ -124,8 +123,6 @@
arrayOf(componentName.packageName)
)
- // Return true by default, we'll test the false path
- `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(true)
// Return false by default, we'll test the true path
`when`(featureFlags.isEnabled(APP_PANELS_ALL_APPS_ALLOWED)).thenReturn(false)
@@ -445,34 +442,6 @@
}
@Test
- fun testActivityDefaultEnabled_flagDisabled_nullPanel() {
- `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(false)
- val serviceInfo = ServiceInfo(
- componentName,
- activityName,
- )
-
- `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
- .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-
- setUpQueryResult(listOf(
- ActivityInfo(
- activityName,
- enabled = true,
- exported = true,
- permission = Manifest.permission.BIND_CONTROLS
- )
- ))
-
- val list = listOf(serviceInfo)
- serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
- executor.runAllReady()
-
- assertNull(controller.getCurrentServices()[0].panelActivity)
- }
-
- @Test
fun testActivityDifferentPackage_nullPanel() {
val serviceInfo = ServiceInfo(
componentName,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1f66e5b..28328c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -316,7 +316,7 @@
TestableLooper.get(this).processAllMessages();
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
- mViewMediator.onWakeAndUnlocking();
+ mViewMediator.onWakeAndUnlocking(false);
mViewMediator.onStartedWakingUp(OFF_BECAUSE_OF_USER, false);
TestableLooper.get(this).processAllMessages();
@@ -378,6 +378,8 @@
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
+ when(mPowerManager.isInteractive()).thenReturn(true);
+
// Given device is dreaming
when(mUpdateMonitor.isDreaming()).thenReturn(true);
@@ -717,14 +719,14 @@
@Test
public void testWakeAndUnlocking() {
- mViewMediator.onWakeAndUnlocking();
+ mViewMediator.onWakeAndUnlocking(false);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
}
@Test
public void testWakeAndUnlockingOverDream() {
// Send signal to wake
- mViewMediator.onWakeAndUnlocking();
+ mViewMediator.onWakeAndUnlocking(true);
// Ensure not woken up yet
verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
@@ -753,7 +755,7 @@
@Test
public void testWakeAndUnlockingOverDream_signalAuthenticateIfStillShowing() {
// Send signal to wake
- mViewMediator.onWakeAndUnlocking();
+ mViewMediator.onWakeAndUnlocking(true);
// Ensure not woken up yet
verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
@@ -783,6 +785,35 @@
}
@Test
+ public void testWakeAndUnlockingOverNonInteractiveDream_noWakeByKeyguardViewMediator() {
+ // Send signal to wake
+ mViewMediator.onWakeAndUnlocking(false);
+
+ // Ensure not woken up yet
+ verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+
+ // Verify keyguard told of authentication
+ verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(true,
+ mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
+ final ArgumentCaptor<Runnable> animationRunnableCaptor =
+ ArgumentCaptor.forClass(Runnable.class);
+ verify(mStatusBarKeyguardViewManager).startPreHideAnimation(
+ animationRunnableCaptor.capture());
+
+ when(mStatusBarStateController.isDreaming()).thenReturn(true);
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ animationRunnableCaptor.getValue().run();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ mViewMediator.mViewMediatorCallback.keyguardGone();
+
+ // Verify not woken up.
+ verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+ }
+
+ @Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testDoKeyguardWhileInteractive_resets() {
mViewMediator.setShowingLocked(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index ba7d349..5e3376a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -49,6 +49,7 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -85,13 +86,14 @@
private val mainDispatcher = StandardTestDispatcher()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
+ private lateinit var systemClock: FakeSystemClock
private lateinit var underTest: KeyguardRepositoryImpl
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
+ systemClock = FakeSystemClock()
underTest =
KeyguardRepositoryImpl(
statusBarStateController,
@@ -107,6 +109,7 @@
dreamOverlayCallbackController,
mainDispatcher,
testScope.backgroundScope,
+ systemClock,
)
}
@@ -167,11 +170,15 @@
@Test
fun dozeTimeTick() =
testScope.runTest {
- var dozeTimeTickValue = collectLastValue(underTest.dozeTimeTick)
- underTest.dozeTimeTick()
- runCurrent()
+ val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
+ assertThat(lastDozeTimeTick).isEqualTo(0L)
- assertThat(dozeTimeTickValue()).isNull()
+ // WHEN dozeTimeTick updated
+ systemClock.setUptimeMillis(systemClock.uptimeMillis() + 5)
+ underTest.dozeTimeTick()
+
+ // THEN listeners were updated to the latest uptime millis
+ assertThat(systemClock.uptimeMillis()).isEqualTo(lastDozeTimeTick)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index 069a486..6308269 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -23,10 +23,9 @@
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -48,7 +47,8 @@
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
private lateinit var configurationRepository: FakeConfigurationRepository
- private lateinit var systemClock: FakeSystemClock
+ private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
private lateinit var testScope: TestScope
private lateinit var underTest: BurnInInteractor
@@ -56,8 +56,10 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
configurationRepository = FakeConfigurationRepository()
- systemClock = FakeSystemClock()
-
+ KeyguardInteractorFactory.create().let {
+ keyguardInteractor = it.keyguardInteractor
+ fakeKeyguardRepository = it.repository
+ }
whenever(burnInHelperWrapper.burnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
setBurnInProgress(.65f)
@@ -68,26 +70,11 @@
burnInHelperWrapper,
testScope.backgroundScope,
configurationRepository,
- systemClock,
+ keyguardInteractor,
)
}
@Test
- fun dozeTimeTick_updatesOnDozeTimeTick() =
- testScope.runTest {
- // Initial state set to 0
- val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
- assertEquals(0L, lastDozeTimeTick)
-
- // WHEN dozeTimeTick updated
- incrementUptimeMillis()
- underTest.dozeTimeTick()
-
- // THEN listeners were updated to the latest uptime millis
- assertThat(systemClock.uptimeMillis()).isEqualTo(lastDozeTimeTick)
- }
-
- @Test
fun udfpsBurnInOffset_updatesOnResolutionScaleChange() =
testScope.runTest {
val udfpsBurnInOffsetX by collectLastValue(underTest.udfpsBurnInXOffset)
@@ -111,25 +98,18 @@
assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
setBurnInProgress(.88f)
- incrementUptimeMillis()
- underTest.dozeTimeTick()
+ fakeKeyguardRepository.dozeTimeTick(10)
assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
setBurnInProgress(.92f)
- incrementUptimeMillis()
- underTest.dozeTimeTick()
+ fakeKeyguardRepository.dozeTimeTick(20)
assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
setBurnInProgress(.32f)
- incrementUptimeMillis()
- underTest.dozeTimeTick()
+ fakeKeyguardRepository.dozeTimeTick(30)
assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
}
- private fun incrementUptimeMillis() {
- systemClock.setUptimeMillis(systemClock.uptimeMillis() + 5)
- }
-
private fun setBurnInProgress(progress: Float) {
burnInProgress = progress
whenever(burnInHelperWrapper.burnInProgressOffset()).thenReturn(burnInProgress)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index d01a46e..daf5ce6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -89,6 +89,8 @@
FromAlternateBouncerTransitionInteractor
private lateinit var fromPrimaryBouncerTransitionInteractor:
FromPrimaryBouncerTransitionInteractor
+ private lateinit var fromDreamingLockscreenHostedTransitionInteractor:
+ FromDreamingLockscreenHostedTransitionInteractor
@Before
fun setUp() {
@@ -140,6 +142,15 @@
)
.apply { start() }
+ fromDreamingLockscreenHostedTransitionInteractor =
+ FromDreamingLockscreenHostedTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
@@ -299,6 +310,38 @@
}
@Test
+ fun lockscreenToDreamingLockscreenHosted() =
+ testScope.runTest {
+ // GIVEN a device that is not dreaming or dozing
+ keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
+
+ // WHEN the device begins to dream and the dream is lockscreen hosted
+ keyguardRepository.setDreamingWithOverlay(true)
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ advanceUntilIdle()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
+ assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun lockscreenToDozing() =
testScope.runTest {
// GIVEN a device with AOD not available
@@ -353,6 +396,149 @@
}
@Test
+ fun dreamingLockscreenHostedToLockscreen() =
+ testScope.runTest {
+ // GIVEN a device dreaming with the lockscreen hosted dream and not dozing
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+ runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+ // WHEN the lockscreen hosted dream stops
+ keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+ advanceUntilIdle()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to Lockscreen should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun dreamingLockscreenHostedToGone() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+ runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+ // WHEN biometrics succeeds with wake and unlock from dream mode
+ keyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ )
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to Gone should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.to).isEqualTo(KeyguardState.GONE)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun dreamingLockscreenHostedToPrimaryBouncer() =
+ testScope.runTest {
+ // GIVEN a device dreaming with lockscreen hosted dream and not dozing
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ runCurrent()
+
+ // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+ runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+ // WHEN the primary bouncer is set to show
+ bouncerRepository.setPrimaryShow(true)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to PRIMARY_BOUNCER should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun dreamingLockscreenHostedToDozing() =
+ testScope.runTest {
+ // GIVEN a device is dreaming with lockscreen hosted dream
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ runCurrent()
+
+ // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+ runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+ // WHEN the device begins to sleep
+ keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.INITIALIZED, to = DozeStateModel.DOZE)
+ )
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to DOZING should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun dreamingLockscreenHostedToOccluded() =
+ testScope.runTest {
+ // GIVEN device is dreaming with lockscreen hosted dream and not occluded
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+ runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+ // WHEN the keyguard is occluded and the lockscreen hosted dream stops
+ keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to OCCLUDED should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun dozingToLockscreen() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
@@ -533,6 +719,38 @@
}
@Test
+ fun goneToDreamingLockscreenHosted() =
+ testScope.runTest {
+ // GIVEN a device that is not dreaming or dozing
+ keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to GONE
+ runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+ // WHEN the device begins to dream with the lockscreen hosted dream
+ keyguardRepository.setDreamingWithOverlay(true)
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ advanceUntilIdle()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
+ assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.GONE)
+ assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun alternateBouncerToPrimaryBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -726,6 +944,34 @@
}
@Test
+ fun primaryBouncerToDreamingLockscreenHosted() =
+ testScope.runTest {
+ // GIVEN device dreaming with the lockscreen hosted dream and not dozing
+ keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ keyguardRepository.setWakefulnessModel(startingToWake())
+
+ // GIVEN a prior transition has run to PRIMARY_BOUNCER
+ bouncerRepository.setPrimaryShow(true)
+ runTransition(KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.PRIMARY_BOUNCER)
+
+ // WHEN the primary bouncer stops showing and lockscreen hosted dream still active
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition back to DREAMING_LOCKSCREEN_HOSTED should occur
+ assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+ assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun occludedToGone() =
testScope.runTest {
// GIVEN a device on lockscreen
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index b019a21..6efec99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -38,7 +38,6 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -68,6 +67,7 @@
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var burnInInteractor: BurnInInteractor
private lateinit var shadeRepository: FakeShadeRepository
+ private lateinit var keyguardInteractor: KeyguardInteractor
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
@Mock private lateinit var dialogManager: SystemUIDialogManager
@@ -79,35 +79,32 @@
MockitoAnnotations.initMocks(this)
testScope = TestScope()
configRepository = FakeConfigurationRepository()
- keyguardRepository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
- shadeRepository = FakeShadeRepository()
- fakeCommandQueue = FakeCommandQueue()
featureFlags =
FakeFeatureFlags().apply {
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
set(Flags.FACE_AUTH_REFACTOR, false)
}
+ KeyguardInteractorFactory.create(featureFlags = featureFlags).let {
+ keyguardInteractor = it.keyguardInteractor
+ keyguardRepository = it.repository
+ }
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ shadeRepository = FakeShadeRepository()
+ fakeCommandQueue = FakeCommandQueue()
burnInInteractor =
BurnInInteractor(
context,
burnInHelper,
testScope.backgroundScope,
configRepository,
- FakeSystemClock(),
+ keyguardInteractor
)
underTest =
UdfpsKeyguardInteractor(
configRepository,
burnInInteractor,
- KeyguardInteractor(
- keyguardRepository,
- fakeCommandQueue,
- featureFlags,
- bouncerRepository,
- configRepository,
- ),
+ keyguardInteractor,
shadeRepository,
dialogManager,
)
@@ -215,7 +212,7 @@
private fun setAwake() {
keyguardRepository.setDozeAmount(0f)
- burnInInteractor.dozeTimeTick()
+ keyguardRepository.dozeTimeTick()
bouncerRepository.setAlternateVisible(false)
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index b985b3c..bd17de3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -20,21 +20,19 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -58,9 +56,9 @@
private lateinit var configRepository: FakeConfigurationRepository
private lateinit var bouncerRepository: KeyguardBouncerRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var shadeRepository: FakeShadeRepository
+ private lateinit var keyguardInteractor: KeyguardInteractor
@Mock private lateinit var dialogManager: SystemUIDialogManager
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
@@ -70,17 +68,21 @@
MockitoAnnotations.initMocks(this)
overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
testScope = TestScope()
- configRepository = FakeConfigurationRepository()
- keyguardRepository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
- fakeCommandQueue = FakeCommandQueue()
shadeRepository = FakeShadeRepository()
featureFlags =
FakeFeatureFlags().apply {
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
set(Flags.FACE_AUTH_REFACTOR, false)
}
-
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .also {
+ keyguardInteractor = it.keyguardInteractor
+ keyguardRepository = it.repository
+ configRepository = it.configurationRepository
+ bouncerRepository = it.bouncerRepository
+ }
val udfpsKeyguardInteractor =
UdfpsKeyguardInteractor(
configRepository,
@@ -89,15 +91,9 @@
burnInHelper,
testScope.backgroundScope,
configRepository,
- FakeSystemClock(),
+ keyguardInteractor,
),
- KeyguardInteractor(
- keyguardRepository,
- fakeCommandQueue,
- featureFlags,
- bouncerRepository,
- configRepository,
- ),
+ keyguardInteractor,
shadeRepository,
dialogManager,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index 0fbcec2..80ab418 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -30,12 +30,11 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -90,14 +89,10 @@
testScope.backgroundScope,
)
val keyguardInteractor =
- KeyguardInteractor(
- keyguardRepository,
- fakeCommandQueue,
- featureFlags,
- bouncerRepository,
- configRepository,
- )
-
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .keyguardInteractor
underTest =
FingerprintViewModel(
context,
@@ -109,7 +104,7 @@
burnInHelper,
testScope.backgroundScope,
configRepository,
- FakeSystemClock(),
+ keyguardInteractor,
),
keyguardInteractor,
shadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index 41ae931..0456824 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -39,7 +40,6 @@
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.time.FakeSystemClock
import com.android.wm.shell.animation.Interpolators
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
@@ -72,6 +72,7 @@
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
private lateinit var configRepository: FakeConfigurationRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var keyguardInteractor: KeyguardInteractor
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var shadeRepository: FakeShadeRepository
private lateinit var featureFlags: FakeFeatureFlags
@@ -81,23 +82,21 @@
MockitoAnnotations.initMocks(this)
testScope = TestScope()
transitionRepository = FakeKeyguardTransitionRepository()
- configRepository = FakeConfigurationRepository()
- keyguardRepository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
featureFlags =
FakeFeatureFlags().apply {
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
set(Flags.FACE_AUTH_REFACTOR, false)
}
- val keyguardInteractor =
- KeyguardInteractor(
- keyguardRepository,
- commandQueue = mock(),
- featureFlags,
- bouncerRepository,
- configRepository,
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
)
+ .also {
+ keyguardInteractor = it.keyguardInteractor
+ keyguardRepository = it.repository
+ configRepository = it.configurationRepository
+ bouncerRepository = it.bouncerRepository
+ }
underTest =
UdfpsLockscreenViewModel(
@@ -115,7 +114,7 @@
burnInHelperWrapper = mock(),
testScope.backgroundScope,
configRepository,
- FakeSystemClock(),
+ keyguardInteractor,
),
keyguardInteractor,
shadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 47ca49d0..9bcc8aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -99,6 +99,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
@@ -297,7 +298,8 @@
@Mock protected LockscreenToOccludedTransitionViewModel
mLockscreenToOccludedTransitionViewModel;
@Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
-
+ @Mock protected GoneToDreamingLockscreenHostedTransitionViewModel
+ mGoneToDreamingLockscreenHostedTransitionViewModel;
@Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
@Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -371,6 +373,7 @@
mKeyguardLogger,
mFeatureFlags,
mInteractionJankMonitor,
+ mKeyguardInteractor,
mDumpManager));
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
@@ -477,6 +480,20 @@
when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
.thenReturn(emptyFlow());
+ // Gone->Dreaming lockscreen hosted
+ when(mKeyguardTransitionInteractor.getGoneToDreamingLockscreenHostedTransition())
+ .thenReturn(emptyFlow());
+ when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha())
+ .thenReturn(emptyFlow());
+
+ // Dreaming lockscreen hosted->Lockscreen
+ when(mKeyguardTransitionInteractor.getDreamingLockscreenHostedToLockscreenTransition())
+ .thenReturn(emptyFlow());
+
+ // Lockscreen->Dreaming lockscreen hosted
+ when(mKeyguardTransitionInteractor.getLockscreenToDreamingLockscreenHostedTransition())
+ .thenReturn(emptyFlow());
+
// Lockscreen->Occluded
when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition())
.thenReturn(emptyFlow());
@@ -612,6 +629,7 @@
mOccludedToLockscreenTransitionViewModel,
mLockscreenToDreamingTransitionViewModel,
mGoneToDreamingTransitionViewModel,
+ mGoneToDreamingLockscreenHostedTransitionViewModel,
mLockscreenToOccludedTransitionViewModel,
mMainDispatcher,
mKeyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 91aa138..481f7f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -199,7 +199,7 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mKeyguardViewMediator).onWakeAndUnlocking();
+ verify(mKeyguardViewMediator).onWakeAndUnlocking(false);
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
@@ -217,7 +217,7 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mKeyguardViewMediator).onWakeAndUnlocking();
+ verify(mKeyguardViewMediator).onWakeAndUnlocking(false);
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(MODE_WAKE_AND_UNLOCK);
}
@@ -671,8 +671,9 @@
when(mWakefulnessLifecycle.getLastWakeReason())
.thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
givenDreamingLocked();
+ when(mPowerManager.isInteractive()).thenReturn(true);
mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, true);
- verify(mKeyguardViewMediator).onWakeAndUnlocking();
+ verify(mKeyguardViewMediator).onWakeAndUnlocking(true);
// Ensure that the power hasn't been told to wake up yet.
verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 57037e0..ff6f40d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -43,7 +43,6 @@
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeViewController;
@@ -96,7 +95,6 @@
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private AuthController mAuthController;
@Mock private DozeHost.Callback mCallback;
- @Mock private BurnInInteractor mBurnInInteractor;
@Mock private DozeInteractor mDozeInteractor;
@Before
@@ -108,8 +106,7 @@
() -> mAssistManager, mDozeScrimController,
mKeyguardUpdateMonitor, mPulseExpansionHandler,
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
- mAuthController, mNotificationIconAreaController, mDozeInteractor,
- mBurnInInteractor);
+ mAuthController, mNotificationIconAreaController, mDozeInteractor);
mDozeServiceHost.initialize(
mCentralSurfaces,
@@ -234,11 +231,11 @@
verifyZeroInteractions(mDozeInteractor);
}
@Test
- public void dozeTimeTickSentTBurnInInteractor() {
+ public void dozeTimeTickSentToDozeInteractor() {
// WHEN dozeTimeTick
mDozeServiceHost.dozeTimeTick();
- // THEN burnInInteractor's dozeTimeTick is updated
- verify(mBurnInInteractor).dozeTimeTick();
+ // THEN dozeInteractor's dozeTimeTick is updated
+ verify(mDozeInteractor).dozeTimeTick();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 0663004..462fd0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -40,7 +40,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.CaptioningManager;
import androidx.test.filters.SmallTest;
@@ -64,6 +63,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.Executor;
+
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper
@@ -96,8 +97,6 @@
@Mock
private WakefulnessLifecycle mWakefullnessLifcycle;
@Mock
- private CaptioningManager mCaptioningManager;
- @Mock
private KeyguardManager mKeyguardManager;
@Mock
private ActivityManager mActivityManager;
@@ -105,6 +104,8 @@
private UserTracker mUserTracker;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private Handler mHandler;
@Before
@@ -117,6 +118,7 @@
when(mRingerModeLiveData.getValue()).thenReturn(-1);
when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+ when(mUserTracker.getUserContext()).thenReturn(mContext);
// Enable group volume adjustments
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions,
@@ -127,8 +129,8 @@
mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
- mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mKeyguardManager,
- mActivityManager, mUserTracker, mDumpManager, mCallback);
+ mPackageManager, mWakefullnessLifcycle, mKeyguardManager,
+ mActivityManager, mUserTracker, mDumpManager, mHandler, mCallback);
mVolumeController.setEnableDialogs(true, true);
}
@@ -219,6 +221,11 @@
verify(mRingerModeInternalLiveData).observeForever(any());
}
+ @Test
+ public void testAddCallbackWithUserTracker() {
+ verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
+ }
+
static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver;
@@ -234,16 +241,16 @@
AccessibilityManager accessibilityManager,
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle,
- CaptioningManager captioningManager,
KeyguardManager keyguardManager,
ActivityManager activityManager,
UserTracker userTracker,
DumpManager dumpManager,
+ Handler mainHandler,
C callback) {
super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager,
notificationManager, optionalVibrator, iAudioService, accessibilityManager,
- packageManager, wakefulnessLifecycle, captioningManager, keyguardManager,
- activityManager, userTracker, dumpManager);
+ packageManager, wakefulnessLifecycle, keyguardManager,
+ activityManager, userTracker, dumpManager, mainHandler);
mCallbacks = callback;
ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index e6894d7..15ce055 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -30,7 +30,6 @@
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -60,7 +59,7 @@
private val _isDozing = MutableStateFlow(false)
override val isDozing: StateFlow<Boolean> = _isDozing
- private val _dozeTimeTick = MutableSharedFlow<Unit>()
+ private val _dozeTimeTick = MutableStateFlow<Long>(0L)
override val dozeTimeTick = _dozeTimeTick
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
@@ -174,7 +173,11 @@
}
override fun dozeTimeTick() {
- _dozeTimeTick.tryEmit(Unit)
+ _dozeTimeTick.value = _dozeTimeTick.value + 1
+ }
+
+ fun dozeTimeTick(millis: Long) {
+ _dozeTimeTick.value = millis
}
override fun setLastDozeTapToWakePosition(position: Point) {
@@ -237,6 +240,10 @@
_isBypassEnabled = isEnabled
}
+ fun setScreenModel(screenModel: ScreenModel) {
+ _screenModel.value = screenModel
+ }
+
override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index d33d224..805f6e3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -110,6 +110,7 @@
private boolean mAlwaysOnMagnificationEnabled = false;
private final DisplayManagerInternal mDisplayManagerInternal;
+ private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag;
@NonNull private final Supplier<MagnificationThumbnail> mThumbnailSupplier;
/**
@@ -689,6 +690,13 @@
}
}
+ void onThumbnailFeatureFlagChanged() {
+ synchronized (mLock) {
+ destroyThumbnail();
+ createThumbnailIfSupported();
+ }
+ }
+
/**
* Updates the current magnification spec.
*
@@ -849,19 +857,43 @@
addInfoChangedCallback(magnificationInfoChangedCallback);
mScaleProvider = scaleProvider;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mMagnificationThumbnailFeatureFlag = new MagnificationThumbnailFeatureFlag();
+ mMagnificationThumbnailFeatureFlag.addOnChangedListener(
+ backgroundExecutor, this::onMagnificationThumbnailFeatureFlagChanged);
if (thumbnailSupplier != null) {
mThumbnailSupplier = thumbnailSupplier;
} else {
mThumbnailSupplier = () -> {
- return new MagnificationThumbnail(
- ctx.getContext(),
- ctx.getContext().getSystemService(WindowManager.class),
- new Handler(ctx.getContext().getMainLooper())
- );
+ if (mMagnificationThumbnailFeatureFlag.isFeatureFlagEnabled()) {
+ return new MagnificationThumbnail(
+ ctx.getContext(),
+ ctx.getContext().getSystemService(WindowManager.class),
+ new Handler(ctx.getContext().getMainLooper())
+ );
+ }
+ return null;
};
}
}
+ private void onMagnificationThumbnailFeatureFlagChanged() {
+ synchronized (mLock) {
+ for (int i = 0; i < mDisplays.size(); i++) {
+ onMagnificationThumbnailFeatureFlagChanged(mDisplays.keyAt(i));
+ }
+ }
+ }
+
+ private void onMagnificationThumbnailFeatureFlagChanged(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ display.onThumbnailFeatureFlagChanged();
+ }
+ }
+
/**
* Start tracking the magnification region for services that control magnification and the
* magnification gesture handler.
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
new file mode 100644
index 0000000..519f31b
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import android.provider.DeviceConfig;
+
+/**
+ * Encapsulates the feature flags for magnification thumbnail. {@see DeviceConfig}
+ *
+ * @hide
+ */
+public class MagnificationThumbnailFeatureFlag extends MagnificationFeatureFlagBase {
+
+ private static final String NAMESPACE = DeviceConfig.NAMESPACE_ACCESSIBILITY;
+ private static final String FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL =
+ "enable_magnifier_thumbnail";
+
+ @Override
+ String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ String getFeatureName() {
+ return FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL;
+ }
+
+ @Override
+ boolean getDefaultValue() {
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index c6d6122..80d14a2 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -346,6 +346,9 @@
if (mHandler.hasMessages(CANCEL_GAME_LOADING_MODE)) {
mHandler.removeMessages(CANCEL_GAME_LOADING_MODE);
}
+ Slog.v(TAG, String.format(
+ "Game loading power mode %s (game state change isLoading=%b)",
+ isLoading ? "ON" : "OFF", isLoading));
mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
if (isLoading) {
int loadingBoostDuration = getLoadingBoostDuration(packageName, userId);
@@ -369,6 +372,7 @@
break;
}
case CANCEL_GAME_LOADING_MODE: {
+ Slog.v(TAG, "Game loading power mode OFF (loading boost ended)");
mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false);
break;
}
@@ -1279,6 +1283,7 @@
// instruction.
mHandler.removeMessages(CANCEL_GAME_LOADING_MODE);
} else {
+ Slog.v(TAG, "Game loading power mode ON (loading boost on game start)");
mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, true);
}
@@ -1555,6 +1560,10 @@
}
}
}, new IntentFilter(Intent.ACTION_SHUTDOWN));
+ Slog.v(TAG, "Game loading power mode OFF (game manager service start/restart)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false);
+ Slog.v(TAG, "Game power mode OFF (game manager service start/restart)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME, false);
}
private void sendUserMessage(int userId, int what, String eventForLog, int delayMillis) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 5819ff0..5c1897d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -185,8 +185,7 @@
* True if {@link #mWakeLock} is open for acquisition. It is set to false after the client is
* unregistered.
*/
- @GuardedBy("mWakeLock")
- private boolean mIsWakelockUsable = true;
+ private AtomicBoolean mIsWakelockUsable = new AtomicBoolean(true);
/*
* Internal interface used to invoke client callbacks.
@@ -529,7 +528,7 @@
@VisibleForTesting
boolean isWakelockUsable() {
synchronized (mWakeLock) {
- return mIsWakelockUsable;
+ return mIsWakelockUsable.get();
}
}
@@ -1103,10 +1102,8 @@
private void acquireWakeLock() {
Binder.withCleanCallingIdentity(
() -> {
- synchronized (mWakeLock) {
- if (mIsWakelockUsable) {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
- }
+ if (mIsWakelockUsable.get()) {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
}
});
}
@@ -1119,13 +1116,11 @@
private void releaseWakeLock() {
Binder.withCleanCallingIdentity(
() -> {
- synchronized (mWakeLock) {
- if (mWakeLock.isHeld()) {
- try {
- mWakeLock.release();
- } catch (RuntimeException e) {
- Log.e(TAG, "Releasing the wakelock fails - ", e);
- }
+ if (mWakeLock.isHeld()) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Releasing the wakelock fails - ", e);
}
}
});
@@ -1139,18 +1134,16 @@
private void releaseWakeLockOnExit() {
Binder.withCleanCallingIdentity(
() -> {
- synchronized (mWakeLock) {
- mIsWakelockUsable = false;
- while (mWakeLock.isHeld()) {
- try {
- mWakeLock.release();
- } catch (RuntimeException e) {
- Log.e(
- TAG,
- "Releasing the wakelock for all acquisitions fails - ",
- e);
- break;
- }
+ mIsWakelockUsable.set(false);
+ while (mWakeLock.isHeld()) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(
+ TAG,
+ "Releasing the wakelock for all acquisitions fails - ",
+ e);
+ break;
}
}
});
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index ec0d985..77a60289 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -123,7 +123,7 @@
* @param userId The uid of the user whose profile has been unlocked.
* @param credentialType The type of credential as defined in {@code LockPatternUtils}
* @param credential The credential, encoded as a byte array
- * @param credentialUpdated signals weather credentials were updated.
+ * @param credentialUpdated indicates credentials change.
* @param platformKeyManager platform key manager
* @param testOnlyInsecureCertificateHelper utility class used for end-to-end tests
*/
@@ -143,7 +143,7 @@
mRecoverableKeyStoreDb = recoverableKeyStoreDb;
mUserId = userId;
mCredentialType = credentialType;
- mCredential = credential;
+ mCredential = credential != null ? Arrays.copyOf(credential, credential.length) : null;
mCredentialUpdated = credentialUpdated;
mPlatformKeyManager = platformKeyManager;
mRecoverySnapshotStorage = snapshotStorage;
@@ -160,6 +160,10 @@
}
} catch (Exception e) {
Log.e(TAG, "Unexpected exception thrown during KeySyncTask", e);
+ } finally {
+ if (mCredential != null) {
+ Arrays.fill(mCredential, (byte) 0); // no longer needed.
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
index b5c0417..1ed829e4 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
@@ -32,6 +32,7 @@
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
import android.os.Process;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
@@ -421,9 +422,19 @@
} else {
candidates.addAll(resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates));
}
+ // When we have only a single result belonging to a different user space, we check
+ // if filtering is configured on cross-profile intent resolution for the originating user.
+ // Accordingly, we modify the intent to reflect the content-owner as the originating user,
+ // preventing the resolving user to be assumed the same, when activity is started.
+
+ // In case more than one result is present, the resolver sheet is opened which takes care of
+ // cross user access.
+ if (candidates.size() == 1 && !UserHandle.of(userId).equals(candidates.get(0).userHandle)
+ && isNoFilteringPropertyConfiguredForUser(userId)) {
+ intent.prepareToLeaveUser(userId);
+ }
return new QueryIntentActivitiesResult(sortResult, addInstant, candidates);
}
-
/**
* It filters and combines results from current and cross profile based on domain priority.
* @param computer {@link Computer} instance
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 80e07f4..10cd51a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1886,7 +1886,14 @@
}
public void onSessionFinished(final PackageInstallerSession session, boolean success) {
- mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+ if (success) {
+ // There is a timing issue here, if the callback opens the session again in
+ // notifySessionFinished() immediately, the session may not be removed from
+ // the mSession. But to avoid adding unknown latency, only notifying failures
+ // are moved to the last of posted runnable, notifying success cases are
+ // still kept here.
+ mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+ }
mInstallHandler.post(new Runnable() {
@Override
@@ -1915,6 +1922,10 @@
mSettingsWriteRequest.runNow();
}
+ if (!success) {
+ mCallbacks.notifySessionFinished(
+ session.sessionId, session.userId, success);
+ }
}
});
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 710e0b7..dd434fbe 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -37,6 +37,7 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -1927,11 +1928,32 @@
}
if (shortcut.getIcon() != null) {
ShortcutInfo.validateIcon(shortcut.getIcon());
+ validateIconURI(shortcut);
}
shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED);
}
+ // Validates the calling process has permission to access shortcut icon's image uri
+ private void validateIconURI(@NonNull final ShortcutInfo si) {
+ final int callingUid = injectBinderCallingUid();
+ final Icon icon = si.getIcon();
+ if (icon == null) {
+ // There's no icon in this shortcut, nothing to validate here.
+ return;
+ }
+ int iconType = icon.getType();
+ if (iconType != Icon.TYPE_URI && iconType != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ // The icon is not URI-based, nothing to validate.
+ return;
+ }
+ final Uri uri = icon.getUri();
+ mUriGrantsManagerInternal.checkGrantUriPermission(callingUid, si.getPackage(),
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(callingUid)));
+ }
+
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b01a89e..7897195 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -72,6 +72,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -93,7 +94,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiFunction;
/**
* Manages all permissions and handles permissions related tasks.
@@ -233,11 +233,11 @@
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkPermission(
- packageName, permissionName, userId);
+ return mPermissionManagerServiceImpl.checkPermission(packageName, permissionName,
+ deviceId, userId);
}
- return checkPermissionDelegate.checkPermission(packageName, permissionName, userId,
- mPermissionManagerServiceImpl::checkPermission);
+ return checkPermissionDelegate.checkPermission(packageName, permissionName,
+ deviceId, userId, mPermissionManagerServiceImpl::checkPermission);
}
@Override
@@ -254,10 +254,10 @@
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName);
+ return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId);
}
return checkPermissionDelegate.checkUidPermission(uid, permissionName,
- mPermissionManagerServiceImpl::checkUidPermission);
+ deviceId, mPermissionManagerServiceImpl::checkUidPermission);
}
@Override
@@ -511,14 +511,14 @@
public int getPermissionFlags(String packageName, String permissionName, int deviceId,
int userId) {
return mPermissionManagerServiceImpl
- .getPermissionFlags(packageName, permissionName, userId);
+ .getPermissionFlags(packageName, permissionName, deviceId, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permissionName, int flagMask,
int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask,
- flagValues, checkAdjustPolicyFlagPermission, userId);
+ flagValues, checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -560,14 +560,15 @@
@Override
public void grantRuntimePermission(String packageName, String permissionName, int deviceId,
int userId) {
- mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId);
+ mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName,
+ deviceId, userId);
}
@Override
public void revokeRuntimePermission(String packageName, String permissionName, int deviceId,
int userId, String reason) {
mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName,
- userId, reason);
+ deviceId, userId, reason);
}
@Override
@@ -580,14 +581,14 @@
public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int deviceId, int userId) {
return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
- permissionName, userId);
+ permissionName, deviceId, userId);
}
@Override
public boolean isPermissionRevokedByPolicy(String packageName, String permissionName,
int deviceId, int userId) {
- return mPermissionManagerServiceImpl
- .isPermissionRevokedByPolicy(packageName, permissionName, userId);
+ return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName,
+ permissionName, deviceId, userId);
}
@Override
@@ -868,6 +869,7 @@
*
* @param packageName the name of the package to be checked
* @param permissionName the name of the permission to be checked
+ * @param deviceId The device ID
* @param userId the user ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
@@ -876,20 +878,21 @@
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- @UserIdInt int userId,
- @NonNull TriFunction<String, String, Integer, Integer> superImpl);
+ int deviceId, @UserIdInt int userId,
+ @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl);
/**
* Check whether the given UID has been granted the specified permission.
*
* @param uid the UID to be checked
* @param permissionName the name of the permission to be checked
+ * @param deviceId The device ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
* the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*/
- int checkUidPermission(int uid, @NonNull String permissionName,
- BiFunction<Integer, String, Integer> superImpl);
+ int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
+ TriFunction<Integer, String, Integer, Integer> superImpl);
/**
* @return list of delegated permissions
@@ -918,31 +921,32 @@
@Override
public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
+ int deviceId, int userId,
+ @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl) {
if (mDelegatedPackageName.equals(packageName)
&& isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply("com.android.shell", permissionName, userId);
+ return superImpl.apply("com.android.shell", permissionName, deviceId, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(packageName, permissionName, userId);
+ return superImpl.apply(packageName, permissionName, deviceId, userId);
}
@Override
- public int checkUidPermission(int uid, @NonNull String permissionName,
- @NonNull BiFunction<Integer, String, Integer> superImpl) {
+ public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
+ @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) {
if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(Process.SHELL_UID, permissionName);
+ return superImpl.apply(Process.SHELL_UID, permissionName, deviceId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(uid, permissionName);
+ return superImpl.apply(uid, permissionName, deviceId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 4353c57..6764e08 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -681,7 +681,7 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
final int callingUid = Binder.getCallingUid();
return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
}
@@ -724,7 +724,7 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
final int callingUid = Binder.getCallingUid();
boolean overridePolicy = false;
@@ -908,8 +908,12 @@
}
}
+ private int checkPermission(String pkgName, String permName, int userId) {
+ return checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, userId);
+ }
+
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
@@ -975,8 +979,12 @@
return true;
}
+ private int checkUidPermission(int uid, String permName) {
+ return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
+ }
+
@Override
- public int checkUidPermission(int uid, String permName) {
+ public int checkUidPermission(int uid, String permName, int deviceId) {
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
@@ -1295,7 +1303,8 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, final int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
@@ -1468,11 +1477,11 @@
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ int userId, String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
- checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, deviceId)
== PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
@@ -1859,7 +1868,7 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- @UserIdInt int userId) {
+ int deviceId, @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
@@ -1922,7 +1931,8 @@
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -2059,8 +2069,8 @@
continue;
}
boolean isSystemOrPolicyFixed = (getPermissionFlags(newPackage.getPackageName(),
- permInfo.name, userId) & (FLAG_PERMISSION_SYSTEM_FIXED
- | FLAG_PERMISSION_POLICY_FIXED)) != 0;
+ permInfo.name, Context.DEVICE_ID_DEFAULT, userId) & (
+ FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0;
if (isSystemOrPolicyFixed) {
continue;
}
@@ -2226,7 +2236,8 @@
for (final int userId : userIds) {
final int permissionState = checkPermission(packageName, permName,
userId);
- final int flags = getPermissionFlags(packageName, permName, userId);
+ final int flags = getPermissionFlags(packageName, permName,
+ Context.DEVICE_ID_DEFAULT, userId);
final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED
| FLAG_PERMISSION_GRANTED_BY_DEFAULT
@@ -5122,8 +5133,7 @@
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName,
- @UserIdInt int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
Objects.requireNonNull(packageName, "packageName");
Preconditions.checkArgumentNonNegative(userId, "userId");
return getGrantedPermissionsInternal(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 128f847..2d824aa 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -25,7 +25,6 @@
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.permission.IOnPermissionsChangeListener;
-import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -137,14 +136,16 @@
void removePermission(String permName);
/**
- * Gets the state flags associated with a permission.
+ * Gets the permission state flags associated with a permission.
*
* @param packageName the package name for which to get the flags
* @param permName the permission for which to get the flags
+ * @param deviceId The device for which to get the flags
* @param userId the user for which to get permission flags
* @return the permission flags
*/
- int getPermissionFlags(String packageName, String permName, int userId);
+ int getPermissionFlags(String packageName, String permName, int deviceId,
+ @UserIdInt int userId);
/**
* Updates the flags associated with a permission by replacing the flags in the specified mask
@@ -154,10 +155,11 @@
* @param permName The permission for which to update the flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
+ * @param deviceId The device for which to update the permission flags
* @param userId The user for which to update the permission flags
*/
- void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
+ void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues,
+ boolean checkAdjustPolicyFlagPermission, int deviceId, @UserIdInt int userId);
/**
* Update the permission flags for all packages and runtime permissions of a user in order
@@ -291,11 +293,13 @@
*
* @param packageName the package to which to grant the permission
* @param permName the permission name to grant
+ * @param deviceId the device for which to grant the permission
* @param userId the user for which to grant the permission
*
- * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
+ * @see #revokeRuntimePermission(String, String, int, int, String)
*/
- void grantRuntimePermission(String packageName, String permName, int userId);
+ void grantRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId);
/**
* Revoke a runtime permission that was previously granted by
@@ -310,13 +314,14 @@
*
* @param packageName the package from which to revoke the permission
* @param permName the permission name to revoke
+ * @param deviceId the device for which to revoke the permission
* @param userId the user for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
- * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+ * @see #grantRuntimePermission(String, String, int, int)
*/
- void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason);
+ void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId, String reason);
/**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
@@ -333,24 +338,29 @@
* does not clearly communicate to the user what would be the benefit from grating this
* permission.
*
+ * @param packageName the package name
* @param permName a permission your app wants to request
+ * @param deviceId the device for which to check the permission
+ * @param userId the user for which to check the permission
* @return whether you can show permission rationale UI
*/
boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- @UserIdInt int userId);
+ int deviceId, @UserIdInt int userId);
/**
- * Checks whether a particular permissions has been revoked for a package by policy. Typically
+ * Checks whether a particular permission has been revoked for a package by policy. Typically,
* the device owner or the profile owner may apply such a policy. The user cannot grant policy
* revoked permissions, hence the only way for an app to get such a permission is by a policy
* change.
*
* @param packageName the name of the package you are checking against
* @param permName the name of the permission you are checking for
- *
+ * @param deviceId the device for which you are checking the permission
+ * @param userId the device for which you are checking the permission
* @return whether the permission is restricted by policy
*/
- boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId);
+ boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ @UserIdInt int userId);
/**
* Get set of permissions that have been split into more granular or dependent permissions.
@@ -373,14 +383,25 @@
List<SplitPermissionInfoParcelable> getSplitPermissions();
/**
- * TODO:theianchen add doc describing this is the old checkPermissionImpl
+ * Check whether a permission is granted or not to a package.
+ *
+ * @param pkgName package name
+ * @param permName permission name
+ * @param deviceId device ID
+ * @param userId user ID
+ * @return permission result {@link PackageManager.PermissionResult}
*/
- int checkPermission(String pkgName, String permName, int userId);
+ int checkPermission(String pkgName, String permName, int deviceId, @UserIdInt int userId);
/**
- * TODO:theianchen add doc describing this is the old checkUidPermissionImpl
+ * Check whether a permission is granted or not to an UID.
+ *
+ * @param uid UID
+ * @param permName permission name
+ * @param deviceId device ID
+ * @return permission result {@link PackageManager.PermissionResult}
*/
- int checkUidPermission(int uid, String permName);
+ int checkUidPermission(int uid, String permName, int deviceId);
/**
* Get all the package names requesting app op permissions.
@@ -400,15 +421,11 @@
@UserIdInt int userId);
/**
- * Reset the runtime permission state changes for a package.
+ * Reset the runtime permission state changes for a package for all devices.
*
* TODO(zhanghai): Turn this into package change callback?
- *
- * @param pkg the package
- * @param userId the user ID
*/
- void resetRuntimePermissions(@NonNull AndroidPackage pkg,
- @UserIdInt int userId);
+ void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId);
/**
* Reset the runtime permission state changes for all packages in a user.
@@ -449,8 +466,8 @@
/**
* Get all the permissions granted to a package.
*
- * @param packageName the name of the package
- * @param userId the user ID
+ * @param packageName package name
+ * @param userId user ID
* @return the names of the granted permissions
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index 7f98e21..dacb8c6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -120,21 +120,21 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ")");
- return mService.getPermissionFlags(packageName, permName, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.getPermissionFlags(packageName, permName, deviceId, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = "
+ permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues
+ ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission
- + ", userId = " + userId + ")");
+ + ", deviceId = " + deviceId + ", userId = " + userId + ")");
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -182,18 +182,20 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ int userId) {
Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ")");
- mService.grantRuntimePermission(packageName, permName, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ mService.grantRuntimePermission(packageName, permName, deviceId, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ int userId, String reason) {
Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ", reason = " + reason + ")");
- mService.revokeRuntimePermission(packageName, permName, userId, reason);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId
+ + ", reason = " + reason + ")");
+ mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
}
@Override
@@ -205,17 +207,20 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int userId) {
+ int deviceId, int userId) {
Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName
- + ", permName = " + permName + ", userId = " + userId + ")");
- return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
+ + ", permName = " + permName + ", deviceId = " + deviceId
+ + ", userId = " + userId + ")");
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
+ userId);
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ int userId) {
Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ")");
- return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
}
@Override
@@ -225,16 +230,17 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName
- + ", userId = " + userId + ")");
- return mService.checkPermission(pkgName, permName, userId);
+ + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.checkPermission(pkgName, permName, deviceId, userId);
}
@Override
- public int checkUidPermission(int uid, String permName) {
- Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + ")");
- return mService.checkUidPermission(uid, permName);
+ public int checkUidPermission(int uid, String permName, int deviceId) {
+ Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
+ + ", deviceId = " + deviceId + ")");
+ return mService.checkUidPermission(uid, permName, deviceId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index d4c6d42..35d165b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -153,9 +153,10 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
- int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, userId);
- int newVal = mNewImplementation.getPermissionFlags(packageName, permName, userId);
+ public int getPermissionFlags(String packageName, String permName, int deviceId,
+ @UserIdInt int userId) {
+ int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
+ int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getPermissionFlags");
@@ -165,11 +166,12 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId,
+ @UserIdInt int userId) {
mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -234,16 +236,17 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int userId) {
- mOldImplementation.grantRuntimePermission(packageName, permName, userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, userId);
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
- mOldImplementation.grantRuntimePermission(packageName, permName, userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, userId);
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId, String reason) {
+ mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
}
@Override
@@ -255,11 +258,11 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int userId) {
- boolean oldVal = mOldImplementation
- .shouldShowRequestPermissionRationale(packageName, permName, userId);
- boolean newVal = mNewImplementation
- .shouldShowRequestPermissionRationale(packageName, permName, userId);
+ int deviceId, @UserIdInt int userId) {
+ boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName,
+ permName, deviceId, userId);
+ boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName,
+ permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("shouldShowRequestPermissionRationale");
@@ -268,11 +271,12 @@
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
- boolean oldVal = mOldImplementation
- .isPermissionRevokedByPolicy(packageName, permName, userId);
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ @UserIdInt int userId) {
+ boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName,
+ deviceId, userId);
boolean newVal = mNewImplementation.isPermissionRevokedByPolicy(packageName, permName,
- userId);
+ deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("isPermissionRevokedByPolicy");
@@ -292,9 +296,9 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
- int oldVal = mOldImplementation.checkPermission(pkgName, permName, userId);
- int newVal = mNewImplementation.checkPermission(pkgName, permName, userId);
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId);
+ int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkPermission");
@@ -303,9 +307,9 @@
}
@Override
- public int checkUidPermission(int uid, String permName) {
- int oldVal = mOldImplementation.checkUidPermission(uid, permName);
- int newVal = mNewImplementation.checkUidPermission(uid, permName);
+ public int checkUidPermission(int uid, String permName, int deviceId) {
+ int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId);
+ int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkUidPermission");
@@ -372,7 +376,7 @@
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName, int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
Set<String> oldVal = mOldImplementation.getGrantedPermissions(packageName, userId);
Set<String> newVal = mNewImplementation.getGrantedPermissions(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index 4e72fae..cbeede0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -158,10 +158,10 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags");
try {
- return mService.getPermissionFlags(packageName, permName, userId);
+ return mService.getPermissionFlags(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -169,12 +169,12 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags");
try {
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -253,23 +253,24 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission");
try {
- mService.grantRuntimePermission(packageName, permName, userId);
+ mService.grantRuntimePermission(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ int userId, String reason) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission");
try {
- mService.revokeRuntimePermission(packageName, permName, userId, reason);
+ mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -288,22 +289,24 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int userId) {
+ int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale");
try {
- return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
+ return mService.shouldShowRequestPermissionRationale(
+ packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy");
try {
- return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
+ return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -321,20 +324,20 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission");
try {
- return mService.checkPermission(pkgName, permName, userId);
+ return mService.checkPermission(pkgName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public int checkUidPermission(int uid, String permName) {
+ public int checkUidPermission(int uid, String permName, int deviceId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission");
try {
- return mService.checkUidPermission(uid, permName);
+ return mService.checkUidPermission(uid, permName, deviceId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f86f1d..faf132e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2656,7 +2656,19 @@
}
}
- public void updateSettings() {
+ private void updateSettings() {
+ updateSettings(null);
+ }
+
+ /**
+ * Update provider Setting values on a given {@code handler}, or synchronously if {@code null}
+ * is passed for handler.
+ */
+ void updateSettings(Handler handler) {
+ if (handler != null) {
+ handler.post(() -> updateSettings(null));
+ return;
+ }
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
synchronized (mLock) {
@@ -4499,6 +4511,7 @@
} else {
sleepRelease(event.getEventTime());
}
+ sendSystemKeyToStatusBarAsync(event);
break;
}
@@ -4509,6 +4522,7 @@
if (!down) {
mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
}
+ sendSystemKeyToStatusBarAsync(event);
break;
}
@@ -5583,12 +5597,7 @@
mDefaultDisplayRotation.updateOrientationListener();
synchronized (mLock) {
mSystemReady = true;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- updateSettings();
- }
- });
+ updateSettings(mHandler);
// If this happens, for whatever reason, systemReady came later than systemBooted.
// And keyguard should be already bound from systemBooted
if (mSystemBooted) {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 4f7f13e..fed6e7e 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.AudioAttributes;
import android.os.CombinedVibration;
import android.os.IBinder;
import android.os.VibrationAttributes;
@@ -27,8 +28,10 @@
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
+import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@@ -39,7 +42,9 @@
* The base class for all vibrations.
*/
abstract class Vibration {
- private static final SimpleDateFormat DEBUG_DATE_FORMAT =
+ private static final SimpleDateFormat DEBUG_TIME_FORMAT =
+ new SimpleDateFormat("HH:mm:ss.SSS");
+ private static final SimpleDateFormat DEBUG_DATE_TIME_FORMAT =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
// Used to generate globally unique vibration ids.
private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
@@ -146,10 +151,10 @@
@Override
public String toString() {
return "CallerInfo{"
- + " attrs=" + attrs
- + ", uid=" + uid
- + ", displayId=" + displayId
+ + " uid=" + uid
+ ", opPkg=" + opPkg
+ + ", displayId=" + displayId
+ + ", attrs=" + attrs
+ ", reason=" + reason
+ '}';
}
@@ -203,14 +208,17 @@
* potentially expensive or resource-linked objects, such as {@link IBinder}.
*/
static final class DebugInfo {
- private final long mCreateTime;
+ final long mCreateTime;
+ final CallerInfo mCallerInfo;
+ @Nullable
+ final CombinedVibration mPlayedEffect;
+
private final long mStartTime;
private final long mEndTime;
private final long mDurationMs;
- @Nullable private final CombinedVibration mOriginalEffect;
- @Nullable private final CombinedVibration mPlayedEffect;
+ @Nullable
+ private final CombinedVibration mOriginalEffect;
private final float mScale;
- private final CallerInfo mCallerInfo;
private final Status mStatus;
DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
@@ -230,10 +238,10 @@
@Override
public String toString() {
- return "createTime: " + DEBUG_DATE_FORMAT.format(new Date(mCreateTime))
- + ", startTime: " + DEBUG_DATE_FORMAT.format(new Date(mStartTime))
+ return "createTime: " + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime))
+ + ", startTime: " + DEBUG_DATE_TIME_FORMAT.format(new Date(mStartTime))
+ ", endTime: "
- + (mEndTime == 0 ? null : DEBUG_DATE_FORMAT.format(new Date(mEndTime)))
+ + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime)))
+ ", durationMs: " + mDurationMs
+ ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ ", playedEffect: " + mPlayedEffect
@@ -242,8 +250,56 @@
+ ", callerInfo: " + mCallerInfo;
}
+ /**
+ * Write this info in a compact way into given {@link PrintWriter}.
+ *
+ * <p>This is used by dumpsys to log multiple vibration records in single lines that are
+ * easy to skim through by the sorted created time.
+ */
+ void dumpCompact(IndentingPrintWriter pw) {
+ boolean isExternalVibration = mPlayedEffect == null;
+ String timingsStr = String.format(Locale.ROOT,
+ "%s | %8s | %20s | duration: %5dms | start: %12s | end: %10s",
+ DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
+ isExternalVibration ? "external" : "effect",
+ mStatus.name().toLowerCase(Locale.ROOT),
+ mDurationMs,
+ mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
+ mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
+ String callerInfoStr = String.format(Locale.ROOT,
+ " | %s (uid=%d, displayId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.displayId,
+ mCallerInfo.attrs.usageToString(),
+ AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
+ Long.toBinaryString(mCallerInfo.attrs.getFlags()),
+ mCallerInfo.reason);
+ String effectStr = String.format(Locale.ROOT,
+ " | played: %s | original: %s | scale: %.2f",
+ mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
+ mOriginalEffect == null ? null : mOriginalEffect.toDebugString(),
+ mScale);
+ pw.println(timingsStr + callerInfoStr + effectStr);
+ }
+
+ /** Write this info into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Vibration:");
+ pw.increaseIndent();
+ pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
+ pw.println("durationMs = " + mDurationMs);
+ pw.println("createTime = " + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)));
+ pw.println("startTime = " + DEBUG_DATE_TIME_FORMAT.format(new Date(mStartTime)));
+ pw.println("endTime = "
+ + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime))));
+ pw.println("playedEffect = " + mPlayedEffect);
+ pw.println("originalEffect = " + mOriginalEffect);
+ pw.println("scale = " + String.format(Locale.ROOT, "%.2f", mScale));
+ pw.println("callerInfo = " + mCallerInfo);
+ pw.decreaseIndent();
+ }
+
/** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
- public void dumpProto(ProtoOutputStream proto, long fieldId) {
+ void dump(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(VibrationProto.START_TIME, mStartTime);
proto.write(VibrationProto.END_TIME, mEndTime);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index dbd6bf4..db8a9ae 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -54,6 +54,7 @@
import android.os.Vibrator.VibrationIntensity;
import android.os.vibrator.VibrationConfig;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -65,6 +66,7 @@
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -601,8 +603,32 @@
}
}
+ /** Write current settings into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ pw.println("VibrationSettings:");
+ pw.increaseIndent();
+ pw.println("vibrateOn = " + mVibrateOn);
+ pw.println("vibrateInputDevices = " + mVibrateInputDevices);
+ pw.println("batterySaverMode = " + mBatterySaverMode);
+ pw.println("VibrationIntensities:");
+
+ pw.increaseIndent();
+ for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+ int usage = mCurrentVibrationIntensities.keyAt(i);
+ int intensity = mCurrentVibrationIntensities.valueAt(i);
+ pw.println(VibrationAttributes.usageToString(usage) + " = "
+ + intensityToString(intensity)
+ + ", default: " + intensityToString(getDefaultIntensity(usage)));
+ }
+ pw.decreaseIndent();
+
+ mVibrationConfig.dumpWithoutDefaultSettings(pw);
+ pw.println("processStateCache = " + mUidObserver.mProcStatesCache);
+ pw.decreaseIndent();
+ }
+
/** Write current settings into given {@link ProtoOutputStream}. */
- public void dumpProto(ProtoOutputStream proto) {
+ void dump(ProtoOutputStream proto) {
synchronized (mLock) {
proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 47b3e1a..f5d4d1e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -17,6 +17,7 @@
package com.android.server.vibrator;
import android.annotation.Nullable;
+import android.annotation.Nullable;
import android.hardware.vibrator.IVibrator;
import android.os.Binder;
import android.os.IVibratorStateListener;
@@ -26,6 +27,7 @@
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -351,6 +353,19 @@
+ '}';
}
+ void dump(IndentingPrintWriter pw) {
+ pw.println("VibratorController:");
+ pw.increaseIndent();
+ pw.println("isVibrating = " + mIsVibrating);
+ pw.println("isUnderExternalControl = " + mIsUnderExternalControl);
+ pw.println("currentAmplitude = " + mCurrentAmplitude);
+ pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
+ pw.println("vibratorStateListenerCount = "
+ + mVibratorStateListeners.getRegisteredCallbackCount());
+ mVibratorInfo.dump(pw);
+ pw.decreaseIndent();
+ }
+
@GuardedBy("mLock")
private void notifyListenerOnVibrating(boolean isVibrating) {
if (mIsVibrating != isVibrating) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 6fdb1db..270f7f0c 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -56,6 +56,7 @@
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.persistence.VibrationXmlParser;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -80,6 +81,7 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -204,9 +206,15 @@
mNativeWrapper = injector.getNativeWrapper();
mNativeWrapper.init(listener);
- int dumpLimit = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_previousVibrationsDumpLimit);
- mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+ int recentDumpSizeLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_recentVibrationsDumpSizeLimit);
+ int dumpSizeLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit);
+ int dumpAggregationTimeLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer
+ .config_previousVibrationsDumpAggregationTimeMillisLimit);
+ mVibratorManagerRecords = new VibratorManagerRecords(
+ recentDumpSizeLimit, dumpSizeLimit, dumpAggregationTimeLimit);
mBatteryStatsService = injector.getBatteryStatsService();
mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
@@ -544,49 +552,74 @@
}
}
- private void dumpText(PrintWriter pw) {
+ private void dumpText(PrintWriter w) {
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to text...");
}
+ IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ " ");
synchronized (mLock) {
pw.println("Vibrator Manager Service:");
- pw.println(" mVibrationSettings:");
- pw.println(" " + mVibrationSettings);
+ pw.increaseIndent();
+
+ mVibrationSettings.dump(pw);
pw.println();
- pw.println(" mVibratorControllers:");
+
+ pw.println("VibratorControllers:");
+ pw.increaseIndent();
for (int i = 0; i < mVibrators.size(); i++) {
- pw.println(" " + mVibrators.valueAt(i));
+ mVibrators.valueAt(i).dump(pw);
}
+ pw.decreaseIndent();
pw.println();
- pw.println(" mCurrentVibration:");
- pw.println(" " + (mCurrentVibration == null
- ? null : mCurrentVibration.getVibration().getDebugInfo()));
+
+ pw.println("CurrentVibration:");
+ pw.increaseIndent();
+ if (mCurrentVibration != null) {
+ mCurrentVibration.getVibration().getDebugInfo().dump(pw);
+ } else {
+ pw.println("null");
+ }
+ pw.decreaseIndent();
pw.println();
- pw.println(" mNextVibration:");
- pw.println(" " + (mNextVibration == null
- ? null : mNextVibration.getVibration().getDebugInfo()));
+
+ pw.println("NextVibration:");
+ pw.increaseIndent();
+ if (mNextVibration != null) {
+ mNextVibration.getVibration().getDebugInfo().dump(pw);
+ } else {
+ pw.println("null");
+ }
+ pw.decreaseIndent();
pw.println();
- pw.println(" mCurrentExternalVibration:");
- pw.println(" " + (mCurrentExternalVibration == null
- ? null : mCurrentExternalVibration.getDebugInfo()));
- pw.println();
+
+ pw.println("CurrentExternalVibration:");
+ pw.increaseIndent();
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.getDebugInfo().dump(pw);
+ } else {
+ pw.println("null");
+ }
+ pw.decreaseIndent();
}
- mVibratorManagerRecords.dumpText(pw);
+
+ pw.println();
+ pw.println();
+ mVibratorManagerRecords.dump(pw);
}
- synchronized void dumpProto(FileDescriptor fd) {
+ private void dumpProto(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to proto...");
}
synchronized (mLock) {
- mVibrationSettings.dumpProto(proto);
+ mVibrationSettings.dump(proto);
if (mCurrentVibration != null) {
- mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
+ mCurrentVibration.getVibration().getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
}
if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
+ mCurrentExternalVibration.getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
@@ -601,7 +634,7 @@
proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
isUnderExternalControl);
}
- mVibratorManagerRecords.dumpProto(proto);
+ mVibratorManagerRecords.dump(proto);
proto.flush();
}
@@ -1581,64 +1614,105 @@
/** Keep records of vibrations played and provide debug information for this service. */
private static final class VibratorManagerRecords {
- private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
- new SparseArray<>();
- private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
- new LinkedList<>();
- private final int mPreviousVibrationsLimit;
+ private final VibrationRecords mAggregatedVibrationHistory;
+ private final VibrationRecords mRecentVibrations;
- VibratorManagerRecords(int limit) {
- mPreviousVibrationsLimit = limit;
+ VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit,
+ int aggregationTimeLimit) {
+ mAggregatedVibrationHistory =
+ new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit);
+ mRecentVibrations = new VibrationRecords(
+ recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
}
synchronized void record(HalVibration vib) {
- int usage = vib.callerInfo.attrs.getUsage();
- if (!mPreviousVibrations.contains(usage)) {
- mPreviousVibrations.put(usage, new LinkedList<>());
- }
- record(mPreviousVibrations.get(usage), vib.getDebugInfo());
+ record(vib.getDebugInfo());
}
synchronized void record(ExternalVibrationHolder vib) {
- record(mPreviousExternalVibrations, vib.getDebugInfo());
+ record(vib.getDebugInfo());
}
- synchronized void record(LinkedList<Vibration.DebugInfo> records,
- Vibration.DebugInfo info) {
- if (records.size() > mPreviousVibrationsLimit) {
- records.removeFirst();
+ private synchronized void record(Vibration.DebugInfo info) {
+ AggregatedVibrationRecord removedRecord = mRecentVibrations.record(info);
+ if (removedRecord != null) {
+ mAggregatedVibrationHistory.record(removedRecord.mLatestVibration);
}
- records.addLast(info);
}
- synchronized void dumpText(PrintWriter pw) {
- for (int i = 0; i < mPreviousVibrations.size(); i++) {
- pw.println();
- pw.print(" Previous vibrations for usage ");
- pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
- pw.println(":");
- for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
- pw.println(" " + info);
+ synchronized void dump(IndentingPrintWriter pw) {
+ pw.println("Recent vibrations:");
+ pw.increaseIndent();
+ mRecentVibrations.dump(pw);
+ pw.decreaseIndent();
+ pw.println();
+ pw.println();
+
+ pw.println("Aggregated vibration history:");
+ pw.increaseIndent();
+ mAggregatedVibrationHistory.dump(pw);
+ pw.decreaseIndent();
+ }
+
+ synchronized void dump(ProtoOutputStream proto) {
+ mRecentVibrations.dump(proto);
+ }
+ }
+
+ /** Keep records of vibrations played and provide debug information for this service. */
+ private static final class VibrationRecords {
+ private final SparseArray<LinkedList<AggregatedVibrationRecord>> mVibrations =
+ new SparseArray<>();
+ private final int mSizeLimit;
+ private final int mAggregationTimeLimit;
+
+ VibrationRecords(int sizeLimit, int aggregationTimeLimit) {
+ mSizeLimit = sizeLimit;
+ mAggregationTimeLimit = aggregationTimeLimit;
+ }
+
+ synchronized AggregatedVibrationRecord record(Vibration.DebugInfo info) {
+ int usage = info.mCallerInfo.attrs.getUsage();
+ if (!mVibrations.contains(usage)) {
+ mVibrations.put(usage, new LinkedList<>());
+ }
+ LinkedList<AggregatedVibrationRecord> records = mVibrations.get(usage);
+ if (mAggregationTimeLimit > 0 && !records.isEmpty()) {
+ AggregatedVibrationRecord lastRecord = records.getLast();
+ if (lastRecord.mayAggregate(info, mAggregationTimeLimit)) {
+ lastRecord.record(info);
+ return null;
}
}
+ AggregatedVibrationRecord removedRecord = null;
+ if (records.size() > mSizeLimit) {
+ removedRecord = records.removeFirst();
+ }
+ records.addLast(new AggregatedVibrationRecord(info));
+ return removedRecord;
+ }
- pw.println();
- pw.println(" Previous external vibrations:");
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- pw.println(" " + info);
+ synchronized void dump(IndentingPrintWriter pw) {
+ for (int i = 0; i < mVibrations.size(); i++) {
+ pw.println(VibrationAttributes.usageToString(mVibrations.keyAt(i)) + ":");
+ pw.increaseIndent();
+ for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+ info.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.println();
}
}
- synchronized void dumpProto(ProtoOutputStream proto) {
- for (int i = 0; i < mPreviousVibrations.size(); i++) {
+ synchronized void dump(ProtoOutputStream proto) {
+ for (int i = 0; i < mVibrations.size(); i++) {
long fieldId;
- switch (mPreviousVibrations.keyAt(i)) {
+ switch (mVibrations.keyAt(i)) {
case VibrationAttributes.USAGE_RINGTONE:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
break;
case VibrationAttributes.USAGE_NOTIFICATION:
- fieldId = VibratorManagerServiceDumpProto
- .PREVIOUS_NOTIFICATION_VIBRATIONS;
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
break;
case VibrationAttributes.USAGE_ALARM:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
@@ -1646,18 +1720,70 @@
default:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
}
- for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
- info.dumpProto(proto, fieldId);
+ for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+ if (info.mLatestVibration.mPlayedEffect == null) {
+ // External vibrations are reported separately in the dump proto
+ info.dump(proto,
+ VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ } else {
+ info.dump(proto, fieldId);
+ }
}
}
+ }
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- info.dumpProto(proto,
- VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ synchronized void dumpOnSingleField(ProtoOutputStream proto, long fieldId) {
+ for (int i = 0; i < mVibrations.size(); i++) {
+ for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+ info.dump(proto, fieldId);
+ }
}
}
}
+ /**
+ * Record that keeps the last {@link Vibration.DebugInfo} played, aggregating close vibrations
+ * from the same uid that have the same {@link VibrationAttributes} and {@link VibrationEffect}.
+ */
+ private static final class AggregatedVibrationRecord {
+ private final Vibration.DebugInfo mFirstVibration;
+ private Vibration.DebugInfo mLatestVibration;
+ private int mVibrationCount;
+
+ AggregatedVibrationRecord(Vibration.DebugInfo info) {
+ mLatestVibration = mFirstVibration = info;
+ mVibrationCount = 1;
+ }
+
+ synchronized boolean mayAggregate(Vibration.DebugInfo info, long timeLimit) {
+ return Objects.equals(mLatestVibration.mCallerInfo.uid, info.mCallerInfo.uid)
+ && Objects.equals(mLatestVibration.mCallerInfo.attrs, info.mCallerInfo.attrs)
+ && Objects.equals(mLatestVibration.mPlayedEffect, info.mPlayedEffect)
+ && Math.abs(mLatestVibration.mCreateTime - info.mCreateTime) < timeLimit;
+ }
+
+ synchronized void record(Vibration.DebugInfo vib) {
+ mLatestVibration = vib;
+ mVibrationCount++;
+ }
+
+ synchronized void dump(IndentingPrintWriter pw) {
+ mFirstVibration.dumpCompact(pw);
+ if (mVibrationCount == 1) {
+ return;
+ }
+ if (mVibrationCount > 2) {
+ pw.println(
+ "-> Skipping " + (mVibrationCount - 2) + " aggregated vibrations, latest:");
+ }
+ mLatestVibration.dumpCompact(pw);
+ }
+
+ synchronized void dump(ProtoOutputStream proto, long fieldId) {
+ mLatestVibration.dump(proto, fieldId);
+ }
+ }
+
/** Clears mNextVibration if set, ending it cleanly */
@GuardedBy("mLock")
private void clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
@@ -1676,7 +1802,7 @@
/**
* Ends the external vibration, and clears related service state.
*
- * @param vibrationEndInfo the status and related info to end the associated Vibration with
+ * @param vibrationEndInfo the status and related info to end the associated Vibration
* @param continueExternalControl indicates whether external control will continue. If not, the
* HAL will have external control turned off.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ea06b42..a7849c1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -226,6 +226,7 @@
import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
@@ -2682,7 +2683,9 @@
private boolean transferSplashScreenIfNeeded() {
if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
|| mStartingWindow == null
- || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
+ || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
+ // skip copy splash screen to client if it was resized
+ || (mStartingData != null && mStartingData.mResizedFromTransfer)) {
return false;
}
if (isTransferringSplashScreen()) {
@@ -7644,7 +7647,8 @@
@Override
void prepareSurfaces() {
final boolean show = isVisible() || isAnimating(PARENTS,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS
+ | ANIMATION_TYPE_PREDICT_BACK);
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 0c196d7..976641b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -26,7 +26,9 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS;
+import static com.android.server.wm.BackNavigationProto.ANIMATION_RUNNING;
import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE;
+import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY;
import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
@@ -50,6 +52,7 @@
import android.window.BackAnimationAdapter;
import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
+import android.window.IWindowlessStartingSurfaceCallback;
import android.window.OnBackInvokedCallbackInfo;
import android.window.TaskSnapshot;
@@ -73,6 +76,8 @@
private @BackNavigationInfo.BackTargetType int mLastBackType;
private boolean mShowWallpaper;
private Runnable mPendingAnimation;
+
+ private boolean mBackAnimationRunning;
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
private AnimationHandler mAnimationHandler;
@@ -474,7 +479,7 @@
final ActivityRecord ar = openApps.valueAt(i);
if (mAnimationHandler.isTarget(ar, true /* open */)) {
openApps.removeAt(i);
- mAnimationHandler.mOpenTransitionTargetMatch = true;
+ mAnimationHandler.markStartingSurfaceMatch();
}
}
for (int i = closeApps.size() - 1; i >= 0; --i) {
@@ -583,8 +588,9 @@
* The closing target should only exist in close list, but the opening target can be either in
* open or close list.
*/
- void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets) {
- if (!isMonitoringTransition()) {
+ void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets,
+ SurfaceControl.Transaction startTransaction) {
+ if (!isMonitoringTransition() || targets.isEmpty()) {
return;
}
for (int i = targets.size() - 1; i >= 0; --i) {
@@ -613,6 +619,17 @@
Slog.e(TAG, "Gesture animation is applied on another transition?");
}
mWaitTransitionFinish = transition;
+ // Flag target matches to defer remove the splash screen.
+ for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = mTmpOpenApps.get(i);
+ if (mAnimationHandler.isTarget(wc, true /* open */)) {
+ mAnimationHandler.markStartingSurfaceMatch();
+ break;
+ }
+ }
+ // Because the target will reparent to transition root, so it cannot be controlled by
+ // animation leash. Hide the close target when transition starts.
+ startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
}
mTmpOpenApps.clear();
mTmpCloseApps.clear();
@@ -633,6 +650,7 @@
mAnimationHandler.clearBackAnimateTarget();
mNavigationMonitor.stopMonitorTransition();
mWaitTransitionFinish = null;
+ mBackAnimationRunning = false;
}
/**
@@ -717,11 +735,7 @@
// This will be set before transition happen, to know whether the real opening target
// exactly match animating target. When target match, reparent the starting surface to
// the opening target like starting window do.
- private boolean mOpenTransitionTargetMatch;
- // The starting surface task Id. Used to clear the starting surface if the animation has
- // request one during animating.
- private int mRequestedStartingSurfaceTaskId;
- private SurfaceControl mStartingSurface;
+ private boolean mStartingSurfaceTargetMatch;
private ActivityRecord mOpenActivity;
AnimationHandler(WindowManagerService wms) {
@@ -765,8 +779,8 @@
return;
}
- mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */);
- mOpenAdaptor = createAdaptor(open, true /* isOpen */);
+ mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType);
+ mOpenAdaptor = createAdaptor(open, true, mSwitchType);
mOpenActivity = openActivity;
if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
@@ -774,8 +788,8 @@
}
}
- boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open,
- ActivityRecord openActivity) {
+ private boolean composeAnimations(@NonNull WindowContainer close,
+ @NonNull WindowContainer open, ActivityRecord openActivity) {
if (mComposed || mWaitTransition) {
Slog.e(TAG, "Previous animation is running " + this);
return false;
@@ -805,28 +819,6 @@
.isSupportWindowlessStartingSurface();
}
- void createStartingSurface(TaskSnapshot snapshot) {
- if (!mComposed) {
- return;
- }
-
- final ActivityRecord topActivity = getTopOpenActivity();
- if (topActivity == null) {
- Slog.e(TAG, "createStartingSurface fail, no open activity: " + this);
- return;
- }
- // TODO (b/257857570) draw snapshot by starting surface.
- }
-
- private ActivityRecord getTopOpenActivity() {
- if (mSwitchType == ACTIVITY_SWITCH) {
- return mOpenAdaptor.mTarget.asActivityRecord();
- } else if (mSwitchType == TASK_SWITCH) {
- return mOpenAdaptor.mTarget.asTask().getTopNonFinishingActivity();
- }
- return null;
- }
-
boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) {
for (int i = wcs.size() - 1; i >= 0; --i) {
if (isTarget(wcs.get(i), open)) {
@@ -860,13 +852,13 @@
if (!mComposed) {
return;
}
- cleanUpWindowlessSurface();
if (mCloseAdaptor != null) {
mCloseAdaptor.mTarget.cancelAnimation();
mCloseAdaptor = null;
}
if (mOpenAdaptor != null) {
+ mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch);
mOpenAdaptor.mTarget.cancelAnimation();
mOpenAdaptor = null;
}
@@ -875,36 +867,16 @@
}
}
- private void cleanUpWindowlessSurface() {
- final ActivityRecord ar = getTopOpenActivity();
- if (ar == null) {
- Slog.w(TAG, "finishPresentAnimations without top activity: " + this);
- }
- final SurfaceControl.Transaction pendingT = ar != null ? ar.getPendingTransaction()
- : mOpenAdaptor.mTarget.getPendingTransaction();
- // ensure open target is visible before cancel animation.
- mOpenTransitionTargetMatch &= ar != null;
- if (mOpenTransitionTargetMatch) {
- pendingT.show(ar.getSurfaceControl());
- }
- if (mRequestedStartingSurfaceTaskId != 0) {
- // If open target match, reparent to open activity
- if (mStartingSurface != null && mOpenTransitionTargetMatch) {
- pendingT.reparent(mStartingSurface, ar.getSurfaceControl());
- }
- // remove starting surface.
- mStartingSurface = null;
- // TODO (b/257857570) draw snapshot by starting surface.
- mRequestedStartingSurfaceTaskId = 0;
- }
+ void markStartingSurfaceMatch() {
+ mStartingSurfaceTargetMatch = true;
+ mOpenAdaptor.reparentWindowlessSurfaceToTarget();
}
void clearBackAnimateTarget() {
finishPresentAnimations();
mComposed = false;
mWaitTransition = false;
- mOpenTransitionTargetMatch = false;
- mRequestedStartingSurfaceTaskId = 0;
+ mStartingSurfaceTargetMatch = false;
mSwitchType = UNKNOWN;
mOpenActivity = null;
}
@@ -935,9 +907,9 @@
}
private static BackWindowAnimationAdaptor createAdaptor(
- WindowContainer target, boolean isOpen) {
+ WindowContainer target, boolean isOpen, int switchType) {
final BackWindowAnimationAdaptor adaptor =
- new BackWindowAnimationAdaptor(target, isOpen);
+ new BackWindowAnimationAdaptor(target, isOpen, switchType);
final SurfaceControl.Transaction pt = target.getPendingTransaction();
target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
// Workaround to show TaskFragment which can be hide in Transitions and won't show
@@ -957,11 +929,19 @@
private final WindowContainer mTarget;
private final boolean mIsOpen;
private RemoteAnimationTarget mAnimationTarget;
+ private final int mSwitchType;
- BackWindowAnimationAdaptor(WindowContainer closeTarget, boolean isOpen) {
- mBounds.set(closeTarget.getBounds());
- mTarget = closeTarget;
+ // The starting surface task Id. Used to clear the starting surface if the animation has
+ // requested one during animating.
+ private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ private SurfaceControl mStartingSurface;
+
+ BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen,
+ int switchType) {
+ mBounds.set(target.getBounds());
+ mTarget = target;
mIsOpen = isOpen;
+ mSwitchType = switchType;
}
@Override
public boolean getShowWallpaper() {
@@ -979,6 +959,8 @@
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mCapturedLeash == animationLeash) {
mCapturedLeash = null;
+ mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ mStartingSurface = null;
}
}
@@ -1009,8 +991,15 @@
return mAnimationTarget;
}
Task t = mTarget.asTask();
- final ActivityRecord r = t != null ? t.getTopNonFinishingActivity()
- : mTarget.asActivityRecord();
+ ActivityRecord r = null;
+ if (t == null && mTarget.asTaskFragment() != null) {
+ t = mTarget.asTaskFragment().getTask();
+ r = mTarget.asTaskFragment().getTopNonFinishingActivity();
+ }
+ if (r == null) {
+ r = t != null ? t.getTopNonFinishingActivity()
+ : mTarget.asActivityRecord();
+ }
if (t == null && r != null) {
t = r.getTask();
}
@@ -1037,6 +1026,77 @@
r.checkEnterPictureInPictureAppOpsState());
return mAnimationTarget;
}
+
+ void createStartingSurface() {
+ if (!mIsOpen) {
+ return;
+ }
+ final Task openTask = mSwitchType == TASK_SWITCH
+ ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH
+ ? mTarget.asActivityRecord().getTask() : null;
+ if (openTask == null) {
+ return;
+ }
+ final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH
+ ? mTarget.asActivityRecord()
+ : openTask.getTopNonFinishingActivity();
+ if (mainActivity == null) {
+ return;
+ }
+ final TaskSnapshot snapshot = getSnapshot(mTarget);
+ mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
+ .addWindowlessStartingSurface(openTask, mainActivity,
+ mAnimationTarget.leash, snapshot,
+ new IWindowlessStartingSurfaceCallback.Stub() {
+ // Once the starting surface has been created in shell, it will call
+ // onSurfaceAdded to pass the created surface to core, so if a
+ // transition is triggered by the back gesture, there doesn't need to
+ // create another starting surface for the opening target, just reparent
+ // the starting surface to the opening target.
+ // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
+ // called, there won't be able to reparent the starting surface on
+ // opening target. But if that happens and transition target is matched,
+ // the app window should already draw.
+ @Override
+ public void onSurfaceAdded(SurfaceControl sc) {
+ synchronized (mTarget.mWmService.mGlobalLock) {
+ if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
+ mStartingSurface = sc;
+ }
+ }
+ }
+ });
+ }
+
+ // When back gesture has triggered and transition target matches navigation target,
+ // reparent the starting surface to the opening target as it's starting window.
+ void reparentWindowlessSurfaceToTarget() {
+ if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+ return;
+ }
+ // If open target matches, reparent to open activity or task
+ if (mStartingSurface != null && mStartingSurface.isValid()) {
+ mTarget.getPendingTransaction()
+ .reparent(mStartingSurface, mTarget.getSurfaceControl());
+ // remove starting surface.
+ mStartingSurface = null;
+ }
+ }
+
+ /**
+ * Ask shell to clear the starting surface.
+ * @param openTransitionMatch if true, shell will play the remove starting window
+ * animation, otherwise remove it directly.
+ */
+ void cleanUpWindowlessSurface(boolean openTransitionMatch) {
+ if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+ return;
+ }
+ mTarget.mWmService.mAtmService.mTaskOrganizerController
+ .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
+ !openTransitionMatch);
+ mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ }
}
ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter,
@@ -1089,15 +1149,13 @@
/**
* Apply preview strategy on the opening target
- * @param open The opening target.
+ * @param openAnimationAdaptor The animator who can create starting surface.
* @param visibleOpenActivity The visible activity in opening target.
- * @return If the preview strategy is launch behind, returns the Activity that has
- * launchBehind set, or null otherwise.
*/
- private void applyPreviewStrategy(WindowContainer open,
+ private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor,
ActivityRecord visibleOpenActivity) {
if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
- createStartingSurface(getSnapshot(open));
+ openAnimationAdaptor.createStartingSurface();
return;
}
setLaunchBehind(visibleOpenActivity);
@@ -1119,7 +1177,7 @@
if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) {
return null;
}
- applyPreviewStrategy(mOpenTarget, openActivity);
+ applyPreviewStrategy(mOpenAdaptor, openActivity);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
final RemoteAnimationTarget[] targets = getAnimationTargets();
@@ -1220,6 +1278,7 @@
if (mPendingAnimation != null) {
mPendingAnimation.run();
mPendingAnimation = null;
+ mBackAnimationRunning = true;
}
}
@@ -1236,9 +1295,6 @@
}
static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
- if (!isScreenshotEnabled()) {
- return null;
- }
if (w.asTask() != null) {
final Task task = w.asTask();
return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
@@ -1247,8 +1303,8 @@
}
if (w.asActivityRecord() != null) {
- // TODO (b/259497289) return TaskSnapshot when feature complete.
- return null;
+ final ActivityRecord ar = w.asActivityRecord();
+ return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar);
}
return null;
}
@@ -1270,6 +1326,12 @@
proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress);
proto.write(LAST_BACK_TYPE, mLastBackType);
proto.write(SHOW_WALLPAPER, mShowWallpaper);
+ if (mAnimationHandler.mOpenActivity != null) {
+ mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY);
+ } else {
+ proto.write(MAIN_OPEN_ACTIVITY, "");
+ }
+ proto.write(ANIMATION_RUNNING, mBackAnimationRunning);
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index cff86ad..2b22d75 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -38,6 +38,10 @@
*/
Task mAssociatedTask;
+
+ /** Whether the starting window is resized from transfer across activities. */
+ boolean mResizedFromTransfer;
+
/** Whether the starting window is drawn. */
boolean mIsDisplayed;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index cdb4ad6..b72d027 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -790,12 +790,8 @@
}
boolean isSupportWindowlessStartingSurface() {
- // Enable after ag/20426257
final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
- if (lastOrganizer == null) {
- return false;
- }
- return false;
+ return lastOrganizer != null;
}
/**
* Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 789e3d2..7a904f8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1499,7 +1499,8 @@
mTargets = calculateTargets(mParticipants, mChanges);
// Check whether the participants were animated from back navigation.
- mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets);
+ mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets,
+ transaction);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
info.setDebugId(mSyncId);
mController.assignTrack(this, info);
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 26aab07..726ae5c 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -76,7 +76,7 @@
@NonNull IBinder clientToken, @NonNull WindowContainer<?> container,
@WindowType int type, @Nullable Bundle options) {
registerWindowContainerListener(wpc, clientToken, container, type, options,
- true /* shouDispatchConfigWhenRegistering */);
+ true /* shouldDispatchConfigWhenRegistering */);
}
/**
@@ -91,19 +91,19 @@
* @param container the {@link WindowContainer} which the listener is going to listen to.
* @param type the window type
* @param options a bundle used to pass window-related options.
- * @param shouDispatchConfigWhenRegistering {@code true} to indicate the current
+ * @param shouldDispatchConfigWhenRegistering {@code true} to indicate the current
* {@code container}'s config will dispatch to the client side when
* registering the {@link WindowContextListenerImpl}
*/
void registerWindowContainerListener(@NonNull WindowProcessController wpc,
@NonNull IBinder clientToken, @NonNull WindowContainer<?> container,
@WindowType int type, @Nullable Bundle options,
- boolean shouDispatchConfigWhenRegistering) {
+ boolean shouldDispatchConfigWhenRegistering) {
WindowContextListenerImpl listener = mListeners.get(clientToken);
if (listener == null) {
listener = new WindowContextListenerImpl(wpc, clientToken, container, type,
options);
- listener.register(shouDispatchConfigWhenRegistering);
+ listener.register(shouldDispatchConfigWhenRegistering);
} else {
updateContainerForWindowContextListener(clientToken, container);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 210378f..261d6bc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -305,6 +305,7 @@
import android.window.ScreenCapture;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
+import android.window.WindowContextInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -2737,7 +2738,7 @@
@Nullable
@Override
- public Configuration attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
+ public WindowContextInfo attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
@NonNull IBinder clientToken, @LayoutParams.WindowType int type, int displayId,
@Nullable Bundle options) {
Objects.requireNonNull(appThread);
@@ -2766,8 +2767,8 @@
final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */);
mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
- da, type, options, false /* shouDispatchConfigWhenRegistering */);
- return da.getConfiguration();
+ da, type, options, false /* shouldDispatchConfigWhenRegistering */);
+ return new WindowContextInfo(da.getConfiguration(), displayId);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2776,8 +2777,8 @@
@Nullable
@Override
- public Configuration attachWindowContextToDisplayContent(@NonNull IApplicationThread appThread,
- @NonNull IBinder clientToken, int displayId) {
+ public WindowContextInfo attachWindowContextToDisplayContent(
+ @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
Objects.requireNonNull(appThread);
Objects.requireNonNull(clientToken);
final int callingPid = Binder.getCallingPid();
@@ -2809,16 +2810,17 @@
mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
dc, INVALID_WINDOW_TYPE, null /* options */,
- false /* shouDispatchConfigWhenRegistering */);
- return dc.getConfiguration();
+ false /* shouldDispatchConfigWhenRegistering */);
+ return new WindowContextInfo(dc.getConfiguration(), displayId);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
+ @Nullable
@Override
- public void attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
+ public WindowContextInfo attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
@NonNull IBinder clientToken, @NonNull IBinder token) {
Objects.requireNonNull(appThread);
Objects.requireNonNull(clientToken);
@@ -2834,13 +2836,13 @@
if (wpc == null) {
ProtoLog.w(WM_ERROR, "attachWindowContextToWindowToken: calling from"
+ " non-existing process pid=%d uid=%d", callingPid, callingUid);
- return;
+ return null;
}
final WindowToken windowToken = mRoot.getWindowToken(token);
if (windowToken == null) {
ProtoLog.w(WM_ERROR, "Then token:%s is invalid. It might be "
+ "removed", token);
- return;
+ return null;
}
final int type = mWindowContextListenerController.getWindowType(clientToken);
if (type == INVALID_WINDOW_TYPE) {
@@ -2854,10 +2856,13 @@
}
if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken,
callerCanManageAppTokens, callingUid)) {
- return;
+ return null;
}
mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
- windowToken, windowToken.windowType, windowToken.mOptions);
+ windowToken, windowToken.windowType, windowToken.mOptions,
+ false /* shouldDispatchConfigWhenRegistering */);
+ return new WindowContextInfo(windowToken.getConfiguration(),
+ windowToken.getDisplayContent().getDisplayId());
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index baf97b7..6d0fd2a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1935,7 +1935,9 @@
}
final ActivityRecord atoken = mActivityRecord;
if (atoken != null) {
- return ((!isParentWindowHidden() && atoken.isVisible())
+ final boolean isVisible = isStartingWindowAssociatedToTask()
+ ? mStartingData.mAssociatedTask.isVisible() : atoken.isVisible();
+ return ((!isParentWindowHidden() && isVisible)
|| isAnimationRunningSelfOrParent());
}
final WallpaperWindowToken wtoken = mToken.asWallpaperToken();
@@ -2330,6 +2332,13 @@
// IME surface association. (e.g. Attach IME surface on the display instead of the
// app when the app bounds being letterboxed.)
mDisplayContent.updateImeControlTarget(isImeLayeringTarget() /* updateImeParent */);
+ // Fix the starting window to task when Activity has changed.
+ if (mStartingData != null && mStartingData.mAssociatedTask == null
+ && !mTempConfiguration.windowConfiguration.getBounds().equals(getBounds())) {
+ mStartingData.mResizedFromTransfer = true;
+ // Lock the starting window to task, so it won't resize from transfer anymore.
+ mActivityRecord.associateStartingWindowWithTaskIfNeeded();
+ }
}
}
@@ -3907,7 +3916,7 @@
* LetterboxUiController#shouldShowLetterboxUi} for more context.
*/
boolean areAppWindowBoundsLetterboxed() {
- return mActivityRecord != null
+ return mActivityRecord != null && !isStartingWindowAssociatedToTask()
&& (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
}
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 17474fb..6a349e2 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -26,6 +26,7 @@
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.IndexedMap
import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.DevicePermissionPolicy
import com.android.server.permission.access.util.attributeInt
import com.android.server.permission.access.util.attributeInterned
import com.android.server.permission.access.util.forEachTag
@@ -46,6 +47,7 @@
getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
}
addPolicy(AppIdPermissionPolicy())
+ addPolicy(DevicePermissionPolicy())
addPolicy(AppIdAppOpPolicy())
addPolicy(PackageAppOpPolicy())
} as IndexedMap<String, IndexedMap<String, SchemePolicy>>
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 4ec32ea..94c878a 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -329,6 +329,18 @@
private typealias AppIdPermissionFlagsReference =
MutableReference<AppIdPermissionFlags, MutableAppIdPermissionFlags>
+
+typealias DevicePermissionFlags =
+ IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias MutableDevicePermissionFlags =
+ MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias AppIdDevicePermissionFlags =
+ IntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+typealias MutableAppIdDevicePermissionFlags =
+ MutableIntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+private typealias AppIdDevicePermissionFlagsReference =
+ MutableReference<AppIdDevicePermissionFlags, MutableAppIdDevicePermissionFlags>
+
typealias AppIdAppOpModes =
IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
typealias MutableAppIdAppOpModes =
@@ -346,6 +358,7 @@
sealed class UserState(
internal val packageVersionsReference: PackageVersionsReference,
internal val appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
+ internal val appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
internal val appIdAppOpModesReference: AppIdAppOpModesReference,
internal val packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -357,6 +370,9 @@
val appIdPermissionFlags: AppIdPermissionFlags
get() = appIdPermissionFlagsReference.get()
+ val appIdDevicePermissionFlags: AppIdDevicePermissionFlags
+ get() = appIdDevicePermissionFlagsReference.get()
+
val appIdAppOpModes: AppIdAppOpModes
get() = appIdAppOpModesReference.get()
@@ -375,6 +391,7 @@
class MutableUserState private constructor(
packageVersionsReference: PackageVersionsReference,
appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
+ appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
appIdAppOpModesReference: AppIdAppOpModesReference,
packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -382,6 +399,7 @@
) : UserState(
packageVersionsReference,
appIdPermissionFlagsReference,
+ appIdDevicePermissionFlagsReference,
appIdAppOpModesReference,
packageAppOpModesReference,
defaultPermissionGrantFingerprint,
@@ -390,6 +408,7 @@
constructor() : this(
PackageVersionsReference(MutableIndexedMap<String, Int>()),
AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
+ AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
AppIdAppOpModesReference(MutableAppIdAppOpModes()),
PackageAppOpModesReference(MutablePackageAppOpModes()),
null,
@@ -399,6 +418,7 @@
internal constructor(userState: UserState) : this(
userState.packageVersionsReference.toImmutable(),
userState.appIdPermissionFlagsReference.toImmutable(),
+ userState.appIdDevicePermissionFlagsReference.toImmutable(),
userState.appIdAppOpModesReference.toImmutable(),
userState.packageAppOpModesReference.toImmutable(),
userState.defaultPermissionGrantFingerprint,
@@ -410,6 +430,9 @@
fun mutateAppIdPermissionFlags(): MutableAppIdPermissionFlags =
appIdPermissionFlagsReference.mutate()
+ fun mutateAppIdDevicePermissionFlags(): MutableAppIdDevicePermissionFlags =
+ appIdDevicePermissionFlagsReference.mutate()
+
fun mutateAppIdAppOpModes(): MutableAppIdAppOpModes = appIdAppOpModesReference.mutate()
fun mutatePackageAppOpModes(): MutablePackageAppOpModes = packageAppOpModesReference.mutate()
diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt
index d1abc04..1d46ca7 100644
--- a/services/permission/java/com/android/server/permission/access/AccessUri.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt
@@ -65,6 +65,17 @@
}
}
+data class DevicePermissionUri(
+ val permissionName: String,
+ val deviceId: Int
+) : AccessUri(SCHEME) {
+ override fun toString(): String = "$scheme:///$permissionName/$deviceId"
+
+ companion object {
+ const val SCHEME = "device-permission"
+ }
+}
+
data class UidUri(
val uid: Int
) : AccessUri(SCHEME) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
new file mode 100644
index 0000000..37a4a90
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.permission
+
+import android.util.Slog
+import com.android.modules.utils.BinaryXmlPullParser
+import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.DevicePermissionFlags
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutableAppIdDevicePermissionFlags
+import com.android.server.permission.access.MutableDevicePermissionFlags
+import com.android.server.permission.access.WriteMode
+import com.android.server.permission.access.immutable.IndexedMap
+import com.android.server.permission.access.immutable.MutableIndexedMap
+import com.android.server.permission.access.immutable.forEachIndexed
+import com.android.server.permission.access.immutable.forEachReversedIndexed
+import com.android.server.permission.access.immutable.set
+import com.android.server.permission.access.util.andInv
+import com.android.server.permission.access.util.attributeInt
+import com.android.server.permission.access.util.attributeInterned
+import com.android.server.permission.access.util.forEachTag
+import com.android.server.permission.access.util.getAttributeIntOrThrow
+import com.android.server.permission.access.util.getAttributeValueOrThrow
+import com.android.server.permission.access.util.hasBits
+import com.android.server.permission.access.util.tag
+import com.android.server.permission.access.util.tagName
+
+class DevicePermissionPersistence {
+ fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
+ when (tagName) {
+ TAG_APP_ID_DEVICE_PERMISSIONS -> parseAppIdDevicePermissions(state, userId)
+ else -> {}
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseAppIdDevicePermissions(
+ state: MutableAccessState,
+ userId: Int
+ ) {
+ val userState = state.mutateUserState(userId, WriteMode.NONE)!!
+ val appIdDevicePermissionFlags = userState.mutateAppIdDevicePermissionFlags()
+ forEachTag {
+ when (tagName) {
+ TAG_APP_ID -> parseAppId(appIdDevicePermissionFlags)
+ else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+
+ appIdDevicePermissionFlags.forEachReversedIndexed { appIdIndex, appId, _ ->
+ if (appId !in state.externalState.appIdPackageNames) {
+ Slog.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
+ appIdDevicePermissionFlags.removeAt(appIdIndex)
+ userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseAppId(
+ appIdPermissionFlags: MutableAppIdDevicePermissionFlags
+ ) {
+ val appId = getAttributeIntOrThrow(ATTR_ID)
+ val devicePermissionFlags = MutableDevicePermissionFlags()
+ appIdPermissionFlags[appId] = devicePermissionFlags
+ forEachTag {
+ when (tagName) {
+ TAG_DEVICE -> parseDevice(devicePermissionFlags)
+ else -> {
+ Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseDevice(
+ deviceIdPermissionFlags: MutableDevicePermissionFlags
+ ) {
+ val deviceId = getAttributeValueOrThrow(ATTR_ID)
+ val permissionFlags = MutableIndexedMap<String, Int>()
+ deviceIdPermissionFlags.put(deviceId, permissionFlags)
+ forEachTag {
+ when (tagName) {
+ TAG_PERMISSION -> parsePermission(permissionFlags)
+ else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parsePermission(
+ permissionFlags: MutableIndexedMap<String, Int>
+ ) {
+ val name = getAttributeValueOrThrow(ATTR_NAME).intern()
+ val flags = getAttributeIntOrThrow(ATTR_FLAGS)
+ permissionFlags[name] = flags
+ }
+
+ fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ val appIdDevicePermissionFlags = state.userStates[userId]!!.appIdDevicePermissionFlags
+ tag(TAG_APP_ID_DEVICE_PERMISSIONS) {
+ appIdDevicePermissionFlags.forEachIndexed { _, appId, devicePermissionFlags ->
+ serializeAppId(appId, devicePermissionFlags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeAppId(
+ appId: Int,
+ devicePermissionFlags: DevicePermissionFlags
+ ) {
+ tag(TAG_APP_ID) {
+ attributeInt(ATTR_ID, appId)
+ devicePermissionFlags.forEachIndexed { _, deviceId, permissionFlags ->
+ serializeDevice(deviceId, permissionFlags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeDevice(
+ deviceId: String,
+ permissionFlags: IndexedMap<String, Int>
+ ) {
+ tag(TAG_DEVICE) {
+ attributeInterned(ATTR_ID, deviceId)
+ permissionFlags.forEachIndexed { _, name, flags ->
+ serializePermission(name, flags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializePermission(name: String, flags: Int) {
+ tag(TAG_PERMISSION) {
+ attributeInterned(ATTR_NAME, name)
+ // Never serialize one-time permissions as granted.
+ val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+ flags andInv PermissionFlags.RUNTIME_GRANTED
+ } else {
+ flags
+ }
+ attributeInt(ATTR_FLAGS, serializedFlags)
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = DevicePermissionPersistence::class.java.simpleName
+
+ private const val TAG_APP_ID_DEVICE_PERMISSIONS = "app-id-device-permissions"
+ private const val TAG_APP_ID = "app-id"
+ private const val TAG_DEVICE = "device"
+ private const val TAG_PERMISSION = "permission"
+
+ private const val ATTR_ID = "id"
+ private const val ATTR_NAME = "name"
+ private const val ATTR_FLAGS = "flags"
+ }
+}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
new file mode 100644
index 0000000..c0d7546
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.permission
+
+import android.util.Slog
+import com.android.modules.utils.BinaryXmlPullParser
+import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.DevicePermissionUri
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.SchemePolicy
+import com.android.server.permission.access.UidUri
+import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.util.andInv
+import com.android.server.pm.pkg.PackageState
+
+class DevicePermissionPolicy : SchemePolicy() {
+ private val persistence = DevicePermissionPersistence()
+
+ @Volatile
+ private var listeners: IndexedListSet<OnDevicePermissionFlagsChangedListener> =
+ MutableIndexedListSet()
+ private val listenersLock = Any()
+
+ override val subjectScheme: String
+ get() = UidUri.SCHEME
+
+ override val objectScheme: String
+ get() = DevicePermissionUri.SCHEME
+
+ override fun GetStateScope.onStateMutated() {
+ listeners.forEachIndexed { _, it -> it.onStateMutated() }
+ }
+
+ override fun MutateStateScope.onAppIdRemoved(appId: Int) {
+ newState.userStates.forEachIndexed { userStateIndex, _, userState ->
+ if (appId in userState.appIdDevicePermissionFlags) {
+ newState.mutateUserStateAt(userStateIndex)
+ .mutateAppIdDevicePermissionFlags() -= appId
+ }
+ }
+ }
+
+ override fun MutateStateScope.onStorageVolumeMounted(
+ volumeUuid: String?,
+ packageNames: List<String>,
+ isSystemUpdated: Boolean
+ ) {
+ packageNames.forEachIndexed { _, packageName ->
+ val packageState = newState.externalState.packageStates[packageName]!!
+ trimPermissionStates(packageState.appId)
+ }
+ }
+
+ override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
+ trimPermissionStates(packageState.appId)
+ }
+
+ override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
+ if (appId in newState.externalState.appIdPackageNames) {
+ trimPermissionStates(appId)
+ }
+ }
+
+ override fun MutateStateScope.onPackageUninstalled(
+ packageName: String,
+ appId: Int,
+ userId: Int
+ ) {
+ resetPermissionStates(packageName, userId)
+ }
+
+ private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
+ // It's okay to skip resetting permissions for packages that are removed,
+ // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
+ val packageState = newState.externalState.packageStates[packageName] ?: return
+ val androidPackage = packageState.androidPackage ?: return
+ val appId = packageState.appId
+ val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
+ androidPackage.requestedPermissions.forEach { permissionName ->
+ val isRequestedByOtherPackages = anyPackageInAppId(appId) {
+ it.packageName != packageName &&
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
+ if (isRequestedByOtherPackages) {
+ return@forEach
+ }
+ appIdPermissionFlags[appId]?.forEachIndexed { _, deviceId, _ ->
+ setPermissionFlags(appId, deviceId, userId, permissionName, 0)
+ }
+ }
+ }
+
+ private fun MutateStateScope.trimPermissionStates(appId: Int) {
+ val requestedPermissions = MutableIndexedSet<String>()
+ forEachPackageInAppId(appId) {
+ requestedPermissions += it.androidPackage!!.requestedPermissions
+ }
+ newState.userStates.forEachIndexed { _, userId, userState ->
+ userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
+ _, deviceId, permissionFlags ->
+ permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
+ if (permissionName !in requestedPermissions) {
+ setPermissionFlags(appId, deviceId, userId, permissionName, 0)
+ }
+ }
+ }
+ }
+ }
+
+ private inline fun MutateStateScope.anyPackageInAppId(
+ appId: Int,
+ state: AccessState = newState,
+ predicate: (PackageState) -> Boolean
+ ): Boolean {
+ val packageNames = state.externalState.appIdPackageNames[appId]!!
+ return packageNames.anyIndexed { _, packageName ->
+ val packageState = state.externalState.packageStates[packageName]!!
+ packageState.androidPackage != null && predicate(packageState)
+ }
+ }
+
+ private inline fun MutateStateScope.forEachPackageInAppId(
+ appId: Int,
+ state: AccessState = newState,
+ action: (PackageState) -> Unit
+ ) {
+ val packageNames = state.externalState.appIdPackageNames[appId]!!
+ packageNames.forEachIndexed { _, packageName ->
+ val packageState = state.externalState.packageStates[packageName]!!
+ if (packageState.androidPackage != null) {
+ action(packageState)
+ }
+ }
+ }
+
+ override fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
+ with(persistence) { this@parseUserState.parseUserState(state, userId) }
+ }
+
+ override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
+ }
+
+ fun GetStateScope.getPermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String
+ ): Int =
+ state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
+ ?.getWithDefault(permissionName, 0) ?: 0
+
+ fun MutateStateScope.setPermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String,
+ flags: Int
+ ): Boolean =
+ updatePermissionFlags(
+ appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
+ )
+
+ private fun MutateStateScope.updatePermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String,
+ flagMask: Int,
+ flagValues: Int
+ ): Boolean {
+ if (!isDeviceAwarePermission(permissionName)) {
+ Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
+ return false
+ }
+ val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
+ ?.get(deviceId).getWithDefault(permissionName, 0)
+ val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
+ if (oldFlags == newFlags) {
+ return false
+ }
+ val appIdDevicePermissionFlags =
+ newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
+ val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
+ MutableIndexedReferenceMap()
+ }
+ val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
+ permissionFlags.putWithDefault(permissionName, newFlags, 0)
+ if (permissionFlags.isEmpty()) {
+ devicePermissionFlags -= deviceId
+ if (devicePermissionFlags.isEmpty()) {
+ appIdDevicePermissionFlags -= appId
+ }
+ }
+ listeners.forEachIndexed { _, it ->
+ it.onDevicePermissionFlagsChanged(
+ appId, userId, deviceId, permissionName, oldFlags, newFlags
+ )
+ }
+ return true
+ }
+
+ fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
+ synchronized(listenersLock) {
+ listeners = listeners + listener
+ }
+ }
+
+ fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
+ synchronized(listenersLock) {
+ listeners = listeners - listener
+ }
+ }
+
+ private fun isDeviceAwarePermission(permissionName: String): Boolean =
+ DEVICE_SUPPORTED_PERMISSIONS.contains(permissionName)
+
+ companion object {
+ private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
+
+ /**
+ * These permissions are supported for virtual devices.
+ */
+ private val DEVICE_SUPPORTED_PERMISSIONS = indexedSetOf(
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.RECORD_AUDIO
+ )
+ }
+
+ /**
+ * TODO: b/289355341 - implement listener for permission changes
+ * Listener for permission flags changes.
+ */
+ abstract class OnDevicePermissionFlagsChangedListener {
+ /**
+ * Called when a permission flags change has been made to the upcoming new state.
+ *
+ * Implementations should keep this method fast to avoid stalling the locked state mutation,
+ * and only call external code after [onStateMutated] when the new state has actually become
+ * the current state visible to external code.
+ */
+ abstract fun onDevicePermissionFlagsChanged(
+ appId: Int,
+ userId: Int,
+ deviceId: String,
+ permissionName: String,
+ oldFlags: Int,
+ newFlags: Int
+ )
+
+ /**
+ * Called when the upcoming new state has become the current state.
+ *
+ * Implementations should keep this method fast to avoid stalling the locked state mutation.
+ */
+ abstract fun onStateMutated()
+ }
+}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index edacf188..d9f179a 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -64,9 +64,11 @@
import com.android.server.PermissionThread
import com.android.server.ServiceThread
import com.android.server.SystemConfig
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.AccessState
import com.android.server.permission.access.AppOpUri
+import com.android.server.permission.access.DevicePermissionUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
@@ -110,6 +112,9 @@
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
+ private val devicePolicy =
+ service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
+
private val context = service.context
private lateinit var metricsLogger: MetricsLogger
private lateinit var packageManagerInternal: PackageManagerInternal
@@ -132,6 +137,8 @@
private lateinit var permissionControllerManager: PermissionControllerManager
+ private lateinit var virtualDeviceManagerInternal: VirtualDeviceManagerInternal
+
/**
* A permission backup might contain apps that are not installed. In this case we delay the
* restoration until the app is installed.
@@ -152,6 +159,8 @@
systemConfig = SystemConfig.getInstance()
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
+ virtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal::class.java)
// The package info cache is the cache for package and permission information.
// Disable the package info and package permission caches locally but leave the
@@ -460,7 +469,7 @@
return size
}
- override fun checkUidPermission(uid: Int, permissionName: String): Int {
+ override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
@@ -482,7 +491,7 @@
return PackageManager.PERMISSION_DENIED
}
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName)
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -515,7 +524,12 @@
return false
}
- override fun checkPermission(packageName: String, permissionName: String, userId: Int): Int {
+ override fun checkPermission(
+ packageName: String,
+ permissionName: String,
+ deviceId: Int,
+ userId: Int
+ ): Int {
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
}
@@ -524,7 +538,7 @@
.use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName)
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -542,19 +556,21 @@
private fun GetStateScope.isPermissionGranted(
packageState: PackageState,
userId: Int,
- permissionName: String
+ permissionName: String,
+ deviceId: Int
): Boolean {
val appId = packageState.appId
// Note that instant apps can't have shared UIDs, so we only need to check the current
// package state.
val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
- if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName)) {
+ if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) {
return true
}
val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
if (fullerPermissionName != null &&
- isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName)) {
+ isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
+ ) {
return true
}
@@ -568,9 +584,10 @@
appId: Int,
userId: Int,
isInstantApp: Boolean,
- permissionName: String
+ permissionName: String,
+ deviceId: Int,
): Boolean {
- val flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!PermissionFlags.isPermissionGranted(flags)) {
return false
}
@@ -601,7 +618,8 @@
?: return emptySet()
return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
- if (isPermissionGranted(packageState, userId, permissionName)) {
+ if (isPermissionGranted(
+ packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
permissionName
} else {
null
@@ -640,18 +658,26 @@
}
}
- override fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) {
- setRuntimePermissionGranted(packageName, userId, permissionName, isGranted = true)
+ override fun grantRuntimePermission(
+ packageName: String,
+ permissionName: String,
+ deviceId: Int,
+ userId: Int
+ ) {
+ setRuntimePermissionGranted(
+ packageName, userId, permissionName, deviceId, isGranted = true
+ )
}
override fun revokeRuntimePermission(
packageName: String,
permissionName: String,
+ deviceId: Int,
userId: Int,
reason: String?
) {
setRuntimePermissionGranted(
- packageName, userId, permissionName, isGranted = false, revokeReason = reason
+ packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
)
}
@@ -660,8 +686,8 @@
userId: Int
) {
setRuntimePermissionGranted(
- packageName, userId, Manifest.permission.POST_NOTIFICATIONS, isGranted = false,
- skipKillUid = true
+ packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
+ isGranted = false, skipKillUid = true
)
}
@@ -673,6 +699,7 @@
packageName: String,
userId: Int,
permissionName: String,
+ deviceId: Int,
isGranted: Boolean,
skipKillUid: Boolean = false,
revokeReason: String? = null
@@ -748,7 +775,7 @@
}
setRuntimePermissionGranted(
- packageState, userId, permissionName, isGranted, canManageRolePermission,
+ packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
overridePolicyFixed, reportError = true, methodName
)
}
@@ -782,14 +809,16 @@
if (permissionState ==
PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
setRuntimePermissionGranted(
- packageState, userId, permissionName, isGranted = true,
- canManageRolePermission = false, overridePolicyFixed = false,
- reportError = false, "setRequestedPermissionStates"
+ packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
+ isGranted = true, canManageRolePermission = false,
+ overridePolicyFixed = false, reportError = false,
+ "setRequestedPermissionStates"
)
updatePermissionFlags(
packageState.appId, userId, permissionName,
+ Context.DEVICE_ID_DEFAULT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
canUpdateSystemFlags = false,
reportErrorForUnknownPermission = false,
isPermissionRequested = true, "setRequestedPermissionStates",
@@ -816,6 +845,7 @@
packageState: PackageState,
userId: Int,
permissionName: String,
+ deviceId: Int,
isGranted: Boolean,
canManageRolePermission: Boolean,
overridePolicyFixed: Boolean,
@@ -871,7 +901,7 @@
}
val appId = packageState.appId
- val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
if (reportError) {
@@ -934,7 +964,7 @@
return
}
- with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+ setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
if (permission.isRuntime) {
val action = if (isGranted) {
@@ -963,7 +993,12 @@
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
}
- override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
+ override fun getPermissionFlags(
+ packageName: String,
+ permissionName: String,
+ deviceId: Int,
+ userId: Int,
+ ): Int {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "getPermissionFlags: Unknown user $userId")
return 0
@@ -994,7 +1029,8 @@
}
val flags =
- with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
+
return PermissionFlags.toApiFlags(flags)
}
}
@@ -1002,6 +1038,7 @@
override fun isPermissionRevokedByPolicy(
packageName: String,
permissionName: String,
+ deviceId: Int,
userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
@@ -1018,13 +1055,13 @@
.use { it.getPackageState(packageName) } ?: return false
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName)) {
+ if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
return false
}
- val flags = with(policy) {
- getPermissionFlags(packageState.appId, userId, permissionName)
- }
+ val flags =
+ getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
+
return flags.hasBits(PermissionFlags.POLICY_FIXED)
}
}
@@ -1046,7 +1083,8 @@
override fun shouldShowRequestPermissionRationale(
packageName: String,
permissionName: String,
- userId: Int
+ deviceId: Int,
+ userId: Int,
): Boolean {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId")
@@ -1068,11 +1106,11 @@
val flags: Int
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName)) {
+ if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
return false
}
- flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
}
if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
return false
@@ -1104,6 +1142,7 @@
flagMask: Int,
flagValues: Int,
enforceAdjustPolicyPermission: Boolean,
+ deviceId: Int,
userId: Int
) {
val callingUid = Binder.getCallingUid()
@@ -1199,7 +1238,7 @@
val appId = packageState.appId
service.mutateState {
updatePermissionFlags(
- appId, userId, permissionName, flagMask, flagValues, canUpdateSystemFlags,
+ appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
reportErrorForUnknownPermission = true, isPermissionRequested,
"updatePermissionFlags", packageName
)
@@ -1248,8 +1287,9 @@
val androidPackage = packageState.androidPackage ?: return@forEach
androidPackage.requestedPermissions.forEach { permissionName ->
updatePermissionFlags(
- packageState.appId, userId, permissionName, flagMask, flagValues,
- canUpdateSystemFlags, reportErrorForUnknownPermission = false,
+ packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
+ flagMask, flagValues, canUpdateSystemFlags,
+ reportErrorForUnknownPermission = false,
isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
)
}
@@ -1264,6 +1304,7 @@
appId: Int,
userId: Int,
permissionName: String,
+ deviceId: Int,
flagMask: Int,
flagValues: Int,
canUpdateSystemFlags: Boolean,
@@ -1298,7 +1339,7 @@
return
}
- val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
@@ -1308,7 +1349,7 @@
}
val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
- with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+ setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
}
override fun getAllowlistedRestrictedPermissions(
@@ -1365,6 +1406,49 @@
)
}
+ private fun GetStateScope.getPermissionFlagsWithPolicy(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ deviceId: Int,
+ ): Int =
+ if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ } else {
+ val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ if (persistentDeviceId != null) {
+ with(devicePolicy) {
+ getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
+ }
+ } else {
+ Slog.e(LOG_TAG, "Invalid deviceId $deviceId, no persistent device ID found.")
+ 0
+ }
+ }
+
+ private fun MutateStateScope.setPermissionFlagsWithPolicy(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ deviceId: Int,
+ flags: Int
+ ): Boolean =
+ if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ with(policy) {
+ setPermissionFlags(appId, userId, permissionName, flags)
+ }
+ } else {
+ val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ if (persistentDeviceId != null) {
+ with(devicePolicy) {
+ setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
+ }
+ } else {
+ Slog.e(LOG_TAG, "Invalid deviceId $deviceId, no cdm association found.")
+ false
+ }
+ }
+
/**
* This method does not enforce checks on the caller, should only be called after
* required checks.
@@ -1539,8 +1623,7 @@
) {
service.mutateState {
with(policy) {
- val permissionsFlags =
- getUidPermissionFlags(appId, userId) ?: return@mutateState
+ val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
val permissions = getPermissions()
androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
@@ -1661,8 +1744,6 @@
)
}
-
-
override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
requireNotNull(permissionName) { "permissionName cannot be null" }
val packageNames = ArraySet<String>()
@@ -1879,7 +1960,7 @@
println("Permissions:")
withIndent {
userState.appIdPermissionFlags[appId]?.forEachIndexed {
- _, permissionName, flags ->
+ _, permissionName, flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
println(
"$permissionName: granted=$isGranted, flags=" +
@@ -1888,6 +1969,20 @@
}
}
+ userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
+ _, deviceId, devicePermissionFlags ->
+ println("Permissions (Device $deviceId):")
+ withIndent {
+ devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
+ val isGranted = PermissionFlags.isPermissionGranted(flags)
+ println(
+ "$permissionName: granted=$isGranted, flags=" +
+ PermissionFlags.toString(flags)
+ )
+ }
+ }
+ }
+
println("App ops:")
withIndent {
userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
@@ -2412,7 +2507,7 @@
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
- if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
+ if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
PackageManager.PERMISSION_GRANTED) {
return false
}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 35b9bc3..4a8d73d2 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -254,12 +254,45 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- return userState.getCustomPrinterIcon(printerId);
+ Icon icon = userState.getCustomPrinterIcon(printerId);
+ return validateIconUserBoundary(icon);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+ /**
+ * Validates the custom printer icon to see if it's not in the calling user space.
+ * If the condition is not met, return null. Otherwise, return the original icon.
+ *
+ * @param icon
+ * @return icon (validated)
+ */
+ private Icon validateIconUserBoundary(Icon icon) {
+ // Refer to Icon#getUriString for context. The URI string is invalid for icons of
+ // incompatible types.
+ if (icon != null && (icon.getType() == Icon.TYPE_URI
+ || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
+ String encodedUser = icon.getUri().getEncodedUserInfo();
+
+ // If there is no encoded user, the URI is calling into the calling user space
+ if (encodedUser != null) {
+ int userId = Integer.parseInt(encodedUser);
+ // resolve encoded user
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+
+ synchronized (mLock) {
+ // Only the current group members can get the printer icons.
+ if (resolveCallingProfileParentLocked(resolvedUserId)
+ != getCurrentUserId()) {
+ return null;
+ }
+ }
+ }
+ }
+ return icon;
+ }
+
@Override
public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
if (printJobId == null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index cd3a78e..6906dec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -2157,6 +2157,14 @@
}
@Test
+ public void testResetGamePowerMode() {
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ gameManagerService.onBootCompleted();
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, false);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+ }
+
+ @Test
public void testNotifyGraphicsEnvironmentSetup() {
String configString = "mode=2,loadingBoost=2000";
when(DeviceConfig.getProperty(anyString(), anyString()))
diff --git a/services/tests/servicestests/res/raw-watch/a11y_three_finger_swipe_down_gesture.log b/services/tests/servicestests/res/raw-watch/a11y_three_finger_swipe_down_gesture.log
new file mode 100644
index 0000000..ee331c2
--- /dev/null
+++ b/services/tests/servicestests/res/raw-watch/a11y_three_finger_swipe_down_gesture.log
@@ -0,0 +1,29 @@
+ * Gesture6_id30:Swipe down with 3 finger
+MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=5273700, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_DOWN(1), actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=214.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=2, historySize=0, eventTime=5273700, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=214.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=2, historySize=0, eventTime=5273709, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_DOWN(2), actionButton=0, id[0]=0, x[0]=150.0, y[0]=696.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=214.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=100.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273709, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=100.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273715, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=60.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=80.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=110.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273725, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=70.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=90.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=120.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273734, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=80.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=100.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=130.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273741, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=90.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=110.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=140.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273750, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=100.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=120.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=150.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273758, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=110.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=130.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=160.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273767, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=120.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=140.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=170.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273776, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=130.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=150.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=180.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273784, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=140.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=160.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=190.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273793, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=150.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=170.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=200.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273801, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=160.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=180.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=210.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273810, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=170.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=190.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=220.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273822, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=180.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=200.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=230.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273831, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=190.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=210.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=240.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273836, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=200.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=220.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=250.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273844, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=210.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=230.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=260.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273853, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=220.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=240.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=270.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273862, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=230.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=250.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=280.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273870, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=240.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=260.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=290.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273879, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=250.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=270.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=300.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273888, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_UP(0), actionButton=0, id[0]=0, x[0]=150.0, y[0]=250.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=270.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=300.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273895, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_UP(0), actionButton=0, id[0]=1, x[0]=216.0, y[0]=270.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=2, x[1]=94.0, y[1]=300.0, toolType[1]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=2, historySize=0, eventTime=5273895, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_UP, actionButton=0, id[0]=2, x[0]=94.0, y[0]=300.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=5273895, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index cc1100b..e5909a4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -36,6 +36,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -297,13 +298,15 @@
TestData.getInsecureCertPathForEndpoint1());
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
+ setExpectedScryptArgument(password.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PASSWORD);
- verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
KeyDerivationParams keyDerivationParams =
@@ -314,6 +317,44 @@
}
@Test
+ public void run_zeroizedCredential() throws Exception {
+ String password = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "123";
+ String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias";
+ byte[] zeroizedCredential = password.getBytes();
+ mKeySyncTask = new KeySyncTask(
+ mRecoverableKeyStoreDb,
+ mRecoverySnapshotStorage,
+ mSnapshotListenersStorage,
+ TEST_USER_ID,
+ CREDENTIAL_TYPE_PASSWORD,
+ /*credential=*/ zeroizedCredential,
+ /*credentialUpdated=*/ false,
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper,
+ mMockScrypt);
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+ mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
+ TestData.getInsecureCertPathForEndpoint1());
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
+
+ // Need to check array value during method call since it is modified later.
+ setExpectedScryptArgument(password.getBytes());
+
+ Arrays.fill(zeroizedCredential, (byte) 0);
+ mKeySyncTask.run();
+
+ verify(mMockScrypt).scrypt(any(), any(),
+ eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
+ eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
+ }
+
+ @Test
public void run_useSha256ToHashPatternInProdMode() throws Exception {
String pattern = "123456";
mKeySyncTask = new KeySyncTask(
@@ -368,13 +409,15 @@
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+ setExpectedScryptArgument(shortPassword.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PASSWORD);
- verify(mMockScrypt).scrypt(eq(shortPassword.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
KeyDerivationParams keyDerivationParams =
@@ -650,13 +693,15 @@
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ setExpectedScryptArgument(password.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PASSWORD);
- verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
}
@@ -681,6 +726,8 @@
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ setExpectedScryptArgument(pin.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
@@ -688,7 +735,7 @@
// Password with only digits is changed to pin.
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PIN);
- verify(mMockScrypt).scrypt(eq(pin.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
}
@@ -868,4 +915,14 @@
new Random().nextBytes(bytes);
return bytes;
}
+
+ private void setExpectedScryptArgument(byte[] credentials) {
+ doAnswer(invocation -> {
+ assertThat((byte[]) invocation.getArguments()[0]).isEqualTo(credentials);
+ return invocation.callRealMethod();
+ }).when(mMockScrypt).scrypt(any(), any(),
+ eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
+ eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 8c07b6c..9f855c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -52,7 +52,6 @@
factory.isNamespaceAware = true
factory.newPullParser()
}
- private val ns = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
}
@Test
@@ -102,9 +101,13 @@
val tag = "application"
validateTagAttr(tag, "backupAgent",
R.styleable.AndroidManifestApplication_backupAgent, 1024)
+ validateTagAttrComponentName(tag, "backupAgent",
+ R.styleable.AndroidManifestApplication_backupAgent)
validateTagAttr(tag, "manageSpaceActivity",
R.styleable.AndroidManifestApplication_manageSpaceActivity, 1024)
validateTagAttr(tag, "name", R.styleable.AndroidManifestApplication_name, 1024)
+ validateTagAttrComponentName(tag, "name",
+ R.styleable.AndroidManifestApplication_name)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestApplication_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestApplication_process, 1024)
validateTagAttr(tag, "requiredAccountType",
@@ -134,6 +137,7 @@
fun parseReceiverTag() {
val tag = "receiver"
validateTagAttr(tag, "name", R.styleable.AndroidManifestReceiver_name, 1024)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestReceiver_name)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestReceiver_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestReceiver_process, 1024)
validateTagCount("meta-data", 1000, tag)
@@ -144,6 +148,7 @@
fun parseServiceTag() {
val tag = "service"
validateTagAttr(tag, "name", R.styleable.AndroidManifestService_name, 1024)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestService_name)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestService_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestService_process, 1024)
validateTagCount("meta-data", 1000, tag)
@@ -158,6 +163,8 @@
R.styleable.AndroidManifestActivityAlias_permission, 1024)
validateTagAttr(tag, "targetActivity",
R.styleable.AndroidManifestActivityAlias_targetActivity, 1024)
+ validateTagAttrComponentName(tag, "targetActivity",
+ R.styleable.AndroidManifestActivityAlias_targetActivity)
validateTagCount("meta-data", 1000, tag)
validateTagCount("intent-filter", 20000, tag)
}
@@ -166,14 +173,18 @@
fun parseUsesLibraryTag() {
val tag = "uses-library"
validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesLibrary_name, 1024)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestUsesLibrary_name)
}
@Test
fun parseActivityTag() {
val tag = "activity"
validateTagAttr(tag, "name", R.styleable.AndroidManifestActivity_name, 1024)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestActivity_name)
validateTagAttr(tag, "parentActivityName",
R.styleable.AndroidManifestActivity_parentActivityName, 1024)
+ validateTagAttrComponentName(tag, "parentActivityName",
+ R.styleable.AndroidManifestActivity_parentActivityName)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestActivity_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestActivity_process, 1024)
validateTagAttr(tag, "taskAffinity", R.styleable.AndroidManifestActivity_taskAffinity, 1024)
@@ -200,6 +211,8 @@
fun parseInstrumentationTag() {
val tag = "instrumentation"
validateTagAttr(tag, "name", R.styleable.AndroidManifestInstrumentation_name, 1024)
+ validateTagAttrComponentName(tag, "name",
+ R.styleable.AndroidManifestInstrumentation_name)
validateTagAttr(tag, "targetPackage",
R.styleable.AndroidManifestInstrumentation_targetPackage, 256)
validateTagAttr(tag, "targetProcesses",
@@ -262,6 +275,7 @@
fun parseProviderTag() {
val tag = "provider"
validateTagAttr(tag, "name", R.styleable.AndroidManifestProvider_name, 1024)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestProvider_name)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestProvider_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestProvider_process, 1024)
validateTagAttr(tag, "readPermission",
@@ -361,6 +375,48 @@
validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesPermission_name, 1024)
}
+ private fun validateTagAttrComponentName(tag: String, attr: String, index: Int) {
+ val passNames = arrayOf("com.android.TestClass", "TestClass", "_", "$", ".TestClass", "上")
+ for (name in passNames) {
+ val xml = "<$tag $attr=\"$name\" />"
+ pullParser.setInput(ByteArrayInputStream(xml.toByteArray()), null)
+ val validator = Validator()
+ pullParser.nextTag()
+ validator.validate(pullParser)
+ try {
+ validator.validateStrAttr(pullParser, attr, name)
+ } catch (e: SecurityException) {
+ fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
+ " ${e.message}")
+ }
+ try {
+ validator.validateResStrAttr(pullParser, index, name)
+ } catch (e: SecurityException) {
+ fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
+ " ${e.message}")
+ }
+ }
+
+ val failNames = arrayOf("com.android.TestClass:", "-TestClass", "TestClass.", ".", "..")
+ for (name in failNames) {
+ val xml = "<$tag $attr=\"$name\" />"
+ pullParser.setInput(ByteArrayInputStream(xml.toByteArray()), null)
+ val validator = Validator()
+ pullParser.nextTag()
+ validator.validate(pullParser)
+ val e1 = assertThrows("$name is not valid Java class name",
+ SecurityException::class.java) {
+ validator.validateStrAttr(pullParser, attr, name)
+ }
+ assertEquals(expectedAttrComponentNameErrorMsg(name), e1.message)
+ val e2 = assertThrows("$name is not valid Java class name",
+ SecurityException::class.java) {
+ validator.validateResStrAttr(pullParser, index, name)
+ }
+ assertEquals(expectedAttrComponentNameErrorMsg(name), e2.message)
+ }
+ }
+
private fun validateTagAttr(tag: String, name: String, index: Int?, maxLen: Int) {
validateTagAttr_shouldPass(tag, name, index, maxLen)
validateTagAttr_shouldFail(tag, name, index, maxLen)
@@ -468,4 +524,6 @@
fun expectedResAttrLengthErrorMsg(tag: String) =
"String length limit exceeded for attribute in $tag"
+
+ fun expectedAttrComponentNameErrorMsg(name: String) = "$name is not a valid Java class name"
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
index 62875e5..896edff 100644
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -27,6 +27,7 @@
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,6 +42,11 @@
public class CombinationKeyTests extends ShortcutKeyTestBase {
private static final long A11Y_KEY_HOLD_MILLIS = 3500;
+ @Before
+ public void setUp() {
+ setUpPhoneWindowManager();
+ }
+
/**
* Power-VolDown to take screenshot.
*/
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 9029bc4..2c35cf0 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -44,6 +44,7 @@
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
@Presubmit
@@ -61,6 +62,11 @@
META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING);
}
+ @Before
+ public void setUp() {
+ setUpPhoneWindowManager();
+ }
+
/**
* Test meta+ shortcuts defined in bookmarks.xml.
*/
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index c3b7849..6f65406 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -26,6 +26,7 @@
import android.provider.Settings;
import android.view.Display;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -35,6 +36,11 @@
* atest WmTests:PowerKeyGestureTests
*/
public class PowerKeyGestureTests extends ShortcutKeyTestBase {
+ @Before
+ public void setUp() {
+ setUpPhoneWindowManager();
+ }
+
/**
* Power single press to turn screen on/off.
*/
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index bf88ce4..f83aecb 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -39,6 +39,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
@@ -53,12 +54,17 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+
import org.junit.After;
-import org.junit.Before;
+import org.junit.Rule;
import java.util.Map;
class ShortcutKeyTestBase {
+ @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
TestPhoneWindowManager mPhoneWindowManager;
final Context mContext = spy(getInstrumentation().getTargetContext());
@@ -78,18 +84,35 @@
MODIFIER = unmodifiableMap(map);
}
- @Before
- public void setUp() {
+ /** Same as {@link setUpPhoneWindowManager(boolean)}, without supporting settings update. */
+ protected final void setUpPhoneWindowManager() {
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ false);
+ }
+
+ /**
+ * Creates and sets up a {@link TestPhoneWindowManager} instance.
+ *
+ * <p>Subclasses must call this at the start of the test if they intend to interact with phone
+ * window manager.
+ *
+ * @param supportSettingsUpdate {@code true} if this test should read and listen to provider
+ * settings values.
+ */
+ protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
if (Looper.myLooper() == null) {
Looper.prepare();
}
- mPhoneWindowManager = new TestPhoneWindowManager(mContext);
+ doReturn(mSettingsProviderRule.mockContentResolver(mContext))
+ .when(mContext).getContentResolver();
+ mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
}
@After
public void tearDown() {
- mPhoneWindowManager.tearDown();
+ if (mPhoneWindowManager != null) {
+ mPhoneWindowManager.tearDown();
+ }
}
void sendKeyCombination(int[] keyCodes, long duration) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index feca326..e474317 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -221,9 +221,8 @@
}
@Before
- @Override
public void setUp() {
- super.setUp();
+ setUpPhoneWindowManager();
mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID);
mPhoneWindowManager.overrideLaunchHome();
mPhoneWindowManager.overrideSearchKeyBehavior(
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index fe8017e..c433e64 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -16,21 +16,14 @@
package com.android.server.policy;
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-
-import android.content.Context;
-import android.content.res.Resources;
+import android.provider.Settings;
import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
/**
* Test class for stem key gesture.
@@ -39,17 +32,13 @@
* atest WmTests:StemKeyGestureTests
*/
public class StemKeyGestureTests extends ShortcutKeyTestBase {
- @Mock private Resources mResources;
-
/**
* Stem single key should not launch behavior during set up.
*/
@Test
public void stemSingleKey_duringSetup_doNothing() {
- stemKeySetup(
- () -> overrideBehavior(
- com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
- SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS));
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(false);
@@ -63,10 +52,9 @@
*/
@Test
public void stemSingleKey_AfterSetup_openAllApp() {
- stemKeySetup(
- () -> overrideBehavior(
- com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
- SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS));
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideStartActivity();
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -75,28 +63,7 @@
mPhoneWindowManager.assertOpenAllAppView();
}
- private void stemKeySetup(Runnable behaviorOverrideRunnable) {
- super.tearDown();
- setupResourcesMock();
- behaviorOverrideRunnable.run();
- super.setUp();
- }
-
- private void setupResourcesMock() {
- Resources realResources = mContext.getResources();
-
- mResources = Mockito.mock(Resources.class);
- doReturn(mResources).when(mContext).getResources();
-
- doAnswer(invocation -> realResources.getXml((Integer) invocation.getArguments()[0]))
- .when(mResources).getXml(anyInt());
- doAnswer(invocation -> realResources.getString((Integer) invocation.getArguments()[0]))
- .when(mResources).getString(anyInt());
- doAnswer(invocation -> realResources.getBoolean((Integer) invocation.getArguments()[0]))
- .when(mResources).getBoolean(anyInt());
- }
-
- private void overrideBehavior(int resId, int expectedBehavior) {
- doReturn(expectedBehavior).when(mResources).getInteger(eq(resId));
+ private void overrideBehavior(String key, int expectedBehavior) {
+ Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 1866767..7e7a9e1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -27,6 +27,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.description;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
@@ -177,16 +178,16 @@
}
}
- TestPhoneWindowManager(Context context) {
+ TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
mHandlerThread = new HandlerThread("fake window manager");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
- mHandler.runWithScissors(this::setUp, 0 /* timeout */);
+ mHandler.runWithScissors(() -> setUp(supportSettingsUpdate), 0 /* timeout */);
}
- private void setUp() {
+ private void setUp(boolean supportSettingsUpdate) {
mPhoneWindowManager = spy(new PhoneWindowManager());
// Use stubOnly() to reduce memory usage if it doesn't need verification.
@@ -266,7 +267,15 @@
});
doNothing().when(mPhoneWindowManager).initializeHdmiState();
- doNothing().when(mPhoneWindowManager).updateSettings();
+ if (supportSettingsUpdate) {
+ doAnswer(inv -> {
+ // Make any call to updateSettings run synchronously for tests.
+ mPhoneWindowManager.updateSettings(null);
+ return null;
+ }).when(mPhoneWindowManager).updateSettings(any(Handler.class));
+ } else {
+ doNothing().when(mPhoneWindowManager).updateSettings(any());
+ }
doNothing().when(mPhoneWindowManager).screenTurningOn(anyInt(), any());
doNothing().when(mPhoneWindowManager).screenTurnedOn(anyInt());
doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 6d7f2c1..1c86758 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -570,6 +570,7 @@
final ContextWrapper contextSpy = Mockito.spy(new ContextWrapper(mWm.mContext));
final Resources resourcesSpy = Mockito.spy(contextSpy.getResources());
+ spyOn(mAtm.mTaskOrganizerController);
when(contextSpy.getResources()).thenReturn(resourcesSpy);
MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class)
@@ -597,7 +598,8 @@
mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity);
assertTrue(toHomeBuilder.mIsLaunchBehind);
toHomeBuilder.build();
- verify(animationHandler, never()).createStartingSurface(any());
+ verify(mAtm.mTaskOrganizerController, never())
+ .addWindowlessStartingSurface(any(), any(), any(), any(), any());
animationHandler.clearBackAnimateTarget();
// Back to ACTIVITY and TASK have the same logic, just with different target.
@@ -609,9 +611,11 @@
assertFalse(toActivityBuilder.mIsLaunchBehind);
toActivityBuilder.build();
if (preferWindowlessSurface) {
- verify(animationHandler).createStartingSurface(any());
+ verify(mAtm.mTaskOrganizerController)
+ .addWindowlessStartingSurface(any(), any(), any(), any(), any());
} else {
- verify(animationHandler, never()).createStartingSurface(any());
+ verify(mAtm.mTaskOrganizerController, never())
+ .addWindowlessStartingSurface(any(), any(), any(), any(), any());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index 1180ebd..c3db241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -47,6 +47,7 @@
import android.view.IWindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.window.WindowContextInfo;
import android.window.WindowTokenClient;
import com.android.server.inputmethod.InputMethodDialogWindowContext;
@@ -99,7 +100,7 @@
final WindowProcessController wpc = mAtm.getProcessController(appThread);
mWm.mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
dc.getImeContainer(), TYPE_INPUT_METHOD_DIALOG, null /* options */);
- return dc.getImeContainer().getConfiguration();
+ return new WindowContextInfo(dc.getImeContainer().getConfiguration(), displayId);
}).when(mIWindowManager).attachWindowContextToDisplayArea(any(), any(),
eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 325176f..55fda05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -468,7 +468,7 @@
mWm.attachWindowContextToWindowToken(mAppThread, new Binder(), windowToken.token);
verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(
- any(), any(), any(), anyInt(), any());
+ any(), any(), any(), anyInt(), any(), anyBoolean());
}
@Test
@@ -484,9 +484,9 @@
final IBinder clientToken = new Binder();
mWm.attachWindowContextToWindowToken(mAppThread, clientToken, windowToken.token);
final WindowProcessController wpc = mAtm.getProcessController(mAppThread);
- verify(mWm.mWindowContextListenerController).registerWindowContainerListener(eq(wpc),
- eq(clientToken), eq(windowToken), eq(TYPE_INPUT_METHOD),
- eq(windowToken.mOptions));
+ verify(mWm.mWindowContextListenerController).registerWindowContainerListener(wpc,
+ clientToken, windowToken, TYPE_INPUT_METHOD, windowToken.mOptions,
+ false /* shouldDispatchConfigWhenRegistering */);
}
@Test
@@ -514,7 +514,7 @@
new InsetsSourceControl.Array(), new Rect(), new float[1]);
verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
- any(), any(), anyInt(), any());
+ any(), any(), anyInt(), any(), anyBoolean());
}
@Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ed1c41f..314150b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9407,10 +9407,11 @@
"missed_incoming_call_sms_pattern_string_array";
/**
- * Indicate the satellite services supported per provider by a carrier.
- *
- * Key is the PLMN of a satellite provider. Value should be an integer array of supported
- * services with the following value:
+ * A PersistableBundle that contains a list of key-value pairs, where the values are integer
+ * arrays.
+ * <p>
+ * Keys are the PLMNs of satellite providers as strings and values are integer arrays of
+ * supported services with the following value:
* <ul>
* <li>1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}</li>
* <li>2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}</li>
@@ -9419,19 +9420,35 @@
* <li>5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}</li>
* </ul>
* <p>
- * If this carrier config is not present, the overlay config
+ * An example config for two PLMNs "123411" and "123412":
+ * <pre>{@code
+ * <carrier_config>
+ * <pbundle_as_map name="carrier_supported_satellite_services_per_provider_bundle">
+ * <int-array name = "123411" num = "2">
+ * <item value = "3"/>
+ * <item value = "5"/>
+ * </int-array>
+ * <int-array name = "123412" num = "1">
+ * <item value = "3"/>
+ * </int-array>
+ * </pbundle_as_map>
+ * </carrier_config>
+ * }</pre>
+ * <p>
+ * If this carrier config is not present, the device overlay config
* {@code config_satellite_services_supported_by_providers} will be used. If the carrier config
- * is present, the supported satellite services will be identified as follows:
+ * is present, the supported services associated with the PLMNs listed in the carrier config
+ * will override that of the device overlay config. The supported satellite services will be
+ * identified as follows:
* <ul>
- * <li>For the PLMN that exists in both provider supported satellite services and carrier
- * supported satellite services, the supported services will be the intersection of the two
- * sets.</li>
- * <li>For the PLMN that is present in provider supported satellite services but not in carrier
- * supported satellite services, the provider supported satellite services will be used.</li>
- * <li>For the PLMN that is present in carrier supported satellite services but not in provider
- * supported satellite services, the PLMN will be ignored.</li>
+ * <li>For each PLMN that exists only in the carrier provided satellite services, use the
+ * carrier provided services as the supported services.</li>
+ * <li>For each PLMN that is present only in the device provided satellite services, use the
+ * device provided services as the supported services.</li>
+ * <li>For each PLMN that is present in both the carrier provided and device provided satellite
+ * services, use the carrier provided services as the supported services.</li>
* </ul>
- *
+ * <p>
* This config is empty by default.
*/
public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index feef049..d41c019 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -81,6 +81,19 @@
mCallback = callback;
}
+ @Override
+ public void onServiceConnected() {
+ if (mCallback != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onServiceConnected());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
public void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks) {
if (mCallback != null) {
final long token = Binder.clearCallingIdentity();
@@ -117,6 +130,7 @@
}
}
+ @Override
public void onHotspotNetworkConnectionStatusChanged(
@NonNull HotspotNetworkConnectionStatus status) {
if (mCallback != null) {
@@ -251,7 +265,6 @@
synchronized (mProxyDataLock) {
mProxyMap.put(callback, proxy);
}
- callback.onServiceConnected();
} catch (RemoteException e) {
Log.e(TAG, "Exception in registerCallback", e);
callback.onRegisterCallbackFailed(e);
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
index 737aa6d..521f943 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
@@ -31,4 +31,5 @@
oneway void onKnownNetworksUpdated(in List<KnownNetwork> networks);
oneway void onKnownNetworkConnectionStatusChanged(in KnownNetworkConnectionStatus status);
oneway void onSharedConnectivitySettingsChanged(in SharedConnectivitySettingsState state);
+ oneway void onServiceConnected();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index 2bbe919..ebda6f1 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -276,6 +276,11 @@
private void onRegisterCallback(ISharedConnectivityCallback callback) {
mRemoteCallbackList.register(callback);
+ try {
+ callback.onServiceConnected();
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in onRegisterCallback", e);
+ }
if (mCountDownLatch != null) {
mCountDownLatch.countDown();
}