diff options
314 files changed, 6121 insertions, 2263 deletions
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl index 0c920f1359f3..60c2eeddac63 100644 --- a/core/java/android/app/IUidObserver.aidl +++ b/core/java/android/app/IUidObserver.aidl @@ -58,8 +58,9 @@ oneway interface IUidObserver { * Report a proc oom adj change associated with a uid. * * @param uid The uid for which the state change is being reported. + * @param adj The minimum OOM adj among all processes with this uid. */ - void onUidProcAdjChanged(int uid); + void onUidProcAdjChanged(int uid, int adj); // =============== End of transactions used on native side as well ============================ diff --git a/core/java/android/app/UidObserver.java b/core/java/android/app/UidObserver.java index 9e928073ac5c..519662448e91 100644 --- a/core/java/android/app/UidObserver.java +++ b/core/java/android/app/UidObserver.java @@ -41,7 +41,7 @@ public class UidObserver extends IUidObserver.Stub { } @Override - public void onUidProcAdjChanged(int uid) { + public void onUidProcAdjChanged(int uid, int adj) { } @Override diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index de66f050c007..56f6f8206d30 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2072,6 +2072,25 @@ public class PackageInstaller { return new InstallInfo(result); } + /** + * Parse a single APK file passed as an FD to get install relevant information about + * the package wrapped in {@link InstallInfo}. + * @throws PackageParsingException if the package source file(s) provided is(are) not valid, + * or the parser isn't able to parse the supplied source(s). + * @hide + */ + @NonNull + public InstallInfo readInstallInfo(@NonNull ParcelFileDescriptor pfd, + @Nullable String debugPathName, int flags) throws PackageParsingException { + final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); + final ParseResult<PackageLite> result = ApkLiteParseUtils.parseMonolithicPackageLite(input, + pfd.getFileDescriptor(), debugPathName, flags); + if (result.isError()) { + throw new PackageParsingException(result.getErrorCode(), result.getErrorMessage()); + } + return new InstallInfo(result); + } + // (b/239722738) This class serves as a bridge between the PackageLite class, which // is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java) // This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or @@ -2125,6 +2144,21 @@ public class PackageInstaller { public long calculateInstalledSize(@NonNull SessionParams params) throws IOException { return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride); } + + /** + * @param params {@link SessionParams} of the installation + * @param pfd of an APK opened for read + * @return Total disk space occupied by an application after installation. + * Includes the size of the raw APKs, possibly unpacked resources, raw dex metadata files, + * and all relevant native code. + * @throws IOException when size of native binaries cannot be calculated. + * @hide + */ + public long calculateInstalledSize(@NonNull SessionParams params, + @NonNull ParcelFileDescriptor pfd) throws IOException { + return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride, + pfd.getFileDescriptor()); + } } /** diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index a4339d41dfd2..d209b35ac810 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -109,7 +109,7 @@ public class ApkLiteParseUtils { } /** - * Parse lightweight details about a single APK files. + * Parse lightweight details about a single APK file. */ public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input, File packageFile, int flags) { @@ -135,6 +135,33 @@ public class ApkLiteParseUtils { } /** + * Parse lightweight details about a single APK file passed as an FD. + */ + public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input, + FileDescriptor packageFd, String debugPathName, int flags) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); + try { + final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName, + flags); + if (result.isError()) { + return input.error(result); + } + + final ApkLite baseApk = result.getResult(); + final String packagePath = debugPathName; + return input.success( + new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */, + null /* isFeatureSplits */, null /* usesSplitNames */, + null /* configForSplit */, null /* splitApkPaths */, + null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(), + null /* requiredSplitTypes */, null, /* splitTypes */ + baseApk.isAllowUpdateOwnership())); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + /** * Parse lightweight details about a directory of APKs. * * @param packageDir is the folder that contains split apks for a regular app diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 24e28e95cd98..5bcbaa10e95b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -65,7 +65,6 @@ import android.util.Log; import android.view.WindowManager.LayoutParams; import com.android.internal.R; -import com.android.internal.util.FrameworkStatsLog; import java.io.IOException; import java.lang.annotation.Retention; @@ -2533,38 +2532,6 @@ public class UserManager { } /** - * Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to the - * user type. - * @hide - */ - public static int getUserTypeForStatsd(@NonNull String userType) { - switch (userType) { - case USER_TYPE_FULL_SYSTEM: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM; - case USER_TYPE_FULL_SECONDARY: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY; - case USER_TYPE_FULL_GUEST: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST; - case USER_TYPE_FULL_DEMO: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO; - case USER_TYPE_FULL_RESTRICTED: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED; - case USER_TYPE_PROFILE_MANAGED: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED; - case USER_TYPE_SYSTEM_HEADLESS: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS; - case USER_TYPE_PROFILE_CLONE: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE; - default: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN; - } - } - - /** * @hide * @deprecated Use {@link #isRestrictedProfile()} */ diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java index a69af24756a0..470c2801d838 100644 --- a/core/java/android/view/InsetsFrameProvider.java +++ b/core/java/android/view/InsetsFrameProvider.java @@ -62,9 +62,7 @@ public class InsetsFrameProvider implements Parcelable { */ public static final int SOURCE_ARBITRARY_RECTANGLE = 3; - private final IBinder mOwner; - private final int mIndex; - private final @InsetsType int mType; + private final int mId; /** * The selection of the starting rectangle to be converted into source frame. @@ -122,30 +120,30 @@ public class InsetsFrameProvider implements Parcelable { * @param type the {@link InsetsType}. * @see InsetsSource#createId(Object, int, int) */ - public InsetsFrameProvider(IBinder owner, @IntRange(from = 0, to = 2047) int index, + public InsetsFrameProvider(Object owner, @IntRange(from = 0, to = 2047) int index, @InsetsType int type) { - if (index < 0 || index >= 2048) { - throw new IllegalArgumentException(); - } - - // This throws IllegalArgumentException if the type is not valid. - WindowInsets.Type.indexOf(type); - - mOwner = owner; - mIndex = index; - mType = type; + mId = InsetsSource.createId(owner, index, type); } - public IBinder getOwner() { - return mOwner; + /** + * Returns an unique integer which identifies the insets source. + */ + public int getId() { + return mId; } + /** + * Returns the index specified in {@link #InsetsFrameProvider(IBinder, int, int)}. + */ public int getIndex() { - return mIndex; + return InsetsSource.getIndex(mId); } + /** + * Returns the {@link InsetsType} specified in {@link #InsetsFrameProvider(IBinder, int, int)}. + */ public int getType() { - return mType; + return InsetsSource.getType(mId); } public InsetsFrameProvider setSource(int source) { @@ -211,9 +209,9 @@ public class InsetsFrameProvider implements Parcelable { @Override public String toString() { final StringBuilder sb = new StringBuilder("InsetsFrameProvider: {"); - sb.append("owner=").append(mOwner); - sb.append(", index=").append(mIndex); - sb.append(", type=").append(WindowInsets.Type.toString(mType)); + sb.append("id=#").append(Integer.toHexString(mId)); + sb.append(", index=").append(getIndex()); + sb.append(", type=").append(WindowInsets.Type.toString(getType())); sb.append(", source=").append(sourceToString(mSource)); sb.append(", flags=[").append(InsetsSource.flagsToString(mFlags)).append("]"); if (mInsetsSize != null) { @@ -244,9 +242,7 @@ public class InsetsFrameProvider implements Parcelable { } public InsetsFrameProvider(Parcel in) { - mOwner = in.readStrongBinder(); - mIndex = in.readInt(); - mType = in.readInt(); + mId = in.readInt(); mSource = in.readInt(); mFlags = in.readInt(); mInsetsSize = in.readTypedObject(Insets.CREATOR); @@ -256,9 +252,7 @@ public class InsetsFrameProvider implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeStrongBinder(mOwner); - out.writeInt(mIndex); - out.writeInt(mType); + out.writeInt(mId); out.writeInt(mSource); out.writeInt(mFlags); out.writeTypedObject(mInsetsSize, flags); @@ -267,7 +261,7 @@ public class InsetsFrameProvider implements Parcelable { } public boolean idEquals(InsetsFrameProvider o) { - return Objects.equals(mOwner, o.mOwner) && mIndex == o.mIndex && mType == o.mType; + return mId == o.mId; } @Override @@ -279,8 +273,7 @@ public class InsetsFrameProvider implements Parcelable { return false; } final InsetsFrameProvider other = (InsetsFrameProvider) o; - return Objects.equals(mOwner, other.mOwner) && mIndex == other.mIndex - && mType == other.mType && mSource == other.mSource && mFlags == other.mFlags + return mId == other.mId && mSource == other.mSource && mFlags == other.mFlags && Objects.equals(mInsetsSize, other.mInsetsSize) && Arrays.equals(mInsetsSizeOverrides, other.mInsetsSizeOverrides) && Objects.equals(mArbitraryRectangle, other.mArbitraryRectangle); @@ -288,7 +281,7 @@ public class InsetsFrameProvider implements Parcelable { @Override public int hashCode() { - return Objects.hash(mOwner, mIndex, mType, mSource, mFlags, mInsetsSize, + return Objects.hash(mId, mSource, mFlags, mInsetsSize, Arrays.hashCode(mInsetsSizeOverrides), mArbitraryRectangle); } @@ -319,7 +312,7 @@ public class InsetsFrameProvider implements Parcelable { protected InsetsSizeOverride(Parcel in) { mWindowType = in.readInt(); - mInsetsSize = in.readParcelable(null, Insets.class); + mInsetsSize = in.readTypedObject(Insets.CREATOR); } public InsetsSizeOverride(int windowType, Insets insetsSize) { @@ -354,7 +347,7 @@ public class InsetsFrameProvider implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mWindowType); - out.writeParcelable(mInsetsSize, flags); + out.writeTypedObject(mInsetsSize, flags); } @Override diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index bd48771ec4b3..114f4ed04ee6 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -271,7 +271,7 @@ public class InsetsSource implements Parcelable { * @param index An owner may have multiple sources with the same type. For example, the system * server might have multiple display cutout sources. This is used to identify * which one is which. The value must be in a range of [0, 2047]. - * @param type The {@link WindowInsets.Type.InsetsType type} of the source. + * @param type The {@link InsetsType type} of the source. * @return a unique integer as the identifier. */ public static int createId(Object owner, @IntRange(from = 0, to = 2047) int index, @@ -282,11 +282,29 @@ public class InsetsSource implements Parcelable { // owner takes top 16 bits; // index takes 11 bits since the 6th bit; // type takes bottom 5 bits. - return (((owner != null ? owner.hashCode() : 1) % (1 << 16)) << 16) + return ((System.identityHashCode(owner) % (1 << 16)) << 16) + (index << 5) + WindowInsets.Type.indexOf(type); } + /** + * Gets the index from the ID. + * + * @see #createId(Object, int, int) + */ + public static int getIndex(int id) { + return (id % (1 << 16)) >> 5; + } + + /** + * Gets the {@link InsetsType} from the ID. + * + * @see #createId(Object, int, int) + */ + public static int getType(int id) { + return 1 << (id % 32); + } + public static String flagsToString(@Flags int flags) { final StringJoiner joiner = new StringJoiner(" "); if ((flags & FLAG_SUPPRESS_SCRIM) != 0) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8f20e2d044ac..153bfde07758 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -11325,7 +11325,7 @@ public final class ViewRootImpl implements ViewParent, // to sync the same frame in the same BBQ. That shouldn't be possible, but // if it did happen, invoke markSyncReady so the active SSG doesn't get // stuck. - Log.e(mTag, "Unable to syncNextTransaction. Possibly something else is" + Log.w(mTag, "Unable to syncNextTransaction. Possibly something else is" + " trying to sync?"); surfaceSyncGroup.markSyncReady(); } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 628fc3140ee8..c0370cc5517d 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -21,6 +21,7 @@ import static android.app.ActivityOptions.ANIM_CUSTOM; import static android.app.ActivityOptions.ANIM_FROM_STYLE; import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; import static android.app.ActivityOptions.ANIM_SCALE_UP; +import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; @@ -1067,6 +1068,11 @@ public final class TransitionInfo implements Parcelable { return options; } + public static AnimationOptions makeSceneTransitionAnimOptions() { + AnimationOptions options = new AnimationOptions(ANIM_SCENE_TRANSITION); + return options; + } + public int getType() { return mType; } diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto index 3a9e5eeb4877..02ba7c5e39ff 100644 --- a/core/proto/android/companion/telecom.proto +++ b/core/proto/android/companion/telecom.proto @@ -25,7 +25,7 @@ message Telecom { // Next index: 6 message Call { // UUID representing this call - int64 id = 1; + string id = 1; message Origin { // Caller's name and/or phone number; what a user would see displayed when receiving an @@ -48,22 +48,23 @@ message Telecom { } Status status = 3; - enum Control { - UNKNOWN_CONTROL = 0; - ACCEPT = 1; - REJECT = 2; - SILENCE = 3; - MUTE = 4; - UNMUTE = 5; - END = 6; - PUT_ON_HOLD = 7; - TAKE_OFF_HOLD = 8; - REJECT_AND_BLOCK = 9; - IGNORE = 10; - } repeated Control controls = 4; } + enum Control { + UNKNOWN_CONTROL = 0; + ACCEPT = 1; + REJECT = 2; + SILENCE = 3; + MUTE = 4; + UNMUTE = 5; + END = 6; + PUT_ON_HOLD = 7; + TAKE_OFF_HOLD = 8; + REJECT_AND_BLOCK = 9; + IGNORE = 10; + } + // The list of active calls. repeated Call calls = 1; // The list of requested calls or call changes. diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index ad864b13b16d..bc0af12e9569 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -96,12 +96,12 @@ <!-- Width of the navigation bar when it is placed vertically on the screen in car mode --> <dimen name="navigation_bar_width_car_mode">96dp</dimen> <!-- Height of notification icons in the status bar --> - <dimen name="status_bar_icon_size">18sp</dimen> + <dimen name="status_bar_icon_size">22dip</dimen> <!-- Desired size of system icons in status bar. --> - <dimen name="status_bar_system_icon_size">15sp</dimen> + <dimen name="status_bar_system_icon_size">15dp</dimen> <!-- Intrinsic size of most system icons in status bar. This is the default value that is used if a Drawable reports an intrinsic size of 0. --> - <dimen name="status_bar_system_icon_intrinsic_size">17sp</dimen> + <dimen name="status_bar_system_icon_intrinsic_size">17dp</dimen> <!-- Size of the giant number (unread count) in the notifications --> <dimen name="status_bar_content_number_size">48sp</dimen> <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> @@ -330,7 +330,7 @@ <dimen name="notification_icon_circle_start">16dp</dimen> <!-- size (width and height) of the icon in the notification header --> - <dimen name="notification_header_icon_size_ambient">18sp</dimen> + <dimen name="notification_header_icon_size_ambient">18dp</dimen> <!-- The margin before the start of the app name in the header. --> <dimen name="notification_header_app_name_margin_start">3dp</dimen> diff --git a/core/tests/coretests/jni/NativeWorkSourceParcelTest.cpp b/core/tests/coretests/jni/NativeWorkSourceParcelTest.cpp index db1f7bde4b4f..187e2c137f85 100644 --- a/core/tests/coretests/jni/NativeWorkSourceParcelTest.cpp +++ b/core/tests/coretests/jni/NativeWorkSourceParcelTest.cpp @@ -77,15 +77,18 @@ static void nativeUnparcelAndVerifyWorkSource(JNIEnv* env, jobject /* obj */, jo Parcel* parcel = nativeGetParcelData(env, wsParcel); int32_t endMarker; - // read WorkSource and if no error read end marker - status_t err = ws.readFromParcel(parcel) ?: parcel->readInt32(&endMarker); - int32_t dataAvailable = parcel->dataAvail(); - + status_t err = ws.readFromParcel(parcel); if (err != OK) { - ALOGE("WorkSource readFromParcel failed %d", err); + jniThrowException(env, "java/lang/IllegalArgumentException", + StringPrintf("WorkSource readFromParcel failed: %d", err).c_str()); + } + err = parcel->readInt32(&endMarker); + if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", + StringPrintf("Failed to read endMarker: %d", err).c_str()); } - // Now we have a native WorkSource object, verify it. + int32_t dataAvailable = parcel->dataAvail(); if (dataAvailable > 0) { // not all data read from the parcel jniThrowException(env, "java/lang/IllegalArgumentException", StringPrintf("WorkSource contains more data than native read (%d)", diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index 6fa8f1117343..55680abb159d 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -227,5 +227,25 @@ public class InsetsSourceTest { assertEquals(numTotalSources, sources.size()); } + @Test + public void testGetIndex() { + for (int index = 0; index < 2048; index++) { + for (int type = FIRST; type <= LAST; type = type << 1) { + final int id = InsetsSource.createId(null, index, type); + assertEquals(index, InsetsSource.getIndex(id)); + } + } + } + + @Test + public void testGetType() { + for (int index = 0; index < 2048; index++) { + for (int type = FIRST; type <= LAST; type = type << 1) { + final int id = InsetsSource.createId(null, index, type); + assertEquals(type, InsetsSource.getType(id)); + } + } + } + // Parcel and equals already tested via InsetsStateTest } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 40cb7f2194e9..20a43fc1beb0 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -520,6 +520,8 @@ applications that come with the platform <permission name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/> <!-- Permission required for CTS test - SatelliteManagerTest --> <permission name="android.permission.SATELLITE_COMMUNICATION"/> + <!-- Permission required for GTS test - GtsAttestationVerificationDeviceSideTestCases --> + <permission name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java index 70311fdb2d1f..b1aae7f37c31 100644 --- a/graphics/java/android/graphics/MeshSpecification.java +++ b/graphics/java/android/graphics/MeshSpecification.java @@ -28,11 +28,40 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Class responsible for holding specifications for {@link Mesh} creations. This class - * generates a {@link MeshSpecification} via the Make method, where multiple parameters to set up - * the mesh are supplied, including attributes, vertex stride, varyings, and - * vertex/fragment shaders. There are also additional methods to provide an optional - * {@link ColorSpace} as well as an alpha type. + * Class responsible for holding specifications for {@link Mesh} creations. This class generates a + * {@link MeshSpecification} via the + * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String)} method, + * where multiple parameters to set up the mesh are supplied, including attributes, vertex stride, + * {@link Varying}, and vertex/fragment shaders. There are also additional methods to provide an + * optional {@link ColorSpace} as well as an alpha type. + * + * For example a vertex shader that leverages a {@link Varying} may look like the following: + * + * <pre> + * Varyings main(const Attributes attributes) { + * Varyings varyings; + * varyings.position = attributes.position; + * return varyings; + * } + * </pre> + * + * The corresponding fragment shader that may consume the varying look like the following: + * + * <pre> + * float2 main(const Varyings varyings, out float4 color) { + * color = vec4(1.0, 0.0, 0.0, 1.0); + * return varyings.position; + * } + * </pre> + * + * The color returned from this fragment shader is blended with the other parameters that are + * configured on the Paint object (ex. {@link Paint#setBlendMode(BlendMode)} used to draw the mesh. + * + * The position returned in the fragment shader can be consumed by any following fragment shaders in + * the shader chain. + * + * See https://developer.android.com/develop/ui/views/graphics/agsl for more information + * regarding Android Graphics Shader Language. * * Note that there are several limitations on various mesh specifications: * 1. The max amount of attributes allowed is 8. @@ -118,7 +147,11 @@ public class MeshSpecification { public static final int TYPE_UBYTE4 = 4; /** - * Data class to represent a single attribute in a shader. + * Data class to represent a single attribute in a shader. An attribute is a variable that + * accompanies a vertex, this can be a color or texture coordinates. + * + * See https://developer.android.com/develop/ui/views/graphics/agsl for more information + * regarding Android Graphics Shader Language. * * Note that offset is the offset in number of bytes. For example, if we had two attributes * @@ -128,6 +161,10 @@ public class MeshSpecification { * </pre> * * att1 would have an offset of 0, while att2 would have an offset of 12 bytes. + * + * This is consumed as part of + * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String, ColorSpace, int)} + * to create a {@link MeshSpecification} instance. */ public static class Attribute { @Type @@ -175,7 +212,15 @@ public class MeshSpecification { } /** - * Data class to represent a single varying variable. + * Data class to represent a single varying variable. A Varying variable can be altered by the + * vertex shader defined on the mesh but not by the fragment shader defined by AGSL. + * + * See https://developer.android.com/develop/ui/views/graphics/agsl for more information + * regarding Android Graphics Shader Language. + * + * This is consumed as part of + * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String, ColorSpace, int)} + * to create a {@link MeshSpecification} instance. */ public static class Varying { @Type @@ -220,7 +265,7 @@ public class MeshSpecification { /** * Creates a {@link MeshSpecification} object for use within {@link Mesh}. This uses a default - * color space of {@link ColorSpace.Named#SRGB} and {@link AlphaType} of + * color space of {@link ColorSpace.Named#SRGB} and alphaType of * {@link #ALPHA_TYPE_PREMULTIPLIED}. * * @param attributes list of attributes represented by {@link Attribute}. Can hold a max of @@ -233,7 +278,11 @@ public class MeshSpecification { * the 6 varyings allowed. * @param vertexShader vertex shader to be supplied to the mesh. Ensure that the position * varying is set within the shader to get proper results. + * See {@link MeshSpecification} for an example vertex shader + * implementation * @param fragmentShader fragment shader to be supplied to the mesh. + * See {@link MeshSpecification} for an example fragment shader + * implementation * @return {@link MeshSpecification} object for use when creating {@link Mesh} */ @NonNull @@ -253,7 +302,7 @@ public class MeshSpecification { } /** - * Creates a {@link MeshSpecification} object. This uses a default {@link AlphaType} of + * Creates a {@link MeshSpecification} object. This uses a default alphaType of * {@link #ALPHA_TYPE_PREMULTIPLIED}. * * @param attributes list of attributes represented by {@link Attribute}. Can hold a max of @@ -266,7 +315,11 @@ public class MeshSpecification { * the 6 varyings allowed. * @param vertexShader vertex shader to be supplied to the mesh. Ensure that the position * varying is set within the shader to get proper results. + * See {@link MeshSpecification} for an example vertex shader + * implementation * @param fragmentShader fragment shader to be supplied to the mesh. + * See {@link MeshSpecification} for an example fragment shader + * implementation * @param colorSpace {@link ColorSpace} to tell what color space to work in. * @return {@link MeshSpecification} object for use when creating {@link Mesh} */ @@ -301,7 +354,11 @@ public class MeshSpecification { * the 6 varyings allowed. * @param vertexShader vertex shader to be supplied to the mesh. Ensure that the position * varying is set within the shader to get proper results. + * See {@link MeshSpecification} for an example vertex shader + * implementation * @param fragmentShader fragment shader to be supplied to the mesh. + * See {@link MeshSpecification} for an example fragment shader + * implementation * @param colorSpace {@link ColorSpace} to tell what color space to work in. * @param alphaType Describes how to interpret the alpha component for a pixel. Must be * one of diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index 544d75739547..410ae78dba1b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -181,6 +181,17 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { } /** + * Returns the list of display ids that are tracked by a {@link DisplayAreaInfo} + */ + public int[] getDisplayIds() { + int[] displayIds = new int[mDisplayAreasInfo.size()]; + for (int i = 0; i < mDisplayAreasInfo.size(); i++) { + displayIds[i] = mDisplayAreasInfo.keyAt(i); + } + return displayIds; + } + + /** * Returns the {@link DisplayAreaInfo} of the {@link DisplayAreaInfo#displayId}. */ @Nullable diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java index c767376d4f29..18615f7e353a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java @@ -19,6 +19,8 @@ package com.android.wm.shell.activityembedding; import static android.graphics.Matrix.MTRANS_X; import static android.graphics.Matrix.MTRANS_Y; +import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.shouldUseSnapshotAnimationForClosingChange; + import android.annotation.CallSuper; import android.graphics.Point; import android.graphics.Rect; @@ -97,10 +99,17 @@ class ActivityEmbeddingAnimationAdapter { final Rect startBounds = change.getStartAbsBounds(); final Rect endBounds = change.getEndAbsBounds(); mContentBounds.set(startBounds); - mContentRelOffset.set(change.getEndRelOffset()); - mContentRelOffset.offset( - startBounds.left - endBounds.left, - startBounds.top - endBounds.top); + // Put the transition to the top left for snapshot animation. + if (shouldUseSnapshotAnimationForClosingChange(mChange)) { + // TODO(b/275034335): Fix an issue that black hole when closing the right container + // in bounds change transition. + mContentRelOffset.set(0, 0); + } else { + mContentRelOffset.set(change.getEndRelOffset()); + mContentRelOffset.offset( + startBounds.left - endBounds.left, + startBounds.top - endBounds.top); + } } else { mContentBounds.set(change.getEndAbsBounds()); mContentRelOffset.set(change.getEndRelOffset()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index 1df6ecda78c3..ab7c7d8ae6c8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; +import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationSpec.createShowSnapshotForClosingAnimation; import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition; import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow; import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet; @@ -42,6 +43,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationAdapter.SnapshotAdapter; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.util.TransitionUtil; @@ -185,23 +187,23 @@ class ActivityEmbeddingAnimationRunner { return createChangeAnimationAdapters(info, startTransaction); } if (TransitionUtil.isClosingType(info.getType())) { - return createCloseAnimationAdapters(info); + return createCloseAnimationAdapters(info, startTransaction); } - return createOpenAnimationAdapters(info); + return createOpenAnimationAdapters(info, startTransaction); } @NonNull private List<ActivityEmbeddingAnimationAdapter> createOpenAnimationAdapters( - @NonNull TransitionInfo info) { + @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { return createOpenCloseAnimationAdapters(info, true /* isOpening */, - mAnimationSpec::loadOpenAnimation); + mAnimationSpec::loadOpenAnimation, startTransaction); } @NonNull private List<ActivityEmbeddingAnimationAdapter> createCloseAnimationAdapters( - @NonNull TransitionInfo info) { + @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { return createOpenCloseAnimationAdapters(info, false /* isOpening */, - mAnimationSpec::loadCloseAnimation); + mAnimationSpec::loadCloseAnimation, startTransaction); } /** @@ -211,7 +213,8 @@ class ActivityEmbeddingAnimationRunner { @NonNull private List<ActivityEmbeddingAnimationAdapter> createOpenCloseAnimationAdapters( @NonNull TransitionInfo info, boolean isOpening, - @NonNull AnimationProvider animationProvider) { + @NonNull AnimationProvider animationProvider, + @NonNull SurfaceControl.Transaction startTransaction) { // We need to know if the change window is only a partial of the whole animation screen. // If so, we will need to adjust it to make the whole animation screen looks like one. final List<TransitionInfo.Change> openingChanges = new ArrayList<>(); @@ -224,6 +227,8 @@ class ActivityEmbeddingAnimationRunner { openingWholeScreenBounds.union(change.getEndAbsBounds()); } else { closingChanges.add(change); + // Also union with the start bounds because the closing transition may be shrunk. + closingWholeScreenBounds.union(change.getStartAbsBounds()); closingWholeScreenBounds.union(change.getEndAbsBounds()); } } @@ -241,6 +246,17 @@ class ActivityEmbeddingAnimationRunner { adapters.add(adapter); } for (TransitionInfo.Change change : closingChanges) { + if (shouldUseSnapshotAnimationForClosingChange(change)) { + SurfaceControl screenshot = getOrCreateScreenshot(change, change, startTransaction); + if (screenshot != null) { + final SnapshotAdapter snapshotAdapter = new SnapshotAdapter( + createShowSnapshotForClosingAnimation(), change, screenshot); + if (!isOpening) { + snapshotAdapter.overrideLayer(offsetLayer++); + } + adapters.add(snapshotAdapter); + } + } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( info, change, animationProvider, closingWholeScreenBounds); if (!isOpening) { @@ -251,6 +267,22 @@ class ActivityEmbeddingAnimationRunner { return adapters; } + /** + * Returns whether we should use snapshot animation for the closing change. + * It's usually because the end bounds of the closing change are shrunk, which leaves a black + * area in the transition. + */ + static boolean shouldUseSnapshotAnimationForClosingChange( + @NonNull TransitionInfo.Change closingChange) { + // Only check closing type because we only take screenshot for closing bounds-changing + // changes. + if (!TransitionUtil.isClosingType(closingChange.getMode())) { + return false; + } + // Don't need to take screenshot if there's no bounds change. + return !closingChange.getStartAbsBounds().equals(closingChange.getEndAbsBounds()); + } + /** Sets the first frame to the {@code startTransaction} to avoid any flicker on start. */ private void prepareForFirstFrame(@NonNull SurfaceControl.Transaction startTransaction, @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index cb8342a10a6a..19eff0e43169 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -77,6 +77,15 @@ class ActivityEmbeddingAnimationSpec { return new AlphaAnimation(alpha, alpha); } + /** + * Animation that intended to show snapshot for closing animation because the closing end bounds + * are changed. + */ + @NonNull + static Animation createShowSnapshotForClosingAnimation() { + return new AlphaAnimation(1f, 1f); + } + /** Animation for window that is opening in a change transition. */ @NonNull Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change, 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 b9ff5f0e820a..f3efaade945f 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 @@ -2884,6 +2884,7 @@ public class BubbleStackView extends FrameLayout /** Hide or show the manage menu for the currently expanded bubble. */ @VisibleForTesting public void showManageMenu(boolean show) { + if ((mManageMenu.getVisibility() == VISIBLE) == show) return; mShowingManage = show; // This should not happen, since the manage menu is only visible when there's an expanded diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 80e920fefe5f..28368ef37061 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -189,12 +189,13 @@ public abstract class WMShellBaseModule { static Optional<DragAndDropController> provideDragAndDropController(Context context, ShellInit shellInit, ShellController shellController, + ShellCommandHandler shellCommandHandler, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController, - displayController, uiEventLogger, iconProvider, mainExecutor)); + shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor)); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index d1760ed75547..76ca68bbfa75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -49,6 +49,12 @@ public class DesktopModeStatus { "persist.wm.debug.desktop_veiled_resizing", true); /** + * Flag to indicate is moving task to another display is enabled. + */ + public static final boolean IS_DISPLAY_CHANGE_ENABLED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_change_display", false); + + /** * Return {@code true} if desktop mode support is enabled */ public static boolean isProto1Enabled() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index c814fe575e81..73a0e362a744 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -235,6 +235,69 @@ class DesktopTasksController( } /** + * Move task to the next display. + * + * Queries all current known display ids and sorts them in ascending order. Then iterates + * through the list and looks for the display id that is larger than the display id for + * the passed in task. If a display with a higher id is not found, iterates through the list and + * finds the first display id that is not the display id for the passed in task. + * + * If a display matching the above criteria is found, re-parents the task to that display. + * No-op if no such display is found. + */ + fun moveToNextDisplay(taskId: Int) { + val task = shellTaskOrganizer.getRunningTaskInfo(taskId) + if (task == null) { + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d not found", taskId) + return + } + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d taskDisplayId=%d", + taskId, task.displayId) + + val displayIds = rootTaskDisplayAreaOrganizer.displayIds.sorted() + // Get the first display id that is higher than current task display id + var newDisplayId = displayIds.firstOrNull { displayId -> displayId > task.displayId } + if (newDisplayId == null) { + // No display with a higher id, get the first display id that is not the task display id + newDisplayId = displayIds.firstOrNull { displayId -> displayId < task.displayId } + } + if (newDisplayId == null) { + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: next display not found") + return + } + moveToDisplay(task, newDisplayId) + } + + /** + * Move [task] to display with [displayId]. + * + * No-op if task is already on that display per [RunningTaskInfo.displayId]. + */ + private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDisplay: taskId=%d displayId=%d", + task.taskId, displayId) + + if (task.displayId == displayId) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "moveToDisplay: task already on display") + return + } + + val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId) + if (displayAreaInfo == null) { + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToDisplay: display not found") + return + } + + val wct = WindowContainerTransaction() + wct.reparent(task.token, displayAreaInfo.token, true /* onTop */) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + + /** * Get windowing move for a given `taskId` * * @return [WindowingMode] for the task or [WINDOWING_MODE_UNDEFINED] if task is not found diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 091de3a86461..be2489da3628 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -35,10 +35,14 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP; + import android.content.ClipDescription; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; +import android.graphics.HardwareRenderer; import android.graphics.PixelFormat; import android.util.Slog; import android.util.SparseArray; @@ -50,6 +54,8 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; +import androidx.annotation.BinderThread; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; @@ -58,25 +64,31 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ExternalInterfaceBinder; +import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalMainThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import java.io.PrintWriter; import java.util.ArrayList; /** * Handles the global drag and drop handling for the Shell. */ -public class DragAndDropController implements DisplayController.OnDisplaysChangedListener, +public class DragAndDropController implements RemoteCallable<DragAndDropController>, + DisplayController.OnDisplaysChangedListener, View.OnDragListener, ComponentCallbacks2 { private static final String TAG = DragAndDropController.class.getSimpleName(); private final Context mContext; private final ShellController mShellController; + private final ShellCommandHandler mShellCommandHandler; private final DisplayController mDisplayController; private final DragAndDropEventLogger mLogger; private final IconProvider mIconProvider; @@ -100,6 +112,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange public static DragAndDropController create(Context context, ShellInit shellInit, ShellController shellController, + ShellCommandHandler shellCommandHandler, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @@ -107,19 +120,21 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) { return null; } - return new DragAndDropController(context, shellInit, shellController, displayController, - uiEventLogger, iconProvider, mainExecutor); + return new DragAndDropController(context, shellInit, shellController, shellCommandHandler, + displayController, uiEventLogger, iconProvider, mainExecutor); } DragAndDropController(Context context, ShellInit shellInit, ShellController shellController, + ShellCommandHandler shellCommandHandler, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) { mContext = context; mShellController = shellController; + mShellCommandHandler = shellCommandHandler; mDisplayController = displayController; mLogger = new DragAndDropEventLogger(uiEventLogger); mIconProvider = iconProvider; @@ -137,6 +152,23 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mMainExecutor.executeDelayed(() -> { mDisplayController.addDisplayWindowListener(this); }, 0); + mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP, + this::createExternalInterface, this); + mShellCommandHandler.addDumpCallback(this::dump, this); + } + + private ExternalInterfaceBinder createExternalInterface() { + return new IDragAndDropImpl(this); + } + + @Override + public Context getContext() { + return mContext; + } + + @Override + public ShellExecutor getRemoteCallExecutor() { + return mMainExecutor; } /** @@ -156,7 +188,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mListeners.remove(listener); } - private void notifyListeners() { + private void notifyDragStarted() { for (int i = 0; i < mListeners.size(); i++) { mListeners.get(i).onDragStarted(); } @@ -273,7 +305,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId), event.getClipData(), loggerSessionId); setDropTargetWindowVisibility(pd, View.VISIBLE); - notifyListeners(); + notifyDragStarted(); break; case ACTION_DRAG_ENTERED: pd.dragLayout.show(); @@ -327,13 +359,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange } private void setDropTargetWindowVisibility(PerDisplay pd, int visibility) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, - "Set drop target window visibility: displayId=%d visibility=%d", - pd.displayId, visibility); - pd.rootView.setVisibility(visibility); - if (visibility == View.VISIBLE) { - pd.rootView.requestApplyInsets(); - } + pd.setWindowVisibility(visibility); } private String getMimeTypes(ClipDescription description) { @@ -347,6 +373,18 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange return mimeTypes; } + /** + * Returns if any displays are currently ready to handle a drag/drop. + */ + private boolean isReadyToHandleDrag() { + for (int i = 0; i < mDisplayDropTargets.size(); i++) { + if (mDisplayDropTargets.valueAt(i).mHasDrawn) { + return true; + } + } + return false; + } + // Note: Component callbacks are always called on the main thread of the process @ExternalMainThread @Override @@ -372,12 +410,53 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange // Do nothing } - private static class PerDisplay { + /** + * Dumps information about this controller. + */ + public void dump(@NonNull PrintWriter pw, String prefix) { + pw.println(prefix + TAG); + pw.println(prefix + " listeners=" + mListeners.size()); + } + + /** + * The interface for calls from outside the host process. + */ + @BinderThread + private static class IDragAndDropImpl extends IDragAndDrop.Stub + implements ExternalInterfaceBinder { + private DragAndDropController mController; + + public IDragAndDropImpl(DragAndDropController controller) { + mController = controller; + } + + /** + * Invalidates this instance, preventing future calls from updating the controller. + */ + @Override + public void invalidate() { + mController = null; + } + + @Override + public boolean isReadyToHandleDrag() { + boolean[] result = new boolean[1]; + executeRemoteCallWithTaskPermission(mController, "isReadyToHandleDrag", + controller -> result[0] = controller.isReadyToHandleDrag(), + true /* blocking */ + ); + return result[0]; + } + } + + private static class PerDisplay implements HardwareRenderer.FrameDrawingCallback { final int displayId; final Context context; final WindowManager wm; final FrameLayout rootView; final DragLayout dragLayout; + // Tracks whether the window has fully drawn since it was last made visible + boolean mHasDrawn; boolean isHandlingDrag; // A count of the number of active drags in progress to ensure that we only hide the window @@ -391,5 +470,25 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange rootView = rv; dragLayout = dl; } + + private void setWindowVisibility(int visibility) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Set drop target window visibility: displayId=%d visibility=%d", + displayId, visibility); + rootView.setVisibility(visibility); + if (visibility == View.VISIBLE) { + rootView.requestApplyInsets(); + if (!mHasDrawn && rootView.getViewRootImpl() != null) { + rootView.getViewRootImpl().registerRtFrameCallback(this); + } + } else { + mHasDrawn = false; + } + } + + @Override + public void onFrameDraw(long frame) { + mHasDrawn = true; + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/IDragAndDrop.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/IDragAndDrop.aidl new file mode 100644 index 000000000000..aeb0c63fa4c5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/IDragAndDrop.aidl @@ -0,0 +1,28 @@ +/* + * 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.wm.shell.draganddrop; + +/** + * Interface that is exposed to remote callers to manipulate drag and drop. + */ +interface IDragAndDrop { + /** + * Returns whether the shell drop target is showing and will handle a drag/drop. + */ + boolean isReadyToHandleDrag() = 1; +} +// Last id = 1
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java index bfa63909cd47..5f54f58557d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java @@ -42,4 +42,6 @@ public class ShellSharedConstants { public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; // See IDesktopMode.aidl public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; + // See IDragAndDrop.aidl + public static final String KEY_EXTRA_SHELL_DRAG_AND_DROP = "extra_shell_drag_and_drop"; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 6e9ecdaf55e7..1ee52ae00645 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -21,6 +21,7 @@ import static android.app.ActivityOptions.ANIM_CUSTOM; import static android.app.ActivityOptions.ANIM_NONE; import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; import static android.app.ActivityOptions.ANIM_SCALE_UP; +import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; @@ -625,6 +626,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) { // This received a transferred starting window, so don't animate return null; + } else if (overrideType == ANIM_SCENE_TRANSITION) { + // If there's a scene-transition, then jump-cut. + return null; } else { a = loadAttributeAnimation(info, change, wallpaperTransit, mTransitionAnimation); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index f79f1f7a27b1..a73ab776486b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -533,8 +533,12 @@ public class Transitions implements RemoteCallable<Transitions>, final int layer; // Put all the OPEN/SHOW on top if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { - // Wallpaper is always at the bottom. - layer = -zSplitLine; + // Wallpaper is always at the bottom, opening wallpaper on top of closing one. + if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { + layer = -zSplitLine + numChanges - i; + } else { + layer = -zSplitLine - i; + } } else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { if (isOpening) { // put on top diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 4cda5715ac1f..12edc3548642 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -326,6 +326,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeHandleMenu(); } else if (id == R.id.collapse_menu_button) { decoration.closeHandleMenu(); + } else if (id == R.id.select_button) { + if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) { + // TODO(b/278084491): dev option to enable display switching + // remove when select is implemented + mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId)); + decoration.closeHandleMenu(); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index ed3cca078084..ac4a597c15d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -187,6 +187,8 @@ class HandleMenu { final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView(); final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button); closeBtn.setOnClickListener(mOnClickListener); + final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button); + selectBtn.setOnClickListener(mOnClickListener); } /** diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index b6d92814ad43..b5937ae80f0a 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -21,6 +21,9 @@ <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" /> <!-- Ensure output directory is empty at the start --> <option name="run-command" value="rm -rf /sdcard/flicker" /> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480" /> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index f506969f51df..1335ebf105a6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -31,6 +31,7 @@ import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.DisplayAreaInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER @@ -344,6 +345,57 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveToNextDisplay_noOtherDisplays() { + whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY)) + val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) + controller.moveToNextDisplay(task.taskId) + verifyWCTNotExecuted() + } + + @Test + fun moveToNextDisplay_moveFromFirstToSecondDisplay() { + // Set up two display ids + whenever(rootTaskDisplayAreaOrganizer.displayIds) + .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY)) + // Create a mock for the target display area: second display + val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0) + whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY)) + .thenReturn(secondDisplayArea) + + val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) + controller.moveToNextDisplay(task.taskId) + with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + assertThat(hierarchyOps).hasSize(1) + assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) + assertThat(hierarchyOps[0].isReparent).isTrue() + assertThat(hierarchyOps[0].newParent).isEqualTo(secondDisplayArea.token.asBinder()) + assertThat(hierarchyOps[0].toTop).isTrue() + } + } + + @Test + fun moveToNextDisplay_moveFromSecondToFirstDisplay() { + // Set up two display ids + whenever(rootTaskDisplayAreaOrganizer.displayIds) + .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY)) + // Create a mock for the target display area: default display + val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) + whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) + .thenReturn(defaultDisplayArea) + + val task = setUpFreeformTask(displayId = SECOND_DISPLAY) + controller.moveToNextDisplay(task.taskId) + + with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + assertThat(hierarchyOps).hasSize(1) + assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) + assertThat(hierarchyOps[0].isReparent).isTrue() + assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder()) + assertThat(hierarchyOps[0].toTop).isTrue() + } + } + + @Test fun getTaskWindowingMode() { val fullscreenTask = setUpFullscreenTask() val freeformTask = setUpFreeformTask() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index 523cb6629d9a..54f36f61859d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -48,6 +48,7 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -71,6 +72,8 @@ public class DragAndDropControllerTest extends ShellTestCase { @Mock private ShellController mShellController; @Mock + private ShellCommandHandler mShellCommandHandler; + @Mock private DisplayController mDisplayController; @Mock private UiEventLogger mUiEventLogger; @@ -89,7 +92,8 @@ public class DragAndDropControllerTest extends ShellTestCase { public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mController = new DragAndDropController(mContext, mShellInit, mShellController, - mDisplayController, mUiEventLogger, mIconProvider, mMainExecutor); + mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider, + mMainExecutor); mController.onInit(); } diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 7e9d44fbdbd1..c00a2707e0a2 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -29,6 +29,7 @@ #include "Layer.h" #include "Properties.h" #include "RenderThread.h" +#include "VulkanManager.h" #include "pipeline/skia/ATraceMemoryDump.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" @@ -182,8 +183,14 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) } log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts); + auto vkInstance = VulkanManager::peekInstance(); if (!mGrContext) { - log.appendFormat("No GPU context.\n"); + if (!vkInstance) { + log.appendFormat("No GPU context.\n"); + } else { + log.appendFormat("No GrContext; however %d remaining Vulkan refs", + vkInstance->getStrongCount() - 1); + } return; } std::vector<skiapipeline::ResourcePair> cpuResourceMap = { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index f198bca9060c..4cffc6c2efe3 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -107,11 +107,11 @@ GrVkGetProc VulkanManager::sSkiaGetProp = [](const char* proc_name, VkInstance i #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -sp<VulkanManager> VulkanManager::getInstance() { - // cache a weakptr to the context to enable a second thread to share the same vulkan state - static wp<VulkanManager> sWeakInstance = nullptr; - static std::mutex sLock; +// cache a weakptr to the context to enable a second thread to share the same vulkan state +static wp<VulkanManager> sWeakInstance = nullptr; +static std::mutex sLock; +sp<VulkanManager> VulkanManager::getInstance() { std::lock_guard _lock{sLock}; sp<VulkanManager> vulkanManager = sWeakInstance.promote(); if (!vulkanManager.get()) { @@ -122,6 +122,11 @@ sp<VulkanManager> VulkanManager::getInstance() { return vulkanManager; } +sp<VulkanManager> VulkanManager::peekInstance() { + std::lock_guard _lock{sLock}; + return sWeakInstance.promote(); +} + VulkanManager::~VulkanManager() { if (mDevice != VK_NULL_HANDLE) { mDeviceWaitIdle(mDevice); @@ -404,9 +409,13 @@ void VulkanManager::initialize() { } } -sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options, - ContextType contextType) { +static void onGrContextReleased(void* context) { + VulkanManager* manager = (VulkanManager*)context; + manager->decStrong((void*)onGrContextReleased); +} +sk_sp<GrDirectContext> VulkanManager::createContext(GrContextOptions& options, + ContextType contextType) { GrVkBackendContext backendContext; backendContext.fInstance = mInstance; backendContext.fPhysicalDevice = mPhysicalDevice; @@ -418,6 +427,11 @@ sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& opti backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; backendContext.fGetProc = sSkiaGetProp; + LOG_ALWAYS_FATAL_IF(options.fContextDeleteProc != nullptr, "Conflicting fContextDeleteProcs!"); + this->incStrong((void*)onGrContextReleased); + options.fContextDeleteContext = this; + options.fContextDeleteProc = onGrContextReleased; + return GrDirectContext::MakeVulkan(backendContext, options); } diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index c5196eeccea3..00a40c0c85c3 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -66,6 +66,7 @@ class RenderThread; class VulkanManager final : public RefBase { public: static sp<VulkanManager> getInstance(); + static sp<VulkanManager> peekInstance(); // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply @@ -109,7 +110,7 @@ public: }; // returns a Skia graphic context used to draw content on the specified thread - sk_sp<GrDirectContext> createContext(const GrContextOptions& options, + sk_sp<GrDirectContext> createContext(GrContextOptions& options, ContextType contextType = ContextType::kRenderThread); uint32_t getDriverVersion() const { return mDriverVersion; } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index e73cf87ba9f3..3123ee6dd4d7 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1237,6 +1237,9 @@ public class AudioSystem public static final Set<Integer> DEVICE_IN_ALL_SCO_SET; /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_USB_SET; + /** @hide */ + public static final Set<Integer> DEVICE_IN_ALL_BLE_SET; + static { DEVICE_IN_ALL_SET = new HashSet<>(); DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION); @@ -1276,6 +1279,66 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); + + DEVICE_IN_ALL_BLE_SET = new HashSet<>(); + DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET); + } + + /** @hide */ + public static boolean isBluetoothDevice(int deviceType) { + return isBluetoothA2dpOutDevice(deviceType) + || isBluetoothScoDevice(deviceType) + || isBluetoothLeDevice(deviceType); + } + + /** @hide */ + public static boolean isBluetoothOutDevice(int deviceType) { + return isBluetoothA2dpOutDevice(deviceType) + || isBluetoothScoOutDevice(deviceType) + || isBluetoothLeOutDevice(deviceType); + } + + /** @hide */ + public static boolean isBluetoothInDevice(int deviceType) { + return isBluetoothScoInDevice(deviceType) + || isBluetoothLeInDevice(deviceType); + } + + /** @hide */ + public static boolean isBluetoothA2dpOutDevice(int deviceType) { + return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType); + } + + /** @hide */ + public static boolean isBluetoothScoOutDevice(int deviceType) { + return DEVICE_OUT_ALL_SCO_SET.contains(deviceType); + } + + /** @hide */ + public static boolean isBluetoothScoInDevice(int deviceType) { + return DEVICE_IN_ALL_SCO_SET.contains(deviceType); + } + + /** @hide */ + public static boolean isBluetoothScoDevice(int deviceType) { + return isBluetoothScoOutDevice(deviceType) + || isBluetoothScoInDevice(deviceType); + } + + /** @hide */ + public static boolean isBluetoothLeOutDevice(int deviceType) { + return DEVICE_OUT_ALL_BLE_SET.contains(deviceType); + } + + /** @hide */ + public static boolean isBluetoothLeInDevice(int deviceType) { + return DEVICE_IN_ALL_BLE_SET.contains(deviceType); + } + + /** @hide */ + public static boolean isBluetoothLeDevice(int deviceType) { + return isBluetoothLeOutDevice(deviceType) + || isBluetoothLeInDevice(deviceType); } /** @hide */ diff --git a/native/android/activity_manager.cpp b/native/android/activity_manager.cpp index 155a355241c8..bc6a84f01517 100644 --- a/native/android/activity_manager.cpp +++ b/native/android/activity_manager.cpp @@ -45,7 +45,7 @@ struct UidObserver : public BnUidObserver, public virtual IBinder::DeathRecipien void onUidIdle(uid_t uid, bool disabled) override; void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq, int32_t capability) override; - void onUidProcAdjChanged(uid_t uid) override; + void onUidProcAdjChanged(uid_t uid, int32_t adj) override; // IBinder::DeathRecipient implementation void binderDied(const wp<IBinder>& who) override; @@ -121,7 +121,7 @@ void UidObserver::onUidActive(uid_t uid __unused) {} void UidObserver::onUidIdle(uid_t uid __unused, bool disabled __unused) {} -void UidObserver::onUidProcAdjChanged(uid_t uid __unused) {} +void UidObserver::onUidProcAdjChanged(uid_t uid __unused, int32_t adj __unused) {} void UidObserver::onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq __unused, diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 340285760cda..7d71bd5c91ab 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -78,8 +78,12 @@ <!-- This text is followed by a list of one or more options. [CHAR LIMIT=200] --> <string name="save_credential_to_title">Save <xliff:g id="credentialTypes" example="passkey">%1$s</xliff:g> to</string> - <!-- This appears as the title of the modal bottom sheet for users to choose to create a passkey on another device. [CHAR LIMIT=200] --> - <string name="create_passkey_in_other_device_title">Create passkey in another device?</string> + <!-- This appears as the title of the modal bottom sheet for users to confirm to create a passkey on another device. [CHAR LIMIT=200] --> + <string name="create_passkey_in_other_device_title">Create passkey on another device?</string> + <!-- This appears as the title of the modal bottom sheet for users to confirm to save a password on another device. [CHAR LIMIT=200] --> + <string name="save_password_on_other_device_title">Save password on another device?</string> + <!-- This appears as the title of the modal bottom sheet for users to confirm to save a sign-in credential on another device. [CHAR LIMIT=200] --> + <string name="save_sign_in_on_other_device_title">Save sign-in on another device?</string> <!-- This appears as the title of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=200] --> <string name="use_provider_for_all_title">Use <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g> for all your sign-ins?</string> <!-- This appears as the description body of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=300] --> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 6549b2d0187c..1fb5e3f853b2 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -50,6 +50,8 @@ class CredentialSelectorActivity : ComponentActivity() { super.onCreate(savedInstanceState) overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN, 0, 0) + overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE, + 0, 0) Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity") try { val (isCancellationRequest, shouldShowCancellationUi, _) = diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index dfa517b118b3..d16120fee452 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -465,7 +465,17 @@ fun ExternalOnlySelectionCard( SheetContainerCard { item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) } item { Divider(thickness = 16.dp, color = Color.Transparent) } - item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) } + item { + HeadlineText( + text = stringResource( + when (requestDisplayInfo.type) { + CredentialType.PASSKEY -> R.string.create_passkey_in_other_device_title + CredentialType.PASSWORD -> R.string.save_password_on_other_device_title + else -> R.string.save_sign_in_on_other_device_title + } + ) + ) + } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java index 4c5875b1f2d9..7b17cbdd3a1e 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java @@ -16,14 +16,14 @@ package com.android.packageinstaller; +import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID; + import android.app.Activity; import android.content.Intent; import android.os.Bundle; import androidx.annotation.Nullable; -import java.io.File; - /** * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult. */ @@ -52,8 +52,13 @@ public class DeleteStagedFileOnResult extends Activity { super.onDestroy(); if (isFinishing()) { - File sourceFile = new File(getIntent().getData().getPath()); - new Thread(sourceFile::delete).start(); + // While we expect PIA/InstallStaging to abandon/commit the session, still there + // might be cases when the session becomes orphan. + int sessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0); + try { + getPackageManager().getPackageInstaller().abandonSession(sessionId); + } catch (SecurityException ignored) { + } } } } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index c6217ece800d..7bea33971259 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -16,17 +16,17 @@ package com.android.packageinstaller; +import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID; + import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInstaller; -import android.content.pm.PackageInstaller.InstallInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Process; import android.util.Log; import android.view.View; import android.widget.Button; @@ -34,10 +34,7 @@ import android.widget.Button; import androidx.annotation.Nullable; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; /** * Send package to the package manager and handle results from package manager. Once the @@ -77,7 +74,7 @@ public class InstallInstalling extends AlertActivity { .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = getIntent().getData(); - if ("package".equals(mPackageURI.getScheme())) { + if (PackageInstallerActivity.SCHEME_PACKAGE.equals(mPackageURI.getScheme())) { try { getPackageManager().installExistingPackage(appInfo.packageName); launchSuccess(); @@ -86,6 +83,8 @@ public class InstallInstalling extends AlertActivity { PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } else { + // ContentResolver.SCHEME_FILE + // STAGED_SESSION_ID extra contains an ID of a previously staged install session. final File sourceFile = new File(mPackageURI.getPath()); PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile); @@ -122,41 +121,6 @@ public class InstallInstalling extends AlertActivity { // Does not happen } } else { - PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( - PackageInstaller.SessionParams.MODE_FULL_INSTALL); - final Uri referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER); - params.setPackageSource( - referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE - : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE); - params.setInstallAsInstantApp(false); - params.setReferrerUri(referrerUri); - params.setOriginatingUri(getIntent() - .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI)); - params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, - Process.INVALID_UID)); - params.setInstallerPackageName(getIntent().getStringExtra( - Intent.EXTRA_INSTALLER_PACKAGE_NAME)); - params.setInstallReason(PackageManager.INSTALL_REASON_USER); - - File file = new File(mPackageURI.getPath()); - try { - final InstallInfo result = getPackageManager().getPackageInstaller() - .readInstallInfo(file, 0); - params.setAppPackageName(result.getPackageName()); - params.setInstallLocation(result.getInstallLocation()); - try { - params.setSize(result.calculateInstalledSize(params)); - } catch (IOException e) { - e.printStackTrace(); - params.setSize(file.length()); - } - } catch (PackageInstaller.PackageParsingException e) { - - Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.", e); - Log.e(LOG_TAG, - "Cannot calculate installed size " + file + ". Try only apk size."); - params.setSize(file.length()); - } try { mInstallId = InstallEventReceiver .addObserver(this, EventResultPersister.GENERATE_NEW_ID, @@ -166,9 +130,14 @@ public class InstallInstalling extends AlertActivity { PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } - try { - mSessionId = getPackageManager().getPackageInstaller().createSession(params); - } catch (IOException e) { + mSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0); + // Try to open session previously staged in InstallStaging. + try (PackageInstaller.Session ignored = + getPackageManager().getPackageInstaller().openSession( + mSessionId)) { + Log.d(LOG_TAG, "Staged session is valid, proceeding with the install"); + } catch (IOException | SecurityException e) { + Log.e(LOG_TAG, "Invalid session id passed", e); launchFailure(PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } @@ -293,57 +262,9 @@ public class InstallInstalling extends AlertActivity { @Override protected PackageInstaller.Session doInBackground(Void... params) { - PackageInstaller.Session session; try { - session = getPackageManager().getPackageInstaller().openSession(mSessionId); + return getPackageManager().getPackageInstaller().openSession(mSessionId); } catch (IOException e) { - synchronized (this) { - isDone = true; - notifyAll(); - } - return null; - } - - session.setStagingProgress(0); - - try { - File file = new File(mPackageURI.getPath()); - - try (InputStream in = new FileInputStream(file)) { - long sizeBytes = file.length(); - long totalRead = 0; - try (OutputStream out = session - .openWrite("PackageInstaller", 0, sizeBytes)) { - byte[] buffer = new byte[1024 * 1024]; - while (true) { - int numRead = in.read(buffer); - - if (numRead == -1) { - session.fsync(out); - break; - } - - if (isCancelled()) { - session.close(); - break; - } - - out.write(buffer, 0, numRead); - if (sizeBytes > 0) { - totalRead += numRead; - float fraction = ((float) totalRead / (float) sizeBytes); - session.setStagingProgress(fraction); - } - } - } - } - - return session; - } catch (IOException | SecurityException e) { - Log.e(LOG_TAG, "Could not write package", e); - - session.close(); - return null; } finally { synchronized (this) { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java index 68de2f67bd94..097e47f58db1 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java @@ -16,6 +16,10 @@ package com.android.packageinstaller; +import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH; + +import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID; + import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -23,40 +27,49 @@ import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.content.res.AssetFileDescriptor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.Process; import android.util.Log; import android.view.View; +import android.widget.ProgressBar; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** - * If a package gets installed from an content URI this step loads the package and turns it into - * and installation from a file. Then it re-starts the installation as usual. + * If a package gets installed from a content URI this step stages the installation session + * reading bytes from the URI. */ public class InstallStaging extends AlertActivity { private static final String LOG_TAG = InstallStaging.class.getSimpleName(); - private static final String STAGED_FILE = "STAGED_FILE"; + private static final String STAGED_SESSION_ID = "STAGED_SESSION_ID"; + + private @Nullable PackageInstaller mInstaller; /** Currently running task that loads the file from the content URI into a file */ private @Nullable StagingAsyncTask mStagingTask; - /** The file the package is in */ - private @Nullable File mStagedFile; + /** The session the package is in */ + private int mStagedSessionId; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mInstaller = getPackageManager().getPackageInstaller(); + setFinishOnTouchOutside(true); mAlert.setIcon(R.drawable.ic_file_download); mAlert.setTitle(getString(R.string.app_name_unknown)); @@ -66,6 +79,9 @@ public class InstallStaging extends AlertActivity { if (mStagingTask != null) { mStagingTask.cancel(true); } + + cleanupStagingSession(); + setResult(RESULT_CANCELED); finish(); }, null); @@ -73,11 +89,7 @@ public class InstallStaging extends AlertActivity { requireViewById(R.id.staging).setVisibility(View.VISIBLE); if (savedInstanceState != null) { - mStagedFile = new File(savedInstanceState.getString(STAGED_FILE)); - - if (!mStagedFile.exists()) { - mStagedFile = null; - } + mStagedSessionId = savedInstanceState.getInt(STAGED_SESSION_ID, 0); } } @@ -85,21 +97,41 @@ public class InstallStaging extends AlertActivity { protected void onResume() { super.onResume(); - // This is the first onResume in a single life of the activity + // This is the first onResume in a single life of the activity. if (mStagingTask == null) { - // File does not exist, or became invalid - if (mStagedFile == null) { - // Create file delayed to be able to show error + if (mStagedSessionId > 0) { + final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo( + mStagedSessionId); + if (info == null || !info.isActive() || info.getResolvedBaseApkPath() == null) { + Log.w(LOG_TAG, "Session " + mStagedSessionId + " in funky state; ignoring"); + if (info != null) { + cleanupStagingSession(); + } + mStagedSessionId = 0; + } + } + + // Session does not exist, or became invalid. + if (mStagedSessionId <= 0) { + // Create session here to be able to show error. + final Uri packageUri = getIntent().getData(); + final AssetFileDescriptor afd = openAssetFileDescriptor(packageUri); try { - mStagedFile = TemporaryFileManager.getStagedFile(this); + ParcelFileDescriptor pfd = afd != null ? afd.getParcelFileDescriptor() : null; + PackageInstaller.SessionParams params = createSessionParams( + mInstaller, getIntent(), pfd, packageUri.toString()); + mStagedSessionId = mInstaller.createSession(params); } catch (IOException e) { + Log.w(LOG_TAG, "Failed to create a staging session", e); showError(); return; + } finally { + PackageUtil.safeClose(afd); } } mStagingTask = new StagingAsyncTask(); - mStagingTask.execute(getIntent().getData()); + mStagingTask.execute(); } } @@ -107,7 +139,7 @@ public class InstallStaging extends AlertActivity { protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(STAGED_FILE, mStagedFile.getPath()); + outState.putInt(STAGED_SESSION_ID, mStagedSessionId); } @Override @@ -119,6 +151,65 @@ public class InstallStaging extends AlertActivity { super.onDestroy(); } + private AssetFileDescriptor openAssetFileDescriptor(Uri uri) { + try { + return getContentResolver().openAssetFileDescriptor(uri, "r"); + } catch (Exception e) { + Log.w(LOG_TAG, "Failed to open asset file descriptor", e); + return null; + } + } + + private static PackageInstaller.SessionParams createSessionParams( + @NonNull PackageInstaller installer, @NonNull Intent intent, + @Nullable ParcelFileDescriptor pfd, @NonNull String debugPathName) { + PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL); + final Uri referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER); + params.setPackageSource( + referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE + : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE); + params.setInstallAsInstantApp(false); + params.setReferrerUri(referrerUri); + params.setOriginatingUri(intent + .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI)); + params.setOriginatingUid(intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID, + Process.INVALID_UID)); + params.setInstallerPackageName(intent.getStringExtra( + Intent.EXTRA_INSTALLER_PACKAGE_NAME)); + params.setInstallReason(PackageManager.INSTALL_REASON_USER); + + if (pfd != null) { + try { + final PackageInstaller.InstallInfo result = installer.readInstallInfo(pfd, + debugPathName, 0); + params.setAppPackageName(result.getPackageName()); + params.setInstallLocation(result.getInstallLocation()); + params.setSize(result.calculateInstalledSize(params, pfd)); + } catch (PackageInstaller.PackageParsingException | IOException e) { + Log.e(LOG_TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.", e); + Log.e(LOG_TAG, + "Cannot calculate installed size " + debugPathName + + ". Try only apk size."); + params.setSize(pfd.getStatSize()); + } + } else { + Log.e(LOG_TAG, "Cannot parse package " + debugPathName + ". Assuming defaults."); + } + return params; + } + + private void cleanupStagingSession() { + if (mStagedSessionId > 0) { + try { + mInstaller.abandonSession(mStagedSessionId); + } catch (SecurityException ignored) { + + } + mStagedSessionId = 0; + } + } + /** * Show an error message and set result as error. */ @@ -165,58 +256,109 @@ public class InstallStaging extends AlertActivity { } } - private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> { + private final class StagingAsyncTask extends + AsyncTask<Void, Integer, PackageInstaller.SessionInfo> { + private ProgressBar mProgressBar = null; + + private long getContentSizeBytes() { + try (AssetFileDescriptor afd = openAssetFileDescriptor(getIntent().getData())) { + return afd != null ? afd.getLength() : UNKNOWN_LENGTH; + } catch (IOException ignored) { + return UNKNOWN_LENGTH; + } + } + @Override - protected Boolean doInBackground(Uri... params) { - if (params == null || params.length <= 0) { - return false; + protected void onPreExecute() { + final long sizeBytes = getContentSizeBytes(); + + mProgressBar = sizeBytes > 0 ? requireViewById(R.id.progress_indeterminate) : null; + if (mProgressBar != null) { + mProgressBar.setProgress(0); + mProgressBar.setMax(100); + mProgressBar.setIndeterminate(false); } - Uri packageUri = params[0]; - try (InputStream in = getContentResolver().openInputStream(packageUri)) { - // Despite the comments in ContentResolver#openInputStream the returned stream can - // be null. + } + + @Override + protected PackageInstaller.SessionInfo doInBackground(Void... params) { + Uri packageUri = getIntent().getData(); + try (PackageInstaller.Session session = mInstaller.openSession(mStagedSessionId); + InputStream in = getContentResolver().openInputStream(packageUri)) { + session.setStagingProgress(0); + if (in == null) { - return false; + return null; } - try (OutputStream out = new FileOutputStream(mStagedFile)) { + long sizeBytes = getContentSizeBytes(); + + long totalRead = 0; + try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) { byte[] buffer = new byte[1024 * 1024]; - int bytesRead; - while ((bytesRead = in.read(buffer)) >= 0) { - // Be nice and respond to a cancellation + while (true) { + int numRead = in.read(buffer); + + if (numRead == -1) { + session.fsync(out); + break; + } + if (isCancelled()) { - return false; + break; + } + + out.write(buffer, 0, numRead); + if (sizeBytes > 0) { + totalRead += numRead; + float fraction = ((float) totalRead / (float) sizeBytes); + session.setStagingProgress(fraction); + publishProgress((int) (fraction * 100.0)); } - out.write(buffer, 0, bytesRead); } } + + return mInstaller.getSessionInfo(mStagedSessionId); } catch (IOException | SecurityException | IllegalStateException - | IllegalArgumentException e) { + | IllegalArgumentException e) { Log.w(LOG_TAG, "Error staging apk from content URI", e); - return false; + return null; } - return true; } @Override - protected void onPostExecute(Boolean success) { - if (success) { - // Now start the installation again from a file - Intent installIntent = new Intent(getIntent()); - installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class); - installIntent.setData(Uri.fromFile(mStagedFile)); - - if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { - installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - } - - installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - startActivity(installIntent); + protected void onProgressUpdate(Integer... progress) { + if (mProgressBar != null && progress != null && progress.length > 0) { + mProgressBar.setProgress(progress[0], true); + } + } - InstallStaging.this.finish(); - } else { + @Override + protected void onPostExecute(PackageInstaller.SessionInfo sessionInfo) { + if (sessionInfo == null || !sessionInfo.isActive() + || sessionInfo.getResolvedBaseApkPath() == null) { + Log.w(LOG_TAG, "Session info is invalid: " + sessionInfo); + cleanupStagingSession(); showError(); + return; + } + + // Pass the staged session to the installer. + Intent installIntent = new Intent(getIntent()); + installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class); + installIntent.setData(Uri.fromFile(new File(sessionInfo.getResolvedBaseApkPath()))); + + installIntent.putExtra(EXTRA_STAGED_SESSION_ID, mStagedSessionId); + + if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { + installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } + + installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + + startActivity(installIntent); + + InstallStaging.this.finish(); } } } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java index ac32020e842a..3f98867cb267 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java @@ -77,7 +77,21 @@ public class InstallStart extends Activity { } final ApplicationInfo sourceInfo = getSourceInfo(callingPackage); - final int originatingUid = getOriginatingUid(sourceInfo); + // Uid of the source package, coming from ActivityManager + int callingUid = getLaunchedFromUid(); + if (callingUid == Process.INVALID_UID) { + // Cannot reach ActivityManager. Aborting install. + Log.e(LOG_TAG, "Could not determine the launching uid."); + } + // Uid of the source package, with a preference to uid from ApplicationInfo + final int originatingUid = sourceInfo != null ? sourceInfo.uid : callingUid; + + if (callingUid == Process.INVALID_UID && sourceInfo == null) { + mAbortInstall = true; + } + + boolean isDocumentsManager = checkPermission(Manifest.permission.MANAGE_DOCUMENTS, + -1, callingUid) == PackageManager.PERMISSION_GRANTED; boolean isTrustedSource = false; if (sourceInfo != null && sourceInfo.isPrivilegedApp()) { isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false) || ( @@ -86,7 +100,8 @@ public class InstallStart extends Activity { == PackageManager.PERMISSION_GRANTED); } - if (!isTrustedSource && originatingUid != Process.INVALID_UID) { + if (!isTrustedSource && !isSystemDownloadsProvider(callingUid) && !isDocumentsManager + && originatingUid != Process.INVALID_UID) { final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid); if (targetSdkVersion < 0) { Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid); @@ -144,14 +159,15 @@ public class InstallStart extends Activity { if (packageUri != null && packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT) - && canPackageQuery(originatingUid, packageUri)) { + && canPackageQuery(callingUid, packageUri)) { // [IMPORTANT] This path is deprecated, but should still work. Only necessary // features should be added. - // Copy file to prevent it from being changed underneath this process + // Stage a session with this file to prevent it from being changed underneath + // this process. nextActivity.setClass(this, InstallStaging.class); - } else if (packageUri != null && packageUri.getScheme().equals( - PackageInstallerActivity.SCHEME_PACKAGE)) { + } else if (packageUri != null && PackageInstallerActivity.SCHEME_PACKAGE.equals( + packageUri.getScheme())) { nextActivity.setClass(this, PackageInstallerActivity.class); } else { Intent result = new Intent(); @@ -212,41 +228,6 @@ public class InstallStart extends Activity { return null; } - /** - * Get the originating uid if possible, or {@link Process#INVALID_UID} if not available - * - * @param sourceInfo The source of this installation - * @return The UID of the installation source or INVALID_UID - */ - private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) { - // The originating uid from the intent. We only trust/use this if it comes from either - // the document manager app or the downloads provider - final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, - Process.INVALID_UID); - - final int callingUid; - if (sourceInfo != null) { - callingUid = sourceInfo.uid; - } else { - callingUid = getLaunchedFromUid(); - if (callingUid == Process.INVALID_UID) { - // Cannot reach ActivityManager. Aborting install. - Log.e(LOG_TAG, "Could not determine the launching uid."); - mAbortInstall = true; - return Process.INVALID_UID; - } - } - if (checkPermission(Manifest.permission.MANAGE_DOCUMENTS, -1, callingUid) - == PackageManager.PERMISSION_GRANTED) { - return uidFromIntent; - } - if (isSystemDownloadsProvider(callingUid)) { - return uidFromIntent; - } - // We don't trust uid from the intent. Use the calling uid instead. - return callingUid; - } - private boolean isSystemDownloadsProvider(int uid) { final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider( DOWNLOADS_AUTHORITY, 0); @@ -260,8 +241,7 @@ public class InstallStart extends Activity { } @NonNull - private boolean canPackageQuery(int originatingUid, Uri packageUri) { - String callingPackage = mPackageManager.getPackagesForUid(originatingUid)[0]; + private boolean canPackageQuery(int callingUid, Uri packageUri) { ProviderInfo info = mPackageManager.resolveContentProvider(packageUri.getAuthority(), PackageManager.ComponentInfoFlags.of(0)); if (info == null) { @@ -269,11 +249,20 @@ public class InstallStart extends Activity { } String targetPackage = info.packageName; - try { - return mPackageManager.canPackageQuery(callingPackage, targetPackage); - } catch (PackageManager.NameNotFoundException e) { + String[] callingPackages = mPackageManager.getPackagesForUid(callingUid); + if (callingPackages == null) { return false; } + for (String callingPackage: callingPackages) { + try { + if (mPackageManager.canPackageQuery(callingPackage, targetPackage)) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // no-op + } + } + return false; } private boolean isCallerSessionOwner(int originatingUid, int sessionId) { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 3ba2acb113d3..7e294eee024f 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -82,6 +82,7 @@ public class PackageInstallerActivity extends AlertActivity { static final String EXTRA_CALLING_PACKAGE = "EXTRA_CALLING_PACKAGE"; static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG"; static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO"; + static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID"; private static final String ALLOW_UNKNOWN_SOURCES_KEY = PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY"; @@ -403,6 +404,10 @@ public class PackageInstallerActivity extends AlertActivity { mReferrerURI = null; mPendingUserActionReason = info.getPendingUserActionReason(); } else { + // Two possible callers: + // 1. InstallStart with "SCHEME_PACKAGE". + // 2. InstallStaging with "SCHEME_FILE" and EXTRA_STAGED_SESSION_ID with staged + // session id. mSessionId = -1; packageSource = intent.getData(); mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); @@ -721,14 +726,16 @@ public class PackageInstallerActivity extends AlertActivity { } private void startInstall() { + String installerPackageName = getIntent().getStringExtra( + Intent.EXTRA_INSTALLER_PACKAGE_NAME); + int stagedSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0); + // Start subactivity to actually install the application Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.setClass(this, InstallInstalling.class); - String installerPackageName = getIntent().getStringExtra( - Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (mOriginatingURI != null) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); } @@ -745,6 +752,9 @@ public class PackageInstallerActivity extends AlertActivity { if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); } + if (stagedSessionId > 0) { + newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId); + } newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI); startActivity(newIntent); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java index 698159f24bb7..0270591acceb 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java @@ -33,7 +33,9 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.io.Closeable; import java.io.File; +import java.io.IOException; /** * This is a utility class for defining some utility methods and constants @@ -190,4 +192,19 @@ public class PackageUtil { } return targetSdkVersion; } + + + /** + * Quietly close a closeable resource (e.g. a stream or file). The input may already + * be closed and it may even be null. + */ + static void safeClose(Closeable resource) { + if (resource != null) { + try { + resource.close(); + } catch (IOException ioe) { + // Catch and discard the error + } + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index c9e831256cf4..6cf6825e61e0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -50,6 +50,7 @@ import android.text.TextUtils; import android.util.Log; import androidx.annotation.DoNotInline; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import com.android.internal.annotations.VisibleForTesting; @@ -638,6 +639,11 @@ public class InfoMediaManager extends MediaManager { } @Override + public void onSessionReleased(@NonNull RoutingSessionInfo session) { + refreshDevices(); + } + + @Override public void onRouteListingPreferenceUpdated( String packageName, RouteListingPreference routeListingPreference) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index 270fda89c212..096932706c88 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -141,6 +141,56 @@ public class InfoMediaManagerTest { } @Test + public void onSessionReleased_shouldUpdateConnectedDevice() { + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo sessionInfo1 = mock(RoutingSessionInfo.class); + routingSessionInfos.add(sessionInfo1); + final RoutingSessionInfo sessionInfo2 = mock(RoutingSessionInfo.class); + routingSessionInfos.add(sessionInfo2); + + final List<String> selectedRoutesSession1 = new ArrayList<>(); + selectedRoutesSession1.add(TEST_ID_1); + when(sessionInfo1.getSelectedRoutes()).thenReturn(selectedRoutesSession1); + + final List<String> selectedRoutesSession2 = new ArrayList<>(); + selectedRoutesSession2.add(TEST_ID_2); + when(sessionInfo2.getSelectedRoutes()).thenReturn(selectedRoutesSession2); + + mShadowRouter2Manager.setRoutingSessions(routingSessionInfos); + + final MediaRoute2Info info1 = mock(MediaRoute2Info.class); + when(info1.getId()).thenReturn(TEST_ID_1); + when(info1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + + final MediaRoute2Info info2 = mock(MediaRoute2Info.class); + when(info2.getId()).thenReturn(TEST_ID_2); + when(info2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + + final List<MediaRoute2Info> routes = new ArrayList<>(); + routes.add(info1); + routes.add(info2); + mShadowRouter2Manager.setAllRoutes(routes); + mShadowRouter2Manager.setTransferableRoutes(routes); + + final MediaDevice mediaDevice1 = mInfoMediaManager.findMediaDevice(TEST_ID_1); + assertThat(mediaDevice1).isNull(); + final MediaDevice mediaDevice2 = mInfoMediaManager.findMediaDevice(TEST_ID_2); + assertThat(mediaDevice2).isNull(); + + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); + final MediaDevice infoDevice1 = mInfoMediaManager.mMediaDevices.get(0); + assertThat(infoDevice1.getId()).isEqualTo(TEST_ID_1); + final MediaDevice infoDevice2 = mInfoMediaManager.mMediaDevices.get(1); + assertThat(infoDevice2.getId()).isEqualTo(TEST_ID_2); + // The active routing session is the last one in the list, which maps to infoDevice2. + assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice2); + + routingSessionInfos.remove(sessionInfo2); + mInfoMediaManager.mMediaRouterCallback.onSessionReleased(sessionInfo2); + assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice1); + } + + @Test public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() { final MediaRoute2Info info = mock(MediaRoute2Info.class); when(info.getId()).thenReturn(TEST_ID); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index a110f56d09bd..34a2ce34738d 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -834,6 +834,9 @@ <uses-permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"/> + <!-- Permission required for GTS test - GtsAttestationVerificationDeviceSideTestCases --> + <uses-permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index e6bbf9759651..6a7b8cdfceec 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -158,6 +158,7 @@ android_library { "SystemUIAnimationLib", "SystemUICommon", "SystemUICustomizationLib", + "SystemUILogLib", "SystemUIPluginLib", "SystemUISharedLib", "SystemUI-statsd", diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt index 9346a2f7ebb7..00108940e6ec 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt @@ -23,8 +23,8 @@ import android.animation.ValueAnimator import android.graphics.Canvas import android.graphics.Typeface import android.graphics.fonts.Font +import android.graphics.fonts.FontVariationAxis import android.text.Layout -import android.text.TextPaint import android.util.LruCache private const val DEFAULT_ANIMATION_DURATION: Long = 300 @@ -33,18 +33,39 @@ private const val TYPEFACE_CACHE_MAX_ENTRIES = 5 typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit interface TypefaceVariantCache { - fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface? + fun getTypefaceForVariant(fvar: String?): Typeface? + + companion object { + fun createVariantTypeface(baseTypeface: Typeface, fVar: String?): Typeface { + if (fVar.isNullOrEmpty()) { + return baseTypeface + } + + val axes = FontVariationAxis.fromFontVariationSettings(fVar).toMutableList() + axes.removeIf { !baseTypeface.isSupportedAxes(it.getOpenTypeTagValue()) } + if (axes.isEmpty()) { + return baseTypeface + } + return Typeface.createFromTypefaceWithVariation(baseTypeface, axes) + } + } } -class TypefaceVariantCacheImpl() : TypefaceVariantCache { +class TypefaceVariantCacheImpl( + var baseTypeface: Typeface, +) : TypefaceVariantCache { private val cache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES) - override fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface? { + override fun getTypefaceForVariant(fvar: String?): Typeface? { + if (fvar == null) { + return baseTypeface + } cache.get(fvar)?.let { return it } - targetPaint.fontVariationSettings = fvar - return targetPaint.typeface?.also { cache.put(fvar, it) } + return TypefaceVariantCache + .createVariantTypeface(baseTypeface, fvar) + .also { cache.put(fvar, it) } } } @@ -78,7 +99,7 @@ class TextAnimator( layout: Layout, private val invalidateCallback: () -> Unit, ) { - var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl() + var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl(layout.paint.typeface) get() = field set(value) { field = value @@ -244,8 +265,7 @@ class TextAnimator( } if (!fvar.isNullOrBlank()) { - textInterpolator.targetPaint.typeface = - typefaceCache.getTypefaceForVariant(fvar, textInterpolator.targetPaint) + textInterpolator.targetPaint.typeface = typefaceCache.getTypefaceForVariant(fvar) } if (color != null) { @@ -339,4 +359,3 @@ class TextAnimator( ) } } - diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index 79189bc3e5c3..8ed8d8fb61fd 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -38,6 +38,8 @@ class TextInterpolator( * Once you modified the style parameters, you have to call reshapeText to recalculate base text * layout. * + * Do not bypass the cache and update the typeface or font variation directly. + * * @return a paint object */ val basePaint = TextPaint(layout.paint) @@ -48,6 +50,8 @@ class TextInterpolator( * Once you modified the style parameters, you have to call reshapeText to recalculate target * text layout. * + * Do not bypass the cache and update the typeface or font variation directly. + * * @return a paint object */ val targetPaint = TextPaint(layout.paint) @@ -217,14 +221,8 @@ class TextInterpolator( run.fontRuns.forEach { fontRun -> fontRun.baseFont = fontInterpolator.lerp(fontRun.baseFont, fontRun.targetFont, progress) - val fvar = run { - val tmpFontVariationsArray = mutableListOf<FontVariationAxis>() - fontRun.baseFont.axes.forEach { - tmpFontVariationsArray.add(FontVariationAxis(it.tag, it.styleValue)) - } - FontVariationAxis.toFontVariationSettings(tmpFontVariationsArray) - } - basePaint.typeface = typefaceCache.getTypefaceForVariant(fvar, basePaint) + val fvar = FontVariationAxis.toFontVariationSettings(fontRun.baseFont.axes) + basePaint.typeface = typefaceCache.getTypefaceForVariant(fvar) } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index 3a19990f0627..941a925c9d10 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -33,8 +33,8 @@ import com.android.systemui.animation.GlyphCallback import com.android.systemui.animation.Interpolators import com.android.systemui.animation.TextAnimator import com.android.systemui.customization.R -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG import java.io.PrintWriter import java.util.Calendar import java.util.Locale diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index b0c02407873c..aa1bb3f640ee 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -23,6 +23,12 @@ import android.os.UserHandle import android.provider.Settings import android.util.Log import androidx.annotation.OpenForTesting +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogMessage +import com.android.systemui.log.LogMessageImpl +import com.android.systemui.log.MessageInitializer +import com.android.systemui.log.MessagePrinter import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockId import com.android.systemui.plugins.ClockMetadata @@ -32,12 +38,6 @@ import com.android.systemui.plugins.ClockSettings import com.android.systemui.plugins.PluginLifecycleManager import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogMessage -import com.android.systemui.plugins.log.LogMessageImpl -import com.android.systemui.plugins.log.MessageInitializer -import com.android.systemui.plugins.log.MessagePrinter import com.android.systemui.util.Assert import java.io.PrintWriter import java.util.concurrent.ConcurrentHashMap diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 2cc36008e4f8..6aa74fbf24e7 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -24,6 +24,7 @@ import android.view.View import android.widget.FrameLayout import androidx.annotation.VisibleForTesting import com.android.systemui.customization.R +import com.android.systemui.log.LogBuffer import com.android.systemui.plugins.ClockAnimations import com.android.systemui.plugins.ClockConfig import com.android.systemui.plugins.ClockController @@ -32,7 +33,6 @@ import com.android.systemui.plugins.ClockFaceConfig import com.android.systemui.plugins.ClockFaceController import com.android.systemui.plugins.ClockFaceEvents import com.android.systemui.plugins.ClockSettings -import com.android.systemui.plugins.log.LogBuffer import java.io.PrintWriter import java.util.Locale import java.util.TimeZone diff --git a/packages/SystemUI/log/.gitignore b/packages/SystemUI/log/.gitignore new file mode 100644 index 000000000000..f9a33dbbcc7e --- /dev/null +++ b/packages/SystemUI/log/.gitignore @@ -0,0 +1,9 @@ +.idea/ +.gradle/ +gradle/ +build/ +gradlew* +local.properties +*.iml +android.properties +buildSrc
\ No newline at end of file diff --git a/packages/SystemUI/log/Android.bp b/packages/SystemUI/log/Android.bp new file mode 100644 index 000000000000..627ac4b7c381 --- /dev/null +++ b/packages/SystemUI/log/Android.bp @@ -0,0 +1,38 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + +android_library { + name: "SystemUILogLib", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + static_libs: [ + "androidx.core_core-ktx", + "androidx.annotation_annotation", + "error_prone_annotations", + "SystemUICommon", + ], + manifest: "AndroidManifest.xml", + kotlincflags: ["-Xjvm-default=all"], +} diff --git a/packages/SystemUI/log/AndroidManifest.xml b/packages/SystemUI/log/AndroidManifest.xml new file mode 100644 index 000000000000..4021e1a5f751 --- /dev/null +++ b/packages/SystemUI/log/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.log"> + + +</manifest> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/ConstantStringsLogger.kt b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLogger.kt index f95b1874da36..bc35095a3436 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/ConstantStringsLogger.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLogger.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log import com.google.errorprone.annotations.CompileTimeConstant diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/ConstantStringsLoggerImpl.kt b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt index 91b39e6fcc13..6fc525369e8b 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/ConstantStringsLoggerImpl.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log import com.google.errorprone.annotations.CompileTimeConstant diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt index 0a7ccc525a73..af1a11f6597a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log import android.os.Trace import android.util.Log diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogLevel.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt index b036cf0be1d6..7d9647a13149 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogLevel.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log import android.util.Log diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogMessage.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt index 9468681289bf..88c34f82a089 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogMessage.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log import java.io.PrintWriter import java.text.SimpleDateFormat diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogMessageImpl.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt index f2a6a91adcdf..5e10f783850f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogMessageImpl.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log /** Recyclable implementation of [LogMessage]. */ data class LogMessageImpl( diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTracker.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt index cfe894f276a0..55f3a738e4f1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTracker.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log /** Keeps track of which [LogBuffer] messages should also appear in logcat. */ interface LogcatEchoTracker { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt index 7a125ac14ea1..d0ad28f04670 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log import android.content.ContentResolver import android.database.ContentObserver diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerProd.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt index 3c8bda4a44e0..56966773d1b0 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerProd.kt +++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.plugins.log +package com.android.systemui.log /** Production version of [LogcatEchoTracker] that isn't configurable. */ class LogcatEchoTrackerProd : LogcatEchoTracker { diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index 22bcba4a2b6c..fec093b26bcc 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -36,10 +36,10 @@ java_library { // in PluginInstance. That will ensure that loaded plugins have access to the related classes. static_libs: [ "androidx.annotation_annotation", - "error_prone_annotations", "PluginCoreLib", "SystemUIAnimationLib", "SystemUICommon", + "SystemUILogLib", ], } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index ca3e710a4acb..7bf139e69f2e 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -18,8 +18,8 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.view.View import com.android.internal.annotations.Keep +import com.android.systemui.log.LogBuffer import com.android.systemui.plugins.annotations.ProvidesInterface -import com.android.systemui.plugins.log.LogBuffer import java.io.PrintWriter import java.util.Locale import java.util.TimeZone diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml index 934fa6f54286..29832a081612 100644 --- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml +++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml @@ -30,7 +30,7 @@ <FrameLayout android:id="@+id/inout_container" - android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size" + android:layout_height="17dp" android:layout_width="wrap_content" android:layout_gravity="center_vertical"> <ImageView @@ -39,25 +39,24 @@ android:layout_width="wrap_content" android:src="@drawable/ic_activity_down" android:visibility="gone" - android:paddingEnd="2sp" + android:paddingEnd="2dp" /> <ImageView android:id="@+id/mobile_out" android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@drawable/ic_activity_up" - android:paddingEnd="2sp" + android:paddingEnd="2dp" android:visibility="gone" /> </FrameLayout> <ImageView android:id="@+id/mobile_type" - android:layout_height="@dimen/status_bar_mobile_signal_size" + android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_gravity="center_vertical" - android:adjustViewBounds="true" - android:paddingStart="2.5sp" - android:paddingEnd="1sp" + android:paddingStart="2.5dp" + android:paddingEnd="1dp" android:visibility="gone" /> <Space android:id="@+id/mobile_roaming_space" @@ -71,14 +70,14 @@ android:layout_gravity="center_vertical"> <com.android.systemui.statusbar.AnimatedImageView android:id="@+id/mobile_signal" - android:layout_height="@dimen/status_bar_mobile_signal_size" - android:layout_width="@dimen/status_bar_mobile_signal_size" + android:layout_height="wrap_content" + android:layout_width="wrap_content" systemui:hasOverlappingRendering="false" /> <ImageView android:id="@+id/mobile_roaming" - android:layout_width="@dimen/status_bar_mobile_signal_size" - android:layout_height="@dimen/status_bar_mobile_signal_size" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:src="@drawable/stat_sys_roaming" android:contentDescription="@string/data_connection_roaming" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index e989372adde3..441f963a855a 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -126,7 +126,8 @@ <com.android.systemui.battery.BatteryMeterView android:id="@+id/batteryRemainingIcon" android:layout_width="wrap_content" - android:layout_height="0dp" + android:layout_height="@dimen/large_screen_shade_header_min_height" + app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" app:layout_constrainedWidth="true" app:textAppearance="@style/TextAppearance.QS.Status" app:layout_constraintStart_toEndOf="@id/statusIcons" diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml index 488be3a4479e..4e5785d284ee 100644 --- a/packages/SystemUI/res/layout/smart_action_button.xml +++ b/packages/SystemUI/res/layout/smart_action_button.xml @@ -29,8 +29,8 @@ android:textSize="@dimen/smart_reply_button_font_size" android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra" android:textColor="@color/smart_reply_button_text" - android:paddingLeft="@dimen/smart_reply_button_action_padding_left" - android:paddingRight="@dimen/smart_reply_button_padding_horizontal" + android:paddingStart="@dimen/smart_reply_button_action_padding_left" + android:paddingEnd="@dimen/smart_reply_button_padding_horizontal" android:drawablePadding="@dimen/smart_action_button_icon_padding" android:textStyle="normal" android:ellipsize="none"/> diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml index ddf16e0afed7..b24362febbdd 100644 --- a/packages/SystemUI/res/layout/smart_reply_button.xml +++ b/packages/SystemUI/res/layout/smart_reply_button.xml @@ -31,7 +31,7 @@ android:textSize="@dimen/smart_reply_button_font_size" android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra" android:textColor="@color/smart_reply_button_text" - android:paddingLeft="@dimen/smart_reply_button_padding_horizontal" - android:paddingRight="@dimen/smart_reply_button_padding_horizontal" + android:paddingStart="@dimen/smart_reply_button_padding_horizontal" + android:paddingEnd="@dimen/smart_reply_button_padding_horizontal" android:textStyle="normal" android:ellipsize="none"/> diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml index 473ab08a1935..0ea0653ab89f 100644 --- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml +++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml @@ -24,11 +24,11 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" - android:layout_marginStart="2.5sp" + android:layout_marginStart="2.5dp" > <FrameLayout android:id="@+id/inout_container" - android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size" + android:layout_height="17dp" android:layout_width="wrap_content" android:gravity="center_vertical" > <ImageView @@ -37,14 +37,14 @@ android:layout_width="wrap_content" android:src="@drawable/ic_activity_down" android:visibility="gone" - android:paddingEnd="2sp" + android:paddingEnd="2dp" /> <ImageView android:id="@+id/wifi_out" android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@drawable/ic_activity_up" - android:paddingEnd="2sp" + android:paddingEnd="2dp" android:visibility="gone" /> </FrameLayout> @@ -62,7 +62,7 @@ <View android:id="@+id/wifi_signal_spacer" android:layout_width="@dimen/status_bar_wifi_signal_spacer_width" - android:layout_height="4sp" + android:layout_height="4dp" android:visibility="gone" /> <!-- Looks like CarStatusBar uses this... --> @@ -75,7 +75,7 @@ <View android:id="@+id/wifi_airplane_spacer" android:layout_width="@dimen/status_bar_airplane_spacer_width" - android:layout_height="4sp" + android:layout_height="4dp" android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml index 18386637e8f8..191158e4e8c2 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml @@ -20,7 +20,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/udfps_animation_view_internal" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:contentDescription="@string/accessibility_fingerprint_label"> <!-- Background protection --> <ImageView diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index f40615eb46d0..20864591ae5a 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -17,7 +17,7 @@ --> <resources> <!-- gap on either side of status bar notification icons --> - <dimen name="status_bar_icon_padding">1sp</dimen> + <dimen name="status_bar_icon_padding">1dp</dimen> <dimen name="controls_header_horizontal_padding">28dp</dimen> <dimen name="controls_content_margin_horizontal">40dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index f5c4a4e4bb52..0aa880fe6d88 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -122,26 +122,26 @@ <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> <!-- Default horizontal drawable padding for status bar icons. --> - <dimen name="status_bar_horizontal_padding">2.5sp</dimen> + <dimen name="status_bar_horizontal_padding">2.5dp</dimen> <!-- Height of the battery icon in the status bar. --> - <dimen name="status_bar_battery_icon_height">13.0sp</dimen> + <dimen name="status_bar_battery_icon_height">13.0dp</dimen> <!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas, - so the width of the icon should be 13.0sp * (12.0 / 20.0) --> - <dimen name="status_bar_battery_icon_width">7.8sp</dimen> + so the width of the icon should be 13.0dp * (12.0 / 20.0) --> + <dimen name="status_bar_battery_icon_width">7.8dp</dimen> - <!-- The battery icon is 13sp tall, but the other system icons are 15sp tall (see + <!-- The battery icon is 13dp tall, but the other system icons are 15dp tall (see @*android:dimen/status_bar_system_icon_size) with some top and bottom padding embedded in - the drawables themselves. So, the battery icon may need an extra 1sp of spacing so that its + the drawables themselves. So, the battery icon may need an extra 1dp of spacing so that its bottom still aligns with the bottom of all the other system icons. See b/258672854. --> - <dimen name="status_bar_battery_extra_vertical_spacing">1sp</dimen> + <dimen name="status_bar_battery_extra_vertical_spacing">1dp</dimen> <!-- The font size for the clock in the status bar. --> <dimen name="status_bar_clock_size">14sp</dimen> <!-- The starting padding for the clock in the status bar. --> - <dimen name="status_bar_clock_starting_padding">7sp</dimen> + <dimen name="status_bar_clock_starting_padding">7dp</dimen> <!-- The end padding for the clock in the status bar. --> <dimen name="status_bar_clock_end_padding">0dp</dimen> @@ -153,19 +153,16 @@ <dimen name="status_bar_left_clock_end_padding">2dp</dimen> <!-- Spacing after the wifi signals that is present if there are any icons following it. --> - <dimen name="status_bar_wifi_signal_spacer_width">2.5sp</dimen> + <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen> <!-- Size of the view displaying the wifi signal icon in the status bar. --> - <dimen name="status_bar_wifi_signal_size">13sp</dimen> - - <!-- Size of the view displaying the mobile signal icon in the status bar. --> - <dimen name="status_bar_mobile_signal_size">13sp</dimen> + <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen> <!-- Spacing before the airplane mode icon if there are any icons preceding it. --> - <dimen name="status_bar_airplane_spacer_width">4sp</dimen> + <dimen name="status_bar_airplane_spacer_width">4dp</dimen> <!-- Spacing between system icons. --> - <dimen name="status_bar_system_icon_spacing">2sp</dimen> + <dimen name="status_bar_system_icon_spacing">0dp</dimen> <!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. --> <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item> @@ -313,7 +310,7 @@ <dimen name="snooze_snackbar_min_height">56dp</dimen> <!-- size at which Notification icons will be drawn in the status bar --> - <dimen name="status_bar_icon_drawing_size">15sp</dimen> + <dimen name="status_bar_icon_drawing_size">15dp</dimen> <!-- size at which Notification icons will be drawn on Ambient Display --> <dimen name="status_bar_icon_drawing_size_dark"> @@ -324,22 +321,22 @@ <item type="dimen" name="status_bar_icon_drawing_alpha">90%</item> <!-- gap on either side of status bar notification icons --> - <dimen name="status_bar_icon_padding">0sp</dimen> + <dimen name="status_bar_icon_padding">0dp</dimen> <!-- the padding on the start of the statusbar --> - <dimen name="status_bar_padding_start">8sp</dimen> + <dimen name="status_bar_padding_start">8dp</dimen> <!-- the padding on the end of the statusbar --> - <dimen name="status_bar_padding_end">8sp</dimen> + <dimen name="status_bar_padding_end">8dp</dimen> <!-- the padding on the top of the statusbar (usually 0) --> - <dimen name="status_bar_padding_top">0sp</dimen> + <dimen name="status_bar_padding_top">0dp</dimen> <!-- the radius of the overflow dot in the status bar --> - <dimen name="overflow_dot_radius">2sp</dimen> + <dimen name="overflow_dot_radius">2dp</dimen> <!-- the padding between dots in the icon overflow --> - <dimen name="overflow_icon_dot_padding">3sp</dimen> + <dimen name="overflow_icon_dot_padding">3dp</dimen> <!-- Dimensions related to screenshots --> @@ -620,8 +617,8 @@ <dimen name="qs_footer_icon_size">20dp</dimen> <dimen name="qs_header_row_min_height">48dp</dimen> - <dimen name="qs_header_non_clickable_element_height">24sp</dimen> - <dimen name="new_qs_header_non_clickable_element_height">24sp</dimen> + <dimen name="qs_header_non_clickable_element_height">24dp</dimen> + <dimen name="new_qs_header_non_clickable_element_height">24dp</dimen> <dimen name="qs_footer_padding">20dp</dimen> <dimen name="qs_security_footer_height">88dp</dimen> @@ -825,7 +822,7 @@ <!-- Padding between the mobile signal indicator and the start icon when the roaming icon is displayed in the upper left corner. --> - <dimen name="roaming_icon_start_padding">2sp</dimen> + <dimen name="roaming_icon_start_padding">2dp</dimen> <!-- Extra padding between the mobile data type icon and the strength indicator when the data type icon is wide for the tile in quick settings. --> @@ -1045,13 +1042,13 @@ <dimen name="display_cutout_margin_consumption">0px</dimen> <!-- Height of the Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_height">24sp</dimen> + <dimen name="ongoing_appops_chip_height">24dp</dimen> <!-- Side padding between background of Ongoing App Ops chip and content --> <dimen name="ongoing_appops_chip_side_padding">8dp</dimen> <!-- Margin between icons of Ongoing App Ops chip --> <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen> <!-- Icon size of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_size">16sp</dimen> + <dimen name="ongoing_appops_chip_icon_size">16dp</dimen> <!-- Radius of Ongoing App Ops chip corners --> <dimen name="ongoing_appops_chip_bg_corner_radius">28dp</dimen> <!-- One or two privacy items --> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 499dfa4b3c9f..eaeaabe8779a 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -198,6 +198,9 @@ <item type="id" name="pm_lite"/> <item type="id" name="settings_button_container"/> + <!--Keyboard Backlight Dialog --> + <item type="id" name="keyboard_backlight_dialog_container"/> + <item type="id" name="log_access_dialog_allow_button" /> <item type="id" name="log_access_dialog_deny_button" /> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt index 2d83458ec2f7..a2b6e2cf9ae3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt @@ -16,27 +16,179 @@ package com.android.systemui.shared.condition +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch + /** * A higher order [Condition] which combines multiple conditions with a specified - * [Evaluator.ConditionOperand]. + * [Evaluator.ConditionOperand]. Conditions are executed lazily as-needed. + * + * @param scope The [CoroutineScope] to execute in. + * @param conditions The list of conditions to evaluate. Since conditions are executed lazily, the + * ordering is important here. + * @param operand The [Evaluator.ConditionOperand] to apply to the conditions. */ -internal class CombinedCondition +@OptIn(ExperimentalCoroutinesApi::class) +class CombinedCondition constructor( + private val scope: CoroutineScope, private val conditions: Collection<Condition>, @Evaluator.ConditionOperand private val operand: Int -) : Condition(null, false), Condition.Callback { +) : Condition(scope, null, false) { + + private var job: Job? = null + private val _startStrategy by lazy { calculateStartStrategy() } override fun start() { - onConditionChanged(this) - conditions.forEach { it.addCallback(this) } - } + job = + scope.launch { + val groupedConditions = conditions.groupBy { it.isOverridingCondition } - override fun onConditionChanged(condition: Condition) { - Evaluator.evaluate(conditions, operand)?.also { value -> updateCondition(value) } - ?: clearCondition() + lazilyEvaluate( + conditions = groupedConditions.getOrDefault(true, emptyList()), + filterUnknown = true + ) + .distinctUntilChanged() + .flatMapLatest { overriddenValue -> + // If there are overriding conditions with values set, they take precedence. + if (overriddenValue == null) { + lazilyEvaluate( + conditions = groupedConditions.getOrDefault(false, emptyList()), + filterUnknown = false + ) + } else { + flowOf(overriddenValue) + } + } + .collect { conditionMet -> + if (conditionMet == null) { + clearCondition() + } else { + updateCondition(conditionMet) + } + } + } } override fun stop() { - conditions.forEach { it.removeCallback(this) } + job?.cancel() + job = null + } + + /** + * Evaluates a list of conditions lazily with support for short-circuiting. Conditions are + * executed serially in the order provided. At any point if the result can be determined, we + * short-circuit and return the result without executing all conditions. + */ + private fun lazilyEvaluate( + conditions: Collection<Condition>, + filterUnknown: Boolean, + ): Flow<Boolean?> = callbackFlow { + val jobs = MutableList<Job?>(conditions.size) { null } + val values = MutableList<Boolean?>(conditions.size) { null } + val flows = conditions.map { it.toFlow() } + + fun cancelAllExcept(indexToSkip: Int) { + for (index in 0 until jobs.size) { + if (index == indexToSkip) { + continue + } + if ( + indexToSkip == -1 || + conditions.elementAt(index).startStrategy == START_WHEN_NEEDED + ) { + jobs[index]?.cancel() + jobs[index] = null + values[index] = null + } + } + } + + fun collectFlow(index: Int) { + // Base case which is triggered once we have collected all the flows. In this case, + // we never short-circuited and therefore should return the fully evaluated + // conditions. + if (flows.isEmpty() || index == -1) { + val filteredValues = + if (filterUnknown) { + values.filterNotNull() + } else { + values + } + trySend(Evaluator.evaluate(filteredValues, operand)) + return + } + jobs[index] = + scope.launch { + flows.elementAt(index).collect { value -> + values[index] = value + if (shouldEarlyReturn(value)) { + trySend(value) + // The overall result is contingent on this condition, so we don't need + // to monitor any other conditions. + cancelAllExcept(index) + } else { + collectFlow(jobs.indexOfFirst { it == null }) + } + } + } + } + + // Collect any eager conditions immediately. + var started = false + for ((index, condition) in conditions.withIndex()) { + if (condition.startStrategy == START_EAGERLY) { + collectFlow(index) + started = true + } + } + + // If no eager conditions started, start the first condition to kick off evaluation. + if (!started) { + collectFlow(0) + } + awaitClose { cancelAllExcept(-1) } + } + + private fun shouldEarlyReturn(conditionMet: Boolean?): Boolean { + return when (operand) { + Evaluator.OP_AND -> conditionMet == false + Evaluator.OP_OR -> conditionMet == true + else -> false + } + } + + /** + * Calculate the start strategy for this condition. This depends on the strategies of the child + * conditions. If there are any eager conditions, we must also start this condition eagerly. In + * the absence of eager conditions, we check for lazy conditions. In the absence of either, we + * make the condition only start when needed. + */ + private fun calculateStartStrategy(): Int { + var startStrategy = START_WHEN_NEEDED + for (condition in conditions) { + when (condition.startStrategy) { + START_EAGERLY -> return START_EAGERLY + START_LAZILY -> { + startStrategy = START_LAZILY + } + START_WHEN_NEEDED -> { + // this is the default, so do nothing + } + } + } + return startStrategy + } + + override fun getStartStrategy(): Int { + return _startStrategy } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java index cc48090e1217..6bf1ce57b01e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java @@ -18,11 +18,14 @@ package com.android.systemui.shared.condition; import android.util.Log; +import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleEventObserver; import androidx.lifecycle.LifecycleOwner; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; @@ -30,6 +33,8 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import kotlinx.coroutines.CoroutineScope; + /** * Base class for a condition that needs to be fulfilled in order for {@link Monitor} to inform * its callbacks. @@ -39,24 +44,27 @@ public abstract class Condition { private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>(); private final boolean mOverriding; + private final CoroutineScope mScope; private Boolean mIsConditionMet; private boolean mStarted = false; /** * By default, conditions have an initial value of false and are not overriding. */ - public Condition() { - this(false, false); + public Condition(CoroutineScope scope) { + this(scope, false, false); } /** * Constructor for specifying initial state and overriding condition attribute. + * * @param initialConditionMet Initial state of the condition. - * @param overriding Whether this condition overrides others. + * @param overriding Whether this condition overrides others. */ - protected Condition(Boolean initialConditionMet, boolean overriding) { + protected Condition(CoroutineScope scope, Boolean initialConditionMet, boolean overriding) { mIsConditionMet = initialConditionMet; mOverriding = overriding; + mScope = scope; } /** @@ -70,6 +78,29 @@ public abstract class Condition { protected abstract void stop(); /** + * Condition should be started as soon as there is an active subscription. + */ + public static final int START_EAGERLY = 0; + /** + * Condition should be started lazily only if needed. But once started, it will not be cancelled + * unless there are no more active subscriptions. + */ + public static final int START_LAZILY = 1; + /** + * Condition should be started lazily only if needed, and can be stopped when not needed. This + * should be used for conditions which are expensive to keep running. + */ + public static final int START_WHEN_NEEDED = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({START_EAGERLY, START_LAZILY, START_WHEN_NEEDED}) + @interface StartStrategy { + } + + @StartStrategy + protected abstract int getStartStrategy(); + + /** * Returns whether the current condition overrides */ public boolean isOverridingCondition() { @@ -183,6 +214,7 @@ public abstract class Condition { /** * Returns whether the condition is set. This method should be consulted to understand the * value of {@link #isConditionMet()}. + * * @return {@code true} if value is present, {@code false} otherwise. */ public boolean isConditionSet() { @@ -210,17 +242,18 @@ public abstract class Condition { * conditions are true. */ public Condition and(@NonNull Collection<Condition> others) { - final List<Condition> conditions = new ArrayList<>(others); + final List<Condition> conditions = new ArrayList<>(); conditions.add(this); - return new CombinedCondition(conditions, Evaluator.OP_AND); + conditions.addAll(others); + return new CombinedCondition(mScope, conditions, Evaluator.OP_AND); } /** * Creates a new condition which will only be true when both this condition and the provided * condition is true. */ - public Condition and(@NonNull Condition other) { - return new CombinedCondition(Arrays.asList(this, other), Evaluator.OP_AND); + public Condition and(@NonNull Condition... others) { + return and(Arrays.asList(others)); } /** @@ -228,17 +261,18 @@ public abstract class Condition { * provided conditions are true. */ public Condition or(@NonNull Collection<Condition> others) { - final List<Condition> conditions = new ArrayList<>(others); + final List<Condition> conditions = new ArrayList<>(); conditions.add(this); - return new CombinedCondition(conditions, Evaluator.OP_OR); + conditions.addAll(others); + return new CombinedCondition(mScope, conditions, Evaluator.OP_OR); } /** * Creates a new condition which will only be true when either this condition or the provided * condition is true. */ - public Condition or(@NonNull Condition other) { - return new CombinedCondition(Arrays.asList(this, other), Evaluator.OP_OR); + public Condition or(@NonNull Condition... others) { + return or(Arrays.asList(others)); } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt index 8f8bff86f64d..84edc3577007 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt @@ -1,14 +1,22 @@ package com.android.systemui.shared.condition +import com.android.systemui.shared.condition.Condition.StartStrategy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch /** Converts a boolean flow to a [Condition] object which can be used with a [Monitor] */ @JvmOverloads -fun Flow<Boolean>.toCondition(scope: CoroutineScope, initialValue: Boolean? = null): Condition { - return object : Condition(initialValue, false) { +fun Flow<Boolean>.toCondition( + scope: CoroutineScope, + @StartStrategy strategy: Int, + initialValue: Boolean? = null +): Condition { + return object : Condition(scope, initialValue, false) { var job: Job? = null override fun start() { @@ -19,5 +27,25 @@ fun Flow<Boolean>.toCondition(scope: CoroutineScope, initialValue: Boolean? = nu job?.cancel() job = null } + + override fun getStartStrategy() = strategy } } + +/** Converts a [Condition] to a boolean flow */ +fun Condition.toFlow(): Flow<Boolean?> { + return callbackFlow { + val callback = + Condition.Callback { condition -> + if (condition.isConditionSet) { + trySend(condition.isConditionMet) + } else { + trySend(null) + } + } + addCallback(callback) + callback.onConditionChanged(this@toFlow) + awaitClose { removeCallback(callback) } + } + .distinctUntilChanged() +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt index 454294f36d2a..584d9785c300 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt @@ -22,7 +22,7 @@ import android.annotation.IntDef * Helper for evaluating a collection of [Condition] objects with a given * [Evaluator.ConditionOperand] */ -internal object Evaluator { +object Evaluator { /** Operands for combining multiple conditions together */ @Retention(AnnotationRetention.SOURCE) @IntDef(value = [OP_AND, OP_OR]) @@ -70,15 +70,31 @@ internal object Evaluator { fun evaluate(conditions: Collection<Condition>, @ConditionOperand operand: Int): Boolean? { if (conditions.isEmpty()) return null // If there are overriding conditions with values set, they take precedence. - val targetConditions = + val values: Collection<Boolean?> = conditions .filter { it.isConditionSet && it.isOverridingCondition } .ifEmpty { conditions } + .map { condition -> + if (condition.isConditionSet) { + condition.isConditionMet + } else { + null + } + } + return evaluate(values = values, operand = operand) + } + + /** + * Evaluates a set of booleans with a given operand + * + * @param operand The operand to use when evaluating. + * @return Either true or false if the value is known, or null if value is unknown + */ + internal fun evaluate(values: Collection<Boolean?>, @ConditionOperand operand: Int): Boolean? { + if (values.isEmpty()) return null return when (operand) { - OP_AND -> - threeValuedAndOrOr(conditions = targetConditions, returnValueIfAnyMatches = false) - OP_OR -> - threeValuedAndOrOr(conditions = targetConditions, returnValueIfAnyMatches = true) + OP_AND -> threeValuedAndOrOr(values = values, returnValueIfAnyMatches = false) + OP_OR -> threeValuedAndOrOr(values = values, returnValueIfAnyMatches = true) else -> null } } @@ -90,16 +106,16 @@ internal object Evaluator { * any value is true. */ private fun threeValuedAndOrOr( - conditions: Collection<Condition>, + values: Collection<Boolean?>, returnValueIfAnyMatches: Boolean ): Boolean? { var hasUnknown = false - for (condition in conditions) { - if (!condition.isConditionSet) { + for (value in values) { + if (value == null) { hasUnknown = true continue } - if (condition.isConditionMet == returnValueIfAnyMatches) { + if (value == returnValueIfAnyMatches) { return returnValueIfAnyMatches } } diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 8b87e2a538ca..4bf7be6df5be 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -42,14 +42,14 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.dagger.KeyguardLargeClockLog import com.android.systemui.log.dagger.KeyguardSmallClockLog import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockFaceController import com.android.systemui.plugins.ClockTickRate import com.android.systemui.plugins.WeatherData -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index a6c782d88e18..1ca63fb88342 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -18,9 +18,10 @@ import androidx.annotation.VisibleForTesting; import com.android.keyguard.dagger.KeyguardStatusViewScope; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.ClockController; -import com.android.systemui.plugins.log.LogBuffer; -import com.android.systemui.plugins.log.LogLevel; +import com.android.systemui.shared.clocks.DefaultClockController; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -46,6 +47,9 @@ public class KeyguardClockSwitch extends RelativeLayout { public static final int LARGE = 0; public static final int SMALL = 1; + // compensate for translation of parents subject to device screen + // In this case, the translation comes from KeyguardStatusView + public int screenOffsetYPadding = 0; /** Returns a region for the large clock to position itself, based on the given parent. */ public static Rect getLargeClockRegion(ViewGroup parent) { @@ -161,8 +165,18 @@ public class KeyguardClockSwitch extends RelativeLayout { } if (mLargeClockFrame.isLaidOut()) { - mClock.getLargeClock().getEvents().onTargetRegionChanged( - getLargeClockRegion(mLargeClockFrame)); + Rect targetRegion = getLargeClockRegion(mLargeClockFrame); + if (mClock instanceof DefaultClockController) { + mClock.getLargeClock().getEvents().onTargetRegionChanged( + targetRegion); + } else { + mClock.getLargeClock().getEvents().onTargetRegionChanged( + new Rect( + targetRegion.left, + targetRegion.top - screenOffsetYPadding, + targetRegion.right, + targetRegion.bottom - screenOffsetYPadding)); + } } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index a34c9fa5aced..d8bf570954df 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -39,10 +39,10 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.log.LogLevel; import com.android.systemui.log.dagger.KeyguardClockLog; import com.android.systemui.plugins.ClockController; -import com.android.systemui.plugins.log.LogBuffer; -import com.android.systemui.plugins.log.LogLevel; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.shared.regionsampling.RegionSampler; @@ -169,6 +169,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** + * Used for status view to pass the screen offset from parent view + */ + public void setLockscreenClockY(int clockY) { + if (mView.screenOffsetYPadding != clockY) { + mView.screenOffsetYPadding = clockY; + mView.updateClockTargetRegions(); + } + } + + /** * Attach the controller to the view it relates to. */ @Override @@ -394,13 +404,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X, x, props, animate); } - - } - - void updateKeyguardStatusViewOffset() { - // updateClockTargetRegions will call onTargetRegionChanged - // which will require the correct translationY property of keyguardStatusView after updating - mView.updateClockTargetRegions(); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 0826f8a8b985..68d8143a4154 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -215,6 +215,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** + * Pass top margin from ClockPositionAlgorithm in NotificationPanelViewController + * Use for clock view in LS to compensate for top margin to align to the screen + * Regardless of translation from AOD and unlock gestures + */ + public void setLockscreenClockY(int clockY) { + mKeyguardClockSwitchController.setLockscreenClockY(clockY); + } + + /** * Set whether the view accessibility importance mode. */ public void setStatusAccessibilityImportance(int mode) { @@ -230,7 +239,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * Update position of the view with an optional animation */ public void updatePosition(int x, int y, float scale, boolean animate) { - float oldY = mView.getY(); setProperty(AnimatableProperty.Y, y, animate); ClockController clock = mKeyguardClockSwitchController.getClock(); @@ -246,10 +254,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV setProperty(AnimatableProperty.SCALE_X, 1f, animate); setProperty(AnimatableProperty.SCALE_Y, 1f, animate); } - - if (oldY != y) { - mKeyguardClockSwitchController.updateKeyguardStatusViewOffset(); - } } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 651c9796140e..d8568ba56ebc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -22,8 +22,8 @@ import android.util.Property; import android.view.View; import com.android.systemui.animation.Interpolators; -import com.android.systemui.plugins.log.LogBuffer; -import com.android.systemui.plugins.log.LogLevel; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.log.LogLevel; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index 6740375c4329..2d0bf9cc2aba 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -27,9 +27,9 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.log.LogBuffer; import com.android.systemui.log.dagger.KeyguardClockLog; import com.android.systemui.plugins.PluginManager; -import com.android.systemui.plugins.log.LogBuffer; import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.shared.clocks.DefaultClockProvider; diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt index 2bb75aa34c6a..e7295ef9052c 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt @@ -17,9 +17,9 @@ package com.android.keyguard.logging import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.dagger.BiometricLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG import javax.inject.Inject /** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */ diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt index 20f90072161b..c00b2c612fa2 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt @@ -18,11 +18,11 @@ package com.android.keyguard.logging import android.hardware.biometrics.BiometricSourceType import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.dagger.BiometricLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.INFO import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_ONLY_WAKE diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt index 4d71a8952021..8b925b1bfb54 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt @@ -18,9 +18,9 @@ package com.android.keyguard.logging import com.android.systemui.biometrics.AuthRippleController import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.KeyguardLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import com.android.systemui.statusbar.KeyguardIndicationController import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index 4974f797d59a..c2d22c3e1d14 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -27,13 +27,13 @@ import com.android.keyguard.KeyguardListenModel import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.TrustGrantFlags import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.ERROR -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogLevel.VERBOSE -import com.android.systemui.plugins.log.LogLevel.WARNING +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.VERBOSE +import com.android.systemui.log.LogLevel.WARNING import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt index 249b3fe97d81..daafea8b62c7 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt @@ -18,9 +18,9 @@ package com.android.keyguard.logging import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.TrustModel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject /** Logging helper for trust repository. */ diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 3579e8cc0218..fd9cee0d6144 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -829,9 +829,9 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, final Rect overlayBounds = new Rect( 0, /* left */ - 0, /* top */ + mCachedDisplayInfo.getNaturalHeight() / 2, /* top */ mCachedDisplayInfo.getNaturalWidth(), /* right */ - mCachedDisplayInfo.getNaturalHeight() /* botom */); + mCachedDisplayInfo.getNaturalHeight() /* bottom */); mUdfpsOverlayParams = new UdfpsOverlayParams( mUdfpsBounds, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index d0ac2968ae8e..55f6d072563b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -37,7 +37,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogLevel import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LiftReveal diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index f876affb2a9c..d953a885fe1f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -364,7 +364,12 @@ class UdfpsControllerOverlay @JvmOverloads constructor( if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) { Rect(overlayParams.sensorBounds) } else { - Rect(overlayParams.overlayBounds) + Rect( + 0, + 0, + overlayParams.naturalDisplayWidth, + overlayParams.naturalDisplayHeight + ) } } else { Rect(overlayParams.sensorBounds) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt index 0d08b4307f12..39199d194cc9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt @@ -16,12 +16,12 @@ package com.android.systemui.biometrics +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.VERBOSE +import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.log.dagger.UdfpsLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogLevel.ERROR -import com.android.systemui.plugins.log.LogLevel.VERBOSE -import com.android.systemui.plugins.log.LogLevel.WARNING import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt index d99625a9fbf2..96af42bfda22 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt @@ -17,9 +17,9 @@ package com.android.systemui.bluetooth import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.BluetoothLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject /** Helper class for logging bluetooth events. */ diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt index d27708fc04d7..5b3a982ab5e2 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt @@ -20,11 +20,11 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogMessage +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogMessage import com.android.systemui.log.dagger.BroadcastDispatcherLog import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt index d19c6ec01109..536978009f71 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt @@ -19,10 +19,10 @@ package com.android.systemui.doze import android.view.Display import com.android.systemui.doze.DozeLog.Reason import com.android.systemui.doze.DozeLog.reasonToString -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.ERROR -import com.android.systemui.plugins.log.LogLevel.INFO +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.dagger.DozeLog import com.android.systemui.statusbar.policy.DevicePostureController import com.google.errorprone.annotations.CompileTimeConstant diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt index eb7929095a4b..fdb765130157 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt @@ -16,9 +16,9 @@ package com.android.systemui.dreams +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.DreamLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject /** Logs dream-related stuff to a {@link LogBuffer}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java index 250cfeca5aa4..c889ac214cda 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java @@ -18,11 +18,14 @@ package com.android.systemui.dreams.conditions; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; +import kotlinx.coroutines.CoroutineScope; + /** * {@link AssistantAttentionCondition} provides a signal when assistant has the user's attention. */ @@ -58,8 +61,10 @@ public class AssistantAttentionCondition extends Condition { @Inject public AssistantAttentionCondition( + @Application CoroutineScope scope, DreamOverlayStateController dreamOverlayStateController, AssistUtils assistUtils) { + super(scope); mDreamOverlayStateController = dreamOverlayStateController; mAssistUtils = assistUtils; } @@ -75,6 +80,11 @@ public class AssistantAttentionCondition extends Condition { mDreamOverlayStateController.removeCallback(mCallback); } + @Override + protected int getStartStrategy() { + return START_EAGERLY; + } + private void enableVisualQueryDetection() { if (mEnabled) { return; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java index 3ef19b760826..99688bee68e0 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java @@ -19,19 +19,20 @@ import android.app.DreamManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; +import kotlinx.coroutines.CoroutineScope; + /** * {@link DreamCondition} provides a signal when a dream begins and ends. */ public class DreamCondition extends Condition { private final DreamManager mDreamManager; - private final KeyguardUpdateMonitor mUpdateMonitor; - private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -41,7 +42,11 @@ public class DreamCondition extends Condition { }; @Inject - public DreamCondition(DreamManager dreamManager, KeyguardUpdateMonitor monitor) { + public DreamCondition( + @Application CoroutineScope scope, + DreamManager dreamManager, + KeyguardUpdateMonitor monitor) { + super(scope); mDreamManager = dreamManager; mUpdateMonitor = monitor; } @@ -56,4 +61,9 @@ public class DreamCondition extends Condition { protected void stop() { mUpdateMonitor.removeCallback(mUpdateCallback); } + + @Override + protected int getStartStrategy() { + return START_EAGERLY; + } } diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt index a2f65ba747ab..4b03fd334cb5 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt @@ -24,7 +24,7 @@ import com.android.systemui.R import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_CRITICAL import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_NORMAL import com.android.systemui.dump.nano.SystemUIProtoDump -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager import com.google.protobuf.nano.MessageNano import java.io.BufferedOutputStream diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt index ab8052c54f92..2d57633e47a8 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt @@ -19,7 +19,7 @@ package com.android.systemui.dump import com.android.systemui.Dumpable import com.android.systemui.ProtoDumpable import com.android.systemui.dump.nano.SystemUIProtoDump -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import java.io.PrintWriter import java.util.TreeMap import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt b/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt index 8299b13d305f..0eab1afc4119 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt @@ -19,7 +19,7 @@ package com.android.systemui.dump import android.content.Context import android.util.Log import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.util.io.Files import com.android.systemui.util.time.SystemClock import java.io.IOException diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 6ca409f07f6f..1fafa3d97c48 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -62,7 +62,8 @@ object Flags { val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply") // TODO(b/279735475): Tracking Bug - @JvmField val NEW_LIGHT_BAR_LOGIC = unreleasedFlag(279735475, "new_light_bar_logic") + @JvmField + val NEW_LIGHT_BAR_LOGIC = unreleasedFlag(279735475, "new_light_bar_logic", teamfood = true) /** * This flag is server-controlled and should stay as [unreleasedFlag] since we never want to diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt index 2ef5e19bf382..328beed3ae4b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt @@ -159,6 +159,7 @@ class KeyboardBacklightDialog( private fun buildRootView(): LinearLayout { val linearLayout = LinearLayout(context).apply { + id = R.id.keyboard_backlight_dialog_container orientation = LinearLayout.HORIZONTAL layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT) setPadding( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index e650b9fc0e47..7c5641fcda9c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.logging.KeyguardLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.plugins.log.LogLevel.VERBOSE +import com.android.systemui.log.LogLevel.VERBOSE import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt index 8e9328117e38..fefa1b29b576 100644 --- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt @@ -6,9 +6,8 @@ import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.dagger.FaceAuthLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG import javax.inject.Inject private const val TAG = "DeviceEntryFaceAuthRepositoryLog" diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt index d6e29e0f2067..e4465acb14ab 100644 --- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt @@ -19,9 +19,6 @@ package com.android.systemui.log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogcatEchoTracker - import javax.inject.Inject @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt index 4300f371e2e1..f7277842c026 100644 --- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt @@ -21,10 +21,10 @@ import android.graphics.Rect import android.graphics.RectF import androidx.core.graphics.toRectF import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR import com.android.systemui.log.dagger.ScreenDecorationsLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.ERROR import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricLog.java index 4b774d3b2192..233f7a123ff4 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricLog.java @@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy; import javax.inject.Qualifier; /** - * A {@link com.android.systemui.plugins.log.LogBuffer} for BiometricMessages processing such as + * A {@link com.android.systemui.log.LogBuffer} for BiometricMessages processing such as * {@link com.android.systemui.biometrics.FaceHelpMessageDeferral} */ @Qualifier diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java index 5cca1ab2abe7..7d1f1c2709fa 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java index 1d016d837b02..9ca0293fbd86 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java index c9f78bcdeef8..7c5f4025117f 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt index 9f563fe4eae5..8732ef576335 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardClockLog.kt @@ -18,19 +18,19 @@ package com.android.systemui.log.dagger import javax.inject.Qualifier -/** A [com.android.systemui.plugins.log.LogBuffer] for keyguard clock logs. */ +/** A [com.android.systemui.log.LogBuffer] for keyguard clock logs. */ @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class KeyguardClockLog -/** A [com.android.systemui.plugins.log.LogBuffer] for small keyguard clock logs. */ +/** A [com.android.systemui.log.LogBuffer] for small keyguard clock logs. */ @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class KeyguardSmallClockLog -/** A [com.android.systemui.plugins.log.LogBuffer] for large keyguard clock logs. */ +/** A [com.android.systemui.log.LogBuffer] for large keyguard clock logs. */ @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java index 76d20bea4bdf..08d969b5eb77 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 6988bd899e23..9be18ace79fa 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -22,13 +22,13 @@ import android.os.Looper; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogBufferFactory; +import com.android.systemui.log.LogcatEchoTracker; +import com.android.systemui.log.LogcatEchoTrackerDebug; +import com.android.systemui.log.LogcatEchoTrackerProd; import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.log.table.TableLogBufferFactory; -import com.android.systemui.plugins.log.LogBuffer; -import com.android.systemui.plugins.log.LogcatEchoTracker; -import com.android.systemui.plugins.log.LogcatEchoTrackerDebug; -import com.android.systemui.plugins.log.LogcatEchoTrackerProd; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.util.Compile; import com.android.systemui.util.wakelock.WakeLockLog; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java index af433476b38c..1c00c93f4e38 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java index f4dac6efe371..86a916ef6541 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaMuteAwaitLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaMuteAwaitLog.java index 73690ab6c24d..c67d8bebe313 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaMuteAwaitLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaMuteAwaitLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java index 0c2cd92d1bb0..98e6556d7f53 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java index 5b7f4bb103b4..dde0ee0796d4 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NearbyMediaDevicesLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NearbyMediaDevicesLog.java index 6d91f0c97c8a..b1c6dcfcb13b 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NearbyMediaDevicesLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NearbyMediaDevicesLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java index 26af4964f7b8..20fc6ff445a6 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java index 61daf9c8d71c..fcc184a317b8 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java index a59afa0fed1b..760fbf3928b6 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java index a2d381ec90f0..f1646a837132 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java index 6f8ea7ff2e9b..a0b686487bec 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java index 835d3490293c..8c8753a07339 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationSectionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationSectionLog.java index 6e2bd7b2e1b5..7259eebf19b6 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationSectionLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationSectionLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java index 77b1bf5fd630..e96e532f94bf 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java index 295bf88d498f..973f6501bbfd 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java index 9fd166b759d2..557a254e5c09 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java index dd168bac5654..dd5010cf39a8 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeLog.java index d24bfcb88188..bd0d298ebdee 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeWindowLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeWindowLog.java index 1d2b68c3bf46..4aa2b1d84b21 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeWindowLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ShadeWindowLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java index af0f7c518e64..f26b3164f488 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java index d58b538f2047..4d797c1a88cd 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java index ba8b27c23ec1..8671dbfdf1fe 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java index 5c2321be4388..dbb6e8cbcc07 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java @@ -18,7 +18,7 @@ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt index 8babfc90d269..1d785ae62d87 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt @@ -20,8 +20,8 @@ import android.os.Trace import com.android.systemui.Dumpable import com.android.systemui.common.buffer.RingBuffer import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import java.text.SimpleDateFormat @@ -35,7 +35,7 @@ import kotlinx.coroutines.launch * A logger that logs changes in table format. * * Some parts of System UI maintain a lot of pieces of state at once. - * [com.android.systemui.plugins.log.LogBuffer] allows us to easily log change events: + * [com.android.systemui.log.LogBuffer] allows us to easily log change events: * - 10-10 10:10:10.456: state2 updated to newVal2 * - 10-10 10:11:00.000: stateN updated to StateN(val1=true, val2=1) * - 10-10 10:11:02.123: stateN updated to StateN(val1=true, val2=2) diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt index 42e742db2842..19e112487c46 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt @@ -21,7 +21,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt index f731dc064355..e2e269de71a0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt @@ -18,9 +18,9 @@ package com.android.systemui.media.controls.pipeline import android.media.session.PlaybackState import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.MediaTimeoutListenerLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject private const val TAG = "MediaTimeout" diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt index 095cf09a6c2c..9e53d77dec99 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt @@ -18,9 +18,9 @@ package com.android.systemui.media.controls.resume import android.content.ComponentName import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.MediaBrowserLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject /** A logger for events in [ResumeMediaBrowser]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt index 9af11b924ec8..0ed24349bdf4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt @@ -17,9 +17,9 @@ package com.android.systemui.media.controls.ui import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.MediaCarouselControllerLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject /** A debug logger for [MediaCarouselController]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt index fdac33ac20b0..c781b7699b26 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt @@ -17,9 +17,9 @@ package com.android.systemui.media.controls.ui import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.MediaViewLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject private const val TAG = "MediaView" diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java index 9ae4577b0394..46efac56ab9d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -17,6 +17,7 @@ package com.android.systemui.media.dagger; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogBufferFactory; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; @@ -30,16 +31,15 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer; import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer; -import com.android.systemui.plugins.log.LogBuffer; - -import java.util.Optional; - -import javax.inject.Named; import dagger.Lazy; import dagger.Module; import dagger.Provides; +import java.util.Optional; + +import javax.inject.Named; + /** Dagger module for the media package. */ @Module(subcomponents = { MediaComplicationComponent.class, diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt index 5ace3ea8a05b..bbcf259418c8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt @@ -2,8 +2,8 @@ package com.android.systemui.media.muteawait import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.dagger.MediaMuteAwaitLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject /** Log messages for [MediaMuteAwaitConnectionManager]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt index 78408fce5a36..66399d580582 100644 --- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt @@ -2,8 +2,8 @@ package com.android.systemui.media.nearby import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.dagger.NearbyMediaDevicesLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject /** Log messages for [NearbyMediaDevicesManager]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt index 0e839c6dbc7a..eeda102702d2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt @@ -16,8 +16,8 @@ package com.android.systemui.media.taptotransfer.common -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel /** A helper for logging media tap-to-transfer events. */ object MediaTttLoggerUtils { diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java index 67e464c344c5..31ccb9afac9f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java @@ -18,7 +18,7 @@ package com.android.systemui.media.taptotransfer.receiver; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt index b0c6257df96c..1502df725a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt @@ -18,8 +18,8 @@ package com.android.systemui.media.taptotransfer.receiver import android.app.StatusBarManager import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils -import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.temporarydisplay.TemporaryViewLogger import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java index a262e97864f3..edee4a808968 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java @@ -18,7 +18,7 @@ package com.android.systemui.media.taptotransfer.sender; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt index 964a95b9be9a..03bcfc8113e3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt @@ -18,9 +18,9 @@ package com.android.systemui.media.taptotransfer.sender import android.app.StatusBarManager import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject /** A logger for all events related to the media tap-to-transfer sender experience. */ diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 166ba9fba166..79167f276576 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -15,7 +15,6 @@ package com.android.systemui.privacy import android.content.Context -import android.content.res.Configuration import android.util.AttributeSet import android.view.Gravity.CENTER_VERTICAL import android.view.Gravity.END @@ -103,11 +102,6 @@ class OngoingPrivacyChip @JvmOverloads constructor( R.string.ongoing_privacy_chip_content_multiple_apps, typesText) } - override fun onConfigurationChanged(newConfig: Configuration?) { - super.onConfigurationChanged(newConfig) - updateResources() - } - private fun updateResources() { iconMargin = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) @@ -116,11 +110,8 @@ class OngoingPrivacyChip @JvmOverloads constructor( iconColor = Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) - val height = context.resources - .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height) val padding = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) - iconsContainer.layoutParams.height = height iconsContainer.setPaddingRelative(padding, 0, padding, 0) iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg) } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt index 03503fd1ff61..f9e1adff012b 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt @@ -18,9 +18,9 @@ package com.android.systemui.privacy.logging import android.permission.PermissionGroupUsage import com.android.systemui.log.dagger.PrivacyLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogMessage +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogMessage import com.android.systemui.privacy.PrivacyDialog import com.android.systemui.privacy.PrivacyItem import java.text.SimpleDateFormat diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java index 80fbf9115065..b6a5ad6f2155 100644 --- a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java +++ b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java @@ -16,11 +16,14 @@ package com.android.systemui.process.condition; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.process.ProcessWrapper; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; +import kotlinx.coroutines.CoroutineScope; + /** * {@link SystemProcessCondition} checks to make sure the current process is being ran by the * System User. @@ -29,8 +32,9 @@ public class SystemProcessCondition extends Condition { private final ProcessWrapper mProcessWrapper; @Inject - public SystemProcessCondition(ProcessWrapper processWrapper) { - super(); + public SystemProcessCondition(@Application CoroutineScope scope, + ProcessWrapper processWrapper) { + super(scope); mProcessWrapper = processWrapper; } @@ -42,4 +46,9 @@ public class SystemProcessCondition extends Condition { @Override protected void stop() { } + + @Override + protected int getStartStrategy() { + return START_EAGERLY; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt index 025fb228b829..cd52ec29177d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt @@ -1,8 +1,8 @@ package com.android.systemui.qs import com.android.systemui.log.dagger.QSFragmentDisableLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.disableflags.DisableFlagsLogger import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt index 5b461a6d8bad..c00a81cbf12b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt @@ -21,14 +21,14 @@ import android.content.res.Configuration.ORIENTATION_PORTRAIT import android.content.res.Configuration.Orientation import android.service.quicksettings.Tile import android.view.View +import com.android.systemui.log.ConstantStringsLogger +import com.android.systemui.log.ConstantStringsLoggerImpl +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.VERBOSE import com.android.systemui.log.dagger.QSConfigLog import com.android.systemui.log.dagger.QSLog -import com.android.systemui.plugins.log.ConstantStringsLogger -import com.android.systemui.plugins.log.ConstantStringsLoggerImpl -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.ERROR -import com.android.systemui.plugins.log.LogLevel.VERBOSE import com.android.systemui.plugins.qs.QSTile import com.android.systemui.statusbar.StatusBarState import com.google.errorprone.annotations.CompileTimeConstant diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt index e85440cad6b0..a066242fd96b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt @@ -18,8 +18,8 @@ package com.android.systemui.qs.pipeline.dagger import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt index 767ce919d027..b564334b5c52 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.qs.pipeline.shared.logging import android.annotation.UserIdInt -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.qs.pipeline.dagger.QSTileListLog import com.android.systemui.qs.pipeline.shared.TileSpec import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 137a99ef39b8..4b44ac01e60a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1513,6 +1513,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard), mKeyguardStatusViewController.isClockTopAligned()); mClockPositionAlgorithm.run(mClockPositionResult); + mKeyguardStatusViewController.setLockscreenClockY( + mClockPositionAlgorithm.getExpandedPreferredClockY()); mKeyguardBottomAreaInteractor.setClockPosition( mClockPositionResult.clockX, mClockPositionResult.clockY); boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt index 1839e13dcdec..25073c1b64db 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt @@ -18,8 +18,8 @@ package com.android.systemui.shade import android.view.MotionEvent import com.android.systemui.log.dagger.ShadeLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.shade.ShadeViewController.Companion.FLING_COLLAPSE import com.android.systemui.shade.ShadeViewController.Companion.FLING_EXPAND import com.android.systemui.shade.ShadeViewController.Companion.FLING_HIDE diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt index 9851625b6152..d8d42795be58 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt @@ -18,12 +18,12 @@ package com.android.systemui.shade import android.view.WindowManager import com.android.systemui.log.dagger.ShadeWindowLog -import com.android.systemui.plugins.log.ConstantStringsLogger -import com.android.systemui.plugins.log.ConstantStringsLoggerImpl -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogMessage +import com.android.systemui.log.ConstantStringsLogger +import com.android.systemui.log.ConstantStringsLoggerImpl +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogMessage import javax.inject.Inject private const val TAG = "systemui.shadewindow" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt index 90c52bd8c9f4..e008ec0dc75c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar import android.app.PendingIntent import com.android.systemui.log.dagger.NotifInteractionLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.collection.NotificationEntry import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index ea5a1c0fbe70..0ea257010751 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -41,8 +41,8 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; +import static com.android.systemui.log.LogLevel.ERROR; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; -import static com.android.systemui.plugins.log.LogLevel.ERROR; import android.app.AlarmManager; import android.app.admin.DevicePolicyManager; @@ -95,8 +95,8 @@ import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.log.LogLevel; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.KeyguardBypassController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 129c8594e48e..77550038c94a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -26,7 +26,6 @@ import android.annotation.IntDef; import android.app.ActivityManager; import android.app.Notification; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -76,9 +75,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi */ private static final float DARK_ALPHA_BOOST = 0.67f; /** - * Status icons are currently drawn with the intention of being 17sp tall, but we - * want to scale them (in a way that doesn't require an asset dump) down 2sp. So - * 17sp * (15 / 17) = 15sp, the new height. After the first call to {@link #reloadDimens} all + * Status icons are currently drawn with the intention of being 17dp tall, but we + * want to scale them (in a way that doesn't require an asset dump) down 2dp. So + * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all * values will be in px. */ private float mSystemIconDesiredHeight = 15f; @@ -145,7 +144,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private String mNumberText; private StatusBarNotification mNotification; private final boolean mBlocked; - private Configuration mConfiguration; + private int mDensity; private boolean mNightMode; private float mIconScale = 1.0f; private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -199,8 +198,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mNumberPain.setAntiAlias(true); setNotification(sbn); setScaleType(ScaleType.CENTER); - mConfiguration = new Configuration(context.getResources().getConfiguration()); - mNightMode = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) + mDensity = context.getResources().getDisplayMetrics().densityDpi; + Configuration configuration = context.getResources().getConfiguration(); + mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; initializeDecorColor(); reloadDimens(); @@ -214,7 +214,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mAlwaysScaleIcon = true; reloadDimens(); maybeUpdateIconScaleDimens(); - mConfiguration = new Configuration(context.getResources().getConfiguration()); + mDensity = context.getResources().getDisplayMetrics().densityDpi; } /** Should always be preceded by {@link #reloadDimens()} */ @@ -231,17 +231,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private void updateIconScaleForNotifications() { final float imageBounds = mIncreasedSize ? mStatusBarIconDrawingSizeIncreased : mStatusBarIconDrawingSize; - float iconHeight = getIconHeight(); - if (iconHeight != 0) { - mIconScale = imageBounds / iconHeight; - } else { - final int outerBounds = mStatusBarIconSize; - mIconScale = imageBounds / (float) outerBounds; - } + final int outerBounds = mStatusBarIconSize; + mIconScale = imageBounds / (float)outerBounds; updatePivot(); } - // Makes sure that all icons are scaled to the same height (15sp). If we cannot get a height + // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height // for the icon, it uses the default SCALE (15f / 17f) which is the old behavior private void updateIconScaleForSystemIcons() { float iconHeight = getIconHeight(); @@ -272,10 +267,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - final int configDiff = newConfig.diff(mConfiguration); - mConfiguration.setTo(newConfig); - if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { - updateIconDimens(); + int density = newConfig.densityDpi; + if (density != mDensity) { + mDensity = density; + reloadDimens(); + updateDrawable(); + maybeUpdateIconScaleDimens(); } boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; @@ -285,15 +282,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } } - /** - * Update the icon dimens and drawable with current resources - */ - public void updateIconDimens() { - reloadDimens(); - updateDrawable(); - maybeUpdateIconScaleDimens(); - } - private void reloadDimens() { boolean applyRadius = mDotRadius == mStaticDotRadius; Resources res = getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 6e74542691a5..2465c21c956f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -73,9 +73,9 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.log.LogLevel; import com.android.systemui.log.dagger.StatusBarNetworkControllerLog; -import com.android.systemui.plugins.log.LogBuffer; -import com.android.systemui.plugins.log.LogLevel; import com.android.systemui.qs.tiles.dialog.InternetDialogFactory; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; @@ -87,6 +87,8 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.CarrierConfigTracker; +import kotlin.Unit; + import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -101,8 +103,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; -import kotlin.Unit; - /** Platform implementation of the network controller. **/ @SysUISingleton public class NetworkControllerImpl extends BroadcastReceiver diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt index 9ce6b02e55d9..a67c26c06cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.gesture import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.dagger.SwipeUpLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject /** Log messages for [SwipeUpGestureHandler]. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt index 3058fbbc1031..a3a72d92c2e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification import com.android.systemui.log.dagger.NotifInteractionLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.collection.NotificationEntry import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt index dd3c2a9df3e5..f7679ed058c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt @@ -13,9 +13,9 @@ package com.android.systemui.statusbar.notification +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.dagger.NotificationLockscreenLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG import com.android.systemui.statusbar.StatusBarState import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt index 9582dfad35cd..487a5f87d0bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt @@ -17,9 +17,9 @@ package com.android.systemui.statusbar.notification import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.dagger.NotificationRemoteInputLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG import javax.inject.Inject /** Logger class for [RemoteInputController]. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt index 68d1319699d4..39d0833c57d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.coalescer import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject class GroupCoalescerLogger @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt index 2919def16304..79c63e6b0db1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt @@ -1,8 +1,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.row.NotificationGuts import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt index 32c3c6665b6f..e17ce5cff37d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt @@ -3,8 +3,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.util.Log import com.android.systemui.log.dagger.NotificationHeadsUpLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject private const val TAG = "HeadsUpCoordinator" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt index 6503a6403eaa..1f8ec3411bcd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.collection.coordinator +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.UnseenNotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject private const val TAG = "KeyguardCoordinator" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt index 9558f47af795..6271d38f1efa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt index d80445491bda..1f4861a10e75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject private const val TAG = "ShadeEventCoordinator" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index 4adc90aec0fb..f13ff6814df8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -17,10 +17,10 @@ package com.android.systemui.statusbar.notification.collection.listbuilder import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogLevel.WARNING +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt index 911a2d0c2b36..20de785bc9bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt @@ -21,12 +21,12 @@ import android.service.notification.NotificationListenerService import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.StatusBarNotification import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.ERROR -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogLevel.WARNING -import com.android.systemui.plugins.log.LogLevel.WTF +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING +import com.android.systemui.log.LogLevel.WTF import com.android.systemui.statusbar.notification.collection.NotifCollection import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt index 9c71e5c1054c..07fd349d3786 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.render import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection import com.android.systemui.util.Compile diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt index 1e22c2cd7c0e..a880b7157708 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.render import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import java.lang.RuntimeException import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt index d4f11fc141f0..0b31265963ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt @@ -1,8 +1,8 @@ package com.android.systemui.statusbar.notification.interruption import com.android.systemui.log.dagger.NotificationHeadsUpLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.INFO +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt index 115e050258c3..5bac2a9350a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.notification.interruption import android.util.Log import com.android.systemui.log.dagger.NotificationInterruptLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogLevel.WARNING +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import com.android.systemui.util.Compile diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt index 10197a38527e..fe03b2ad6a32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.logging +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.dagger.NotificationRenderLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.INFO import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationSection diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt index 46fef3f973a7..45be0b151870 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.row import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.INFO +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 4522e41daf91..b4bfded58e4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -966,7 +966,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder @Override public ApplicationInfo getApplicationInfo() { - ApplicationInfo applicationInfo = super.getApplicationInfo(); + ApplicationInfo applicationInfo = new ApplicationInfo(super.getApplicationInfo()); applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; return applicationInfo; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt index ce11be36acf3..c3dd92a51a91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt @@ -17,9 +17,9 @@ package com.android.systemui.statusbar.notification.row +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt index 8a5d29a1ae2d..684a276ed635 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.row import com.android.systemui.log.dagger.NotificationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.INFO +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt index b61c55edadcd..f9531876e30d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.dagger.NotificationSectionLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject private const val TAG = "NotifSections" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt index 64dd6dcd3008..5b0ec1d14edc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt @@ -1,9 +1,9 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.log.dagger.NotificationHeadsUpLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.INFO +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt index f5de678a8536..cca84b3330a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt @@ -1,8 +1,8 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.log.dagger.NotificationHeadsUpLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 90a6d0fac7ca..eba04f13ac4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -239,7 +239,11 @@ public class KeyguardClockPositionAlgorithm { } } - private int getExpandedPreferredClockY() { + /** + * give the static topMargin, used for lockscreen clocks to get the initial translationY + * to do counter translation + */ + public int getExpandedPreferredClockY() { if (mIsSplitShade) { return mSplitShadeTargetTopMargin; } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index e835c5cebbc3..12ed71bf14d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -45,7 +45,7 @@ import com.android.systemui.R; import com.android.systemui.animation.InterpolatorsAndroidX; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.plugins.log.LogLevel; +import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.statusbar.CommandQueue; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt index 4839fe6a7bef..5c357d7cd3ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt @@ -20,8 +20,8 @@ import android.util.DisplayMetrics import android.view.View import com.android.internal.logging.nano.MetricsProto.MetricsEvent import com.android.systemui.log.dagger.LSShadeTransitionLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index b9a12e28b8ca..006a029de8e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -306,7 +306,7 @@ public class NotificationIconContainer extends ViewGroup { public void applyIconStates() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); - IconState childState = mIconStates.get(child); + ViewState childState = mIconStates.get(child); if (childState != null) { childState.applyToView(child); } @@ -339,7 +339,6 @@ public class NotificationIconContainer extends ViewGroup { } } if (child instanceof StatusBarIconView) { - ((StatusBarIconView) child).updateIconDimens(); ((StatusBarIconView) child).setDozing(mDozing, false, 0); } } @@ -448,14 +447,9 @@ public class NotificationIconContainer extends ViewGroup { @VisibleForTesting boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd, float iconSize) { - if (isLastChild) { - return translationX + iconSize > layoutEnd; - } else { - // If the child is not the last child, we need to ensure that we have room for the next - // icon and the dot. The dot could be as large as an icon, so verify that we have room - // for 2 icons. - return translationX + iconSize * 2f > layoutEnd; - } + // Layout end, as used here, does not include padding end. + final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize; + return translationX >= overflowX; } /** @@ -495,7 +489,10 @@ public class NotificationIconContainer extends ViewGroup { // First icon to overflow. if (firstOverflowIndex == -1 && isOverflowing) { firstOverflowIndex = i; - mVisualOverflowStart = translationX; + mVisualOverflowStart = layoutEnd - mIconSize; + if (forceOverflow || mIsStaticLayout) { + mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart); + } } final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView ? ((StatusBarIconView) view).getIconScaleIncreased() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 678873c0165c..a8a834f1e8f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -203,7 +203,8 @@ public interface StatusBarIconController { @Override protected LayoutParams onCreateLayoutParams() { - LinearLayout.LayoutParams lp = super.onCreateLayoutParams(); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); return lp; } @@ -369,7 +370,7 @@ public interface StatusBarIconController { private final MobileIconsViewModel mMobileIconsViewModel; protected final Context mContext; - protected int mIconSize; + protected final int mIconSize; // Whether or not these icons show up in dumpsys protected boolean mShouldLog = false; private StatusBarIconController mController; @@ -394,10 +395,10 @@ public interface StatusBarIconController { mStatusBarPipelineFlags = statusBarPipelineFlags; mMobileContextProvider = mobileContextProvider; mContext = group.getContext(); + mIconSize = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_icon_size); mLocation = location; - reloadDimens(); - if (statusBarPipelineFlags.runNewMobileIconsBackend()) { // This starts the flow for the new pipeline, and will notify us of changes if // {@link StatusBarPipelineFlags#useNewMobileIcons} is also true. @@ -608,9 +609,13 @@ public interface StatusBarIconController { mGroup.removeAllViews(); } - protected void reloadDimens() { - mIconSize = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_icon_size); + protected void onDensityOrFontScaleChanged() { + for (int i = 0; i < mGroup.getChildCount(); i++) { + View child = mGroup.getChildAt(i); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); + child.setLayoutParams(lp); + } } private void setHeightAndCenter(ImageView imageView, int height) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 80d5651a65dc..3a184239ac43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -109,7 +109,6 @@ public class StatusBarIconControllerImpl implements Tunable, } group.setController(this); - group.reloadDimens(); mIconGroups.add(group); List<Slot> allSlots = mStatusBarIconList.getSlots(); for (int i = 0; i < allSlots.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt index 1f0b96a58da6..12f023b21701 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt @@ -18,11 +18,11 @@ package com.android.systemui.statusbar.phone import android.app.PendingIntent import com.android.systemui.log.dagger.NotifInteractionLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogLevel.ERROR -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogLevel.WARNING +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index ddbfd43f9bf6..26c17674ab10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -22,8 +22,6 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -74,16 +72,13 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { // Any ignored icon will never be added as a child private ArrayList<String> mIgnoredSlots = new ArrayList<>(); - private Configuration mConfiguration; - public StatusIconContainer(Context context) { this(context, null); } public StatusIconContainer(Context context, AttributeSet attrs) { super(context, attrs); - mConfiguration = new Configuration(context.getResources().getConfiguration()); - reloadDimens(); + initDimens(); setWillNotDraw(!DEBUG_OVERFLOW); } @@ -100,7 +95,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { return mShouldRestrictIcons; } - private void reloadDimens() { + private void initDimens() { // This is the same value that StatusBarIconView uses mIconDotFrameWidth = getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size); @@ -216,16 +211,6 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { child.setTag(R.id.status_bar_view_state_tag, null); } - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - final int configDiff = newConfig.diff(mConfiguration); - mConfiguration.setTo(newConfig); - if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { - reloadDimens(); - } - } - /** * Add a name of an icon slot to be ignored. It will not show up nor be measured * @param slotName name of the icon as it exists in @@ -363,17 +348,13 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { int totalVisible = mLayoutStates.size(); int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; - // Init mUnderflowStart value with the offset to let the dot be placed next to battery icon. - // It to prevent if the underflow happens at rightest(totalVisible - 1) child then break the - // for loop with mUnderflowStart staying 0(initial value), causing the dot be placed at the - // leftest side. - mUnderflowStart = (int) Math.max(contentStart, width - getPaddingEnd() - mUnderflowWidth); + mUnderflowStart = 0; int visible = 0; int firstUnderflowIndex = -1; for (int i = totalVisible - 1; i >= 0; i--) { StatusIconState state = mLayoutStates.get(i); // Allow room for underflow if we found we need it in onMeasure - if ((mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth))) + if (mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth)) || (mShouldRestrictIcons && (visible >= maxVisible))) { firstUnderflowIndex = i; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt index 59f74ec453a6..8c19fb4f43c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.phone.fragment import com.android.systemui.log.dagger.CollapsedSbFragmentLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.disableflags.DisableFlagsLogger import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index b3d246164e87..19c77e0b0de3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -22,7 +22,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBufferFactory import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBufferFactory -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt index 68cbbceb056d..b3a1c4075d87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt @@ -22,8 +22,8 @@ import android.telephony.TelephonyDisplayInfo import com.android.settingslib.SignalIcon import com.android.settingslib.mobile.MobileMappings import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.pipeline.dagger.MobileInputLog import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt index f2f91430eba6..7e0c145696c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt @@ -20,8 +20,8 @@ import android.view.View import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel import java.io.PrintWriter diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt index f67bc8f14447..507549b1e234 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt @@ -19,8 +19,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui import android.view.View import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt index 82492babba46..051f43f1059c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt @@ -19,8 +19,8 @@ package com.android.systemui.statusbar.pipeline.shared import android.net.Network import android.net.NetworkCapabilities import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.pipeline.dagger.SharedConnectivityInputLog import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt index a96e8ff20dd4..328d901b541d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.pipeline.shared import android.net.Network import android.net.NetworkCapabilities -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel /** Helper object for logs that are shared between wifi and mobile. */ object LoggerHelper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt index 2a02687f0761..058eda4400df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.pipeline.shared.data.model import android.net.NetworkCapabilities -import com.android.systemui.plugins.log.LogMessage +import com.android.systemui.log.LogMessage /** * A model for all of the current default connections(s). diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt index bb0b166f7aba..4a9ceacb0bd1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt @@ -19,8 +19,8 @@ package com.android.systemui.statusbar.pipeline.wifi.shared import android.net.Network import android.net.NetworkCapabilities import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.statusbar.pipeline.dagger.WifiInputLog import com.android.systemui.statusbar.pipeline.shared.LoggerHelper import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt index f61f3b7a70f0..6ba2a81b4b13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt @@ -21,9 +21,9 @@ import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED import com.android.internal.R +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.VERBOSE import com.android.systemui.log.dagger.DeviceStateAutoRotationLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.VERBOSE import javax.inject.Inject class DeviceStateRotationLockSettingControllerLogger diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt index df1e80b78c9b..06ed1fd279b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt @@ -17,9 +17,9 @@ package com.android.systemui.statusbar.policy import com.android.systemui.log.dagger.NotificationHeadsUpLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel.INFO -import com.android.systemui.plugins.log.LogLevel.VERBOSE +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.VERBOSE import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt index b563d86f65b1..21d03386b9e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt @@ -311,7 +311,7 @@ interface SmartActionInflater { setBounds(0, 0, newIconSize, newIconSize) } // Add the action icon to the Smart Action button. - setCompoundDrawables(iconDrawable, null, null, null) + setCompoundDrawablesRelative(iconDrawable, null, null, null) val onClickListener = View.OnClickListener { onSmartActionClick(entry, smartActions, actionIndex, action) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 9e88ceb3a0d1..fb6ba8542a3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -588,15 +588,15 @@ public class SmartReplyView extends ViewGroup { } /** - * Returns the combined width of the left drawable (the action icon) and the padding between the - * drawable and the button text. + * Returns the combined width of the start drawable (the action icon) and the padding between + * the drawable and the button text. */ - private int getLeftCompoundDrawableWidthWithPadding(Button button) { - Drawable[] drawables = button.getCompoundDrawables(); - Drawable leftDrawable = drawables[0]; - if (leftDrawable == null) return 0; + private int getStartCompoundDrawableWidthWithPadding(Button button) { + Drawable[] drawables = button.getCompoundDrawablesRelative(); + Drawable startDrawable = drawables[0]; + if (startDrawable == null) return 0; - return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding(); + return startDrawable.getBounds().width() + button.getCompoundDrawablePadding(); } private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) { @@ -605,8 +605,8 @@ public class SmartReplyView extends ViewGroup { // Re-measure the squeezed smart reply button. clearLayoutLineCount(button); final int widthMeasureSpec = MeasureSpec.makeMeasureSpec( - button.getPaddingLeft() + button.getPaddingRight() + textWidth - + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST); + button.getPaddingStart() + button.getPaddingEnd() + textWidth + + getStartCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST); button.measure(widthMeasureSpec, heightMeasureSpec); if (button.getLayout() == null) { Log.wtf(TAG, "Button layout is null after measure."); diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt index 667e22a82a2a..066ac04c2727 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.temporarydisplay import android.view.View -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel /** A logger for temporary view changes -- see [TemporaryViewDisplayController]. */ open class TemporaryViewLogger<T : TemporaryViewInfo>( diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt index f23942847e68..d55751b9d8a0 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.temporarydisplay.chipbar import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import com.android.systemui.temporarydisplay.TemporaryViewLogger import com.android.systemui.temporarydisplay.dagger.ChipbarLog import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt index b1be4045eb43..cae13086f592 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt @@ -18,9 +18,9 @@ package com.android.systemui.temporarydisplay.dagger import android.content.Context import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory import com.android.systemui.media.taptotransfer.MediaTttFlags -import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.settings.DisplayTracker import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt index fda511433143..dfe748afbd41 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt @@ -17,10 +17,10 @@ package com.android.systemui.toast import com.android.systemui.log.dagger.ToastLog -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogLevel.DEBUG -import com.android.systemui.plugins.log.LogMessage +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogMessage import javax.inject.Inject private const val TAG = "ToastLog" diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLog.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLog.java index 59cb0525a7e9..9cebc330af12 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLog.java +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLog.java @@ -18,7 +18,7 @@ package com.android.systemui.util.wakelock; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt index 951903dc29bd..09268007dddc 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt @@ -17,8 +17,8 @@ package com.android.systemui.util.wakelock import android.os.PowerManager -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel import javax.inject.Inject class WakeLockLogger @Inject constructor(@WakeLockLog private val buffer: LogBuffer) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 8f4b32006919..19d5278932c2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -35,7 +35,7 @@ import com.android.systemui.plugins.ClockFaceController import com.android.systemui.plugins.ClockFaceConfig import com.android.systemui.plugins.ClockFaceEvents import com.android.systemui.plugins.ClockTickRate -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 95db0c096faf..fb738454fc71 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -45,6 +45,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.log.LogBuffer; import com.android.systemui.plugins.ClockAnimations; import com.android.systemui.plugins.ClockController; import com.android.systemui.plugins.ClockEvents; @@ -52,7 +53,6 @@ import com.android.systemui.plugins.ClockFaceConfig; import com.android.systemui.plugins.ClockFaceController; import com.android.systemui.plugins.ClockFaceEvents; import com.android.systemui.plugins.ClockTickRate; -import com.android.systemui.plugins.log.LogBuffer; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.clocks.AnimatableClockView; import com.android.systemui.shared.clocks.ClockRegistry; diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt index d7aa6e063d1e..14ad3acf7fb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt @@ -39,10 +39,6 @@ import org.mockito.Mockito.verify import kotlin.math.ceil -private val PAINT = TextPaint().apply { - textSize = 32f -} - @RunWith(AndroidTestingRunner::class) @SmallTest class TextAnimatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt index 063757acc1a1..f6fcd16cfd00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt @@ -49,7 +49,7 @@ private val VF_FONT = Font.Builder(File("/system/fonts/Roboto-Regular.ttf")).bui private fun Font.toTypeface() = Typeface.CustomFallbackBuilder(FontFamily.Builder(this).build()).build() -private val PAINT = TextPaint().apply { +internal val PAINT = TextPaint().apply { typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 400").build().toTypeface() textSize = 32f } @@ -79,7 +79,7 @@ class TextInterpolatorTest : SysuiTestCase() { @Before fun setup() { - typefaceCache = TypefaceVariantCacheImpl() + typefaceCache = TypefaceVariantCacheImpl(PAINT.typeface) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 2747e83acb23..8a62ea0d669e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -25,7 +25,6 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROL import android.hardware.biometrics.BiometricOverlayConstants.ShowReason import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.IUdfpsOverlayControllerCallback -import androidx.test.ext.junit.runners.AndroidJUnit4 import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater import android.view.MotionEvent @@ -35,6 +34,7 @@ import android.view.Surface.Rotation import android.view.View import android.view.WindowManager import android.view.accessibility.AccessibilityManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.settingslib.udfps.UdfpsOverlayParams @@ -69,8 +69,8 @@ import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever private const val REQUEST_ID = 2L @@ -340,4 +340,22 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height()) } } + + @Test + fun fullScreenOverlayWithNewTouchDetectionEnabled() = withRotation(ROTATION_0) { + withReason(REASON_AUTH_KEYGUARD) { + whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true) + + controllerOverlay.show(udfpsController, overlayParams) + verify(windowManager).addView( + eq(controllerOverlay.overlayView), + layoutParamsCaptor.capture() + ) + + // Layout params should use natural display width and height + val lp = layoutParamsCaptor.value + assertThat(lp.width).isEqualTo(overlayParams.naturalDisplayWidth) + assertThat(lp.height).isEqualTo(overlayParams.naturalDisplayHeight) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java index ef1061faeed9..07cb5d88a515 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java @@ -42,6 +42,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import kotlinx.coroutines.CoroutineScope; + @SmallTest @RunWith(AndroidTestingRunner.class) public class AssistantAttentionConditionTest extends SysuiTestCase { @@ -51,6 +53,8 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { AssistUtils mAssistUtils; @Mock DreamOverlayStateController mDreamOverlayStateController; + @Mock + CoroutineScope mScope; private AssistantAttentionCondition mAssistantAttentionCondition; @@ -59,7 +63,7 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mAssistantAttentionCondition = - new AssistantAttentionCondition(mDreamOverlayStateController, mAssistUtils); + new AssistantAttentionCondition(mScope, mDreamOverlayStateController, mAssistUtils); // Adding a callback also starts the condition. mAssistantAttentionCondition.addCallback(mCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java index e1c54976d734..68c79652ac00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.DreamManager; -import android.content.Context; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -42,13 +41,12 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import kotlinx.coroutines.CoroutineScope; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamConditionTest extends SysuiTestCase { @Mock - Context mContext; - - @Mock Condition.Callback mCallback; @Mock @@ -57,6 +55,9 @@ public class DreamConditionTest extends SysuiTestCase { @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + CoroutineScope mScope; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -68,7 +69,8 @@ public class DreamConditionTest extends SysuiTestCase { @Test public void testInitialDreamingState() { when(mDreamManager.isDreaming()).thenReturn(true); - final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor); + final DreamCondition condition = new DreamCondition(mScope, mDreamManager, + mKeyguardUpdateMonitor); condition.addCallback(mCallback); verify(mCallback).onConditionChanged(eq(condition)); @@ -81,7 +83,8 @@ public class DreamConditionTest extends SysuiTestCase { @Test public void testInitialNonDreamingState() { when(mDreamManager.isDreaming()).thenReturn(false); - final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor); + final DreamCondition condition = new DreamCondition(mScope, mDreamManager, + mKeyguardUpdateMonitor); condition.addCallback(mCallback); verify(mCallback, never()).onConditionChanged(eq(condition)); @@ -96,7 +99,8 @@ public class DreamConditionTest extends SysuiTestCase { final ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); when(mDreamManager.isDreaming()).thenReturn(true); - final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor); + final DreamCondition condition = new DreamCondition(mScope, mDreamManager, + mKeyguardUpdateMonitor); condition.addCallback(mCallback); verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt index 19135d0cc800..e8cbdf3db327 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt @@ -21,7 +21,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.ProtoDumpable import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt index 55826141739f..02555cfa783a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.dump import androidx.test.filters.SmallTest import com.android.systemui.Dumpable import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.util.mockito.any import java.io.PrintWriter import org.junit.Before diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt index 64547f4463d1..bd029a727ee3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt @@ -16,9 +16,9 @@ package com.android.systemui.dump -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogcatEchoTracker /** * Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests. diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt index bf3c73a2c94e..c2195c7bc2c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt @@ -24,8 +24,8 @@ import com.android.keyguard.logging.TrustRepositoryLogger import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.user.data.repository.FakeUserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/packages/SystemUI/plugin/tests/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt index a39b856f0f49..0cf6d3da7e9c 100644 --- a/packages/SystemUI/plugin/tests/log/LogBufferTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt @@ -2,7 +2,6 @@ package com.android.systemui.log import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.log.LogBuffer import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt index f867fc7bf84a..12f46898ab8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt @@ -18,10 +18,10 @@ package com.android.systemui.log.table import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogcatEchoTracker import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt index 8da1c646109d..b322bb7d6b46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.media.taptotransfer.common import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt index 95df484cddc2..64f3fd304ca6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.media.taptotransfer.receiver import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt index 003375709a16..2287da572795 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.media.taptotransfer.sender import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter diff --git a/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java index fb7197706ddc..ff60fcda53e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java @@ -37,6 +37,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import kotlinx.coroutines.CoroutineScope; + @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest @@ -47,6 +49,9 @@ public class SystemProcessConditionTest extends SysuiTestCase { @Mock Monitor.Callback mCallback; + @Mock + CoroutineScope mScope; + private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); @Before @@ -61,7 +66,7 @@ public class SystemProcessConditionTest extends SysuiTestCase { @Test public void testConditionFailsWithNonSystemProcess() { - final Condition condition = new SystemProcessCondition(mProcessWrapper); + final Condition condition = new SystemProcessCondition(mScope, mProcessWrapper); when(mProcessWrapper.isSystemUser()).thenReturn(false); final Monitor monitor = new Monitor(mExecutor); @@ -82,7 +87,7 @@ public class SystemProcessConditionTest extends SysuiTestCase { @Test public void testConditionSucceedsWithSystemProcess() { - final Condition condition = new SystemProcessCondition(mProcessWrapper); + final Condition condition = new SystemProcessCondition(mScope, mProcessWrapper); when(mProcessWrapper.isSystemUser()).thenReturn(true); final Monitor monitor = new Monitor(mExecutor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt index 68c10f20f6f7..aacc695ef301 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt @@ -20,7 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.statusbar.disableflags.DisableFlagsLogger import com.google.common.truth.Truth.assertThat import java.io.PrintWriter diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java index 31a33d4ff908..cbd9dba3cdbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java @@ -30,6 +30,7 @@ import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivit import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -127,6 +128,9 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { @Before public void setUp() { + assumeFalse("Skip test: does not apply to watches", + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); + MockitoAnnotations.initMocks(this); mMainHandler = mContext.getMainThreadHandler(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt new file mode 100644 index 000000000000..1a0e932d7e8a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt @@ -0,0 +1,450 @@ +/* + * 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.shared.condition + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.shared.condition.Condition.START_EAGERLY +import com.android.systemui.shared.condition.Condition.START_LAZILY +import com.android.systemui.shared.condition.Condition.START_WHEN_NEEDED +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class CombinedConditionTest : SysuiTestCase() { + + class FakeCondition + constructor( + scope: CoroutineScope, + initialValue: Boolean?, + overriding: Boolean = false, + @StartStrategy private val startStrategy: Int = START_WHEN_NEEDED, + ) : Condition(scope, initialValue, overriding) { + private var _started = false + val started: Boolean + get() = _started + + override fun start() { + _started = true + } + + override fun stop() { + _started = false + } + + override fun getStartStrategy(): Int { + return startStrategy + } + + fun setValue(value: Boolean?) { + value?.also { updateCondition(value) } ?: clearCondition() + } + } + + @Test + fun testOrOperatorWithMixedConditions() = runSelfCancelingTest { + val startWhenNeededCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_WHEN_NEEDED) + val eagerCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_EAGERLY) + val lazyCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_LAZILY) + + val combinedCondition = + CombinedCondition( + scope = this, + conditions = + listOf( + eagerCondition, + lazyCondition, + startWhenNeededCondition, + ), + operand = Evaluator.OP_OR + ) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isTrue() + + eagerCondition.setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isFalse() + + startWhenNeededCondition.setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isFalse() + + startWhenNeededCondition.setValue(false) + eagerCondition.setValue(false) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isTrue() + } + + @Test + fun testAndOperatorWithMixedConditions() = runSelfCancelingTest { + val startWhenNeededCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_WHEN_NEEDED) + val eagerCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_EAGERLY) + val lazyCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_LAZILY) + + val combinedCondition = + CombinedCondition( + scope = this, + conditions = + listOf( + startWhenNeededCondition, + lazyCondition, + eagerCondition, + ), + operand = Evaluator.OP_AND + ) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isFalse() + assertThat(startWhenNeededCondition.started).isFalse() + + eagerCondition.setValue(true) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isTrue() + assertThat(lazyCondition.started).isFalse() + + startWhenNeededCondition.setValue(true) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isFalse() + assertThat(lazyCondition.started).isTrue() + + startWhenNeededCondition.setValue(false) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isFalse() + assertThat(lazyCondition.started).isTrue() + + startWhenNeededCondition.setValue(true) + lazyCondition.setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(eagerCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isTrue() + assertThat(lazyCondition.started).isTrue() + } + + @Test + fun testAndOperatorWithStartWhenNeededConditions() = runSelfCancelingTest { + val conditions = + 0.rangeTo(2) + .map { + FakeCondition( + scope = this, + initialValue = false, + startStrategy = START_WHEN_NEEDED + ) + } + .toList() + + val combinedCondition = + CombinedCondition(scope = this, conditions = conditions, operand = Evaluator.OP_AND) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + assertThat(combinedCondition.isConditionMet).isFalse() + // Only the first condition should be started + assertThat(areStarted(conditions)).containsExactly(true, false, false).inOrder() + + conditions[0].setValue(true) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(areStarted(conditions)).containsExactly(false, true, false).inOrder() + + conditions[1].setValue(true) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(areStarted(conditions)).containsExactly(false, false, true).inOrder() + + conditions[2].setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(areStarted(conditions)).containsExactly(true, true, true).inOrder() + + conditions[0].setValue(false) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(areStarted(conditions)).containsExactly(true, false, false).inOrder() + } + + @Test + fun testOrOperatorWithStartWhenNeededConditions() = runSelfCancelingTest { + val conditions = + 0.rangeTo(2) + .map { + FakeCondition( + scope = this, + initialValue = false, + startStrategy = START_WHEN_NEEDED + ) + } + .toList() + + val combinedCondition = + CombinedCondition(scope = this, conditions = conditions, operand = Evaluator.OP_OR) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + // Default is to monitor all conditions when false + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(areStarted(conditions)).containsExactly(true, true, true).inOrder() + + // Condition 2 is true, so we should only monitor condition 2 + conditions[1].setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(areStarted(conditions)).containsExactly(false, true, false).inOrder() + + // Condition 2 becomes false, so we go back to monitoring all conditions + conditions[1].setValue(false) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(areStarted(conditions)).containsExactly(true, true, true).inOrder() + + // Condition 3 becomes true, so we only monitor condition 3 + conditions[2].setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(areStarted(conditions)).containsExactly(false, false, true).inOrder() + + // Condition 2 becomes true, but we are still only monitoring condition 3 + conditions[1].setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(areStarted(conditions)).containsExactly(false, false, true).inOrder() + + // Condition 3 becomes false, so we should now only be monitoring condition 2 + conditions[2].setValue(false) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(areStarted(conditions)).containsExactly(false, true, false).inOrder() + } + + @Test + fun testRemovingCallbackWillStopMonitoringAllConditions() = runSelfCancelingTest { + val conditions = + 0.rangeTo(2) + .map { + FakeCondition( + scope = this, + initialValue = false, + startStrategy = START_WHEN_NEEDED + ) + } + .toList() + + val combinedCondition = + CombinedCondition(scope = this, conditions = conditions, operand = Evaluator.OP_OR) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + assertThat(areStarted(conditions)).containsExactly(true, true, true) + + combinedCondition.removeCallback(callback) + assertThat(areStarted(conditions)).containsExactly(false, false, false) + } + + @Test + fun testOverridingConditionSkipsOtherConditions() = runSelfCancelingTest { + val startWhenNeededCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_WHEN_NEEDED) + val eagerCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_EAGERLY) + val lazyCondition = + FakeCondition(scope = this, initialValue = false, startStrategy = START_LAZILY) + val overridingCondition1 = + FakeCondition(scope = this, initialValue = null, overriding = true) + val overridingCondition2 = + FakeCondition(scope = this, initialValue = null, overriding = true) + + val combinedCondition = + CombinedCondition( + scope = this, + conditions = + listOf( + eagerCondition, + overridingCondition1, + lazyCondition, + startWhenNeededCondition, + overridingCondition2 + ), + operand = Evaluator.OP_OR + ) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isTrue() + assertThat(startWhenNeededCondition.started).isTrue() + assertThat(overridingCondition1.started).isTrue() + + // Overriding condition is true, so we should stop monitoring all other conditions + overridingCondition1.setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(eagerCondition.started).isFalse() + assertThat(lazyCondition.started).isFalse() + assertThat(startWhenNeededCondition.started).isFalse() + assertThat(overridingCondition1.started).isTrue() + assertThat(overridingCondition2.started).isFalse() + + // Overriding condition is false, so we should only monitor other overriding conditions + overridingCondition1.setValue(false) + eagerCondition.setValue(true) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(eagerCondition.started).isFalse() + assertThat(lazyCondition.started).isFalse() + assertThat(startWhenNeededCondition.started).isFalse() + assertThat(overridingCondition1.started).isTrue() + assertThat(overridingCondition2.started).isTrue() + + // Second overriding condition is true, condition 1 is still true + overridingCondition2.setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(eagerCondition.started).isFalse() + assertThat(lazyCondition.started).isFalse() + assertThat(startWhenNeededCondition.started).isFalse() + assertThat(overridingCondition1.started).isFalse() + assertThat(overridingCondition2.started).isTrue() + + // Overriding condition is cleared, condition 1 is still true + overridingCondition1.setValue(null) + overridingCondition2.setValue(null) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(eagerCondition.started).isTrue() + assertThat(lazyCondition.started).isFalse() + assertThat(startWhenNeededCondition.started).isFalse() + assertThat(overridingCondition1.started).isTrue() + assertThat(overridingCondition2.started).isTrue() + } + + @Test + fun testAndOperatorCorrectlyHandlesUnknownValues() = runSelfCancelingTest { + val conditions = + 0.rangeTo(2) + .map { + FakeCondition(scope = this, initialValue = false, startStrategy = START_EAGERLY) + } + .toList() + + val combinedCondition = + CombinedCondition(scope = this, conditions = conditions, operand = Evaluator.OP_AND) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + + conditions[0].setValue(null) + conditions[1].setValue(true) + conditions[2].setValue(false) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(combinedCondition.isConditionSet).isTrue() + + // The condition should not be set since the value is unknown + conditions[2].setValue(true) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(combinedCondition.isConditionSet).isFalse() + + conditions[0].setValue(true) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(combinedCondition.isConditionSet).isTrue() + } + + @Test + fun testOrOperatorCorrectlyHandlesUnknownValues() = runSelfCancelingTest { + val conditions = + 0.rangeTo(2) + .map { + FakeCondition(scope = this, initialValue = false, startStrategy = START_EAGERLY) + } + .toList() + + val combinedCondition = + CombinedCondition(scope = this, conditions = conditions, operand = Evaluator.OP_OR) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + + conditions[0].setValue(null) + conditions[1].setValue(true) + conditions[2].setValue(false) + assertThat(combinedCondition.isConditionMet).isTrue() + assertThat(combinedCondition.isConditionSet).isTrue() + + conditions[1].setValue(false) + assertThat(combinedCondition.isConditionMet).isFalse() + // The condition should not be set since the value is unknown + assertThat(combinedCondition.isConditionSet).isFalse() + } + + @Test + fun testEmptyConditions() = runSelfCancelingTest { + for (operand in intArrayOf(Evaluator.OP_OR, Evaluator.OP_AND)) { + val combinedCondition = + CombinedCondition( + scope = this, + conditions = emptyList(), + operand = operand, + ) + + val callback = Condition.Callback {} + combinedCondition.addCallback(callback) + assertThat(combinedCondition.isConditionMet).isFalse() + assertThat(combinedCondition.isConditionSet).isFalse() + } + } + + private fun areStarted(conditions: List<FakeCondition>): List<Boolean> { + return conditions.map { it.started } + } + + /** + * Executes the given block of execution within the scope of a dedicated [CoroutineScope] which + * is then automatically canceled and cleaned-up. + */ + private fun runSelfCancelingTest( + block: suspend CoroutineScope.() -> Unit, + ) = + runBlocking(IMMEDIATE) { + val scope = CoroutineScope(coroutineContext + Job()) + block(scope) + scope.cancel() + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt index 2b4a7fb4803b..0a8210d6b319 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt @@ -32,7 +32,7 @@ class ConditionExtensionsTest : SysuiTestCase() { fun flowInitiallyTrue() = testScope.runTest { val flow = flowOf(true) - val condition = flow.toCondition(this) + val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) runCurrent() assertThat(condition.isConditionSet).isFalse() @@ -47,7 +47,7 @@ class ConditionExtensionsTest : SysuiTestCase() { fun flowInitiallyFalse() = testScope.runTest { val flow = flowOf(false) - val condition = flow.toCondition(this) + val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) runCurrent() assertThat(condition.isConditionSet).isFalse() @@ -62,7 +62,7 @@ class ConditionExtensionsTest : SysuiTestCase() { fun emptyFlowWithNoInitialValue() = testScope.runTest { val flow = emptyFlow<Boolean>() - val condition = flow.toCondition(this) + val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) condition.start() runCurrent() @@ -74,7 +74,12 @@ class ConditionExtensionsTest : SysuiTestCase() { fun emptyFlowWithInitialValueOfTrue() = testScope.runTest { val flow = emptyFlow<Boolean>() - val condition = flow.toCondition(scope = this, initialValue = true) + val condition = + flow.toCondition( + scope = this, + strategy = Condition.START_EAGERLY, + initialValue = true + ) condition.start() runCurrent() @@ -86,7 +91,12 @@ class ConditionExtensionsTest : SysuiTestCase() { fun emptyFlowWithInitialValueOfFalse() = testScope.runTest { val flow = emptyFlow<Boolean>() - val condition = flow.toCondition(scope = this, initialValue = false) + val condition = + flow.toCondition( + scope = this, + strategy = Condition.START_EAGERLY, + initialValue = false + ) condition.start() runCurrent() @@ -98,7 +108,7 @@ class ConditionExtensionsTest : SysuiTestCase() { fun conditionUpdatesWhenFlowEmitsNewValue() = testScope.runTest { val flow = MutableStateFlow(false) - val condition = flow.toCondition(this) + val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY) condition.start() runCurrent() @@ -120,7 +130,7 @@ class ConditionExtensionsTest : SysuiTestCase() { fun stoppingConditionUnsubscribesFromFlow() = testScope.runTest { val flow = MutableSharedFlow<Boolean>() - val condition = flow.toCondition(this) + val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY) runCurrent() assertThat(flow.subscriptionCount.value).isEqualTo(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java index aa1636d8a030..de5824d1f463 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java @@ -39,12 +39,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Arrays; import java.util.HashSet; +import kotlinx.coroutines.CoroutineScope; + @SmallTest @RunWith(AndroidTestingRunner.class) public class ConditionMonitorTest extends SysuiTestCase { @@ -54,15 +57,18 @@ public class ConditionMonitorTest extends SysuiTestCase { private HashSet<Condition> mConditions; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + @Mock + private CoroutineScope mScope; + private Monitor mConditionMonitor; @Before public void setup() { MockitoAnnotations.initMocks(this); - mCondition1 = spy(new FakeCondition()); - mCondition2 = spy(new FakeCondition()); - mCondition3 = spy(new FakeCondition()); + mCondition1 = spy(new FakeCondition(mScope)); + mCondition2 = spy(new FakeCondition(mScope)); + mCondition3 = spy(new FakeCondition(mScope)); mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3)); mConditionMonitor = new Monitor(mExecutor); @@ -396,7 +402,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void unsetCondition_shouldNotAffectValue() { - final FakeCondition settableCondition = new FakeCondition(null, false); + final FakeCondition settableCondition = new FakeCondition(mScope, null, false); mCondition1.fakeUpdateCondition(true); mCondition2.fakeUpdateCondition(true); mCondition3.fakeUpdateCondition(true); @@ -414,7 +420,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void setUnsetCondition_shouldAffectValue() { - final FakeCondition settableCondition = new FakeCondition(null, false); + final FakeCondition settableCondition = new FakeCondition(mScope, null, false); mCondition1.fakeUpdateCondition(true); mCondition2.fakeUpdateCondition(true); mCondition3.fakeUpdateCondition(true); @@ -443,7 +449,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void clearingOverridingCondition_shouldBeExcluded() { - final FakeCondition overridingCondition = new FakeCondition(true, true); + final FakeCondition overridingCondition = new FakeCondition(mScope, true, true); mCondition1.fakeUpdateCondition(false); mCondition2.fakeUpdateCondition(false); mCondition3.fakeUpdateCondition(false); @@ -466,7 +472,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void settingUnsetOverridingCondition_shouldBeIncluded() { - final FakeCondition overridingCondition = new FakeCondition(null, true); + final FakeCondition overridingCondition = new FakeCondition(mScope, null, true); mCondition1.fakeUpdateCondition(false); mCondition2.fakeUpdateCondition(false); mCondition3.fakeUpdateCondition(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java index 8443221e8b7a..6efade9c9361 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java @@ -34,15 +34,23 @@ import com.android.systemui.SysuiTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import kotlinx.coroutines.CoroutineScope; @SmallTest @RunWith(AndroidTestingRunner.class) public class ConditionTest extends SysuiTestCase { + @Mock + CoroutineScope mScope; + private FakeCondition mCondition; @Before public void setup() { - mCondition = spy(new FakeCondition()); + MockitoAnnotations.initMocks(this); + mCondition = spy(new FakeCondition(mScope)); } @Test @@ -152,168 +160,4 @@ public class ConditionTest extends SysuiTestCase { mCondition.clearCondition(); assertThat(mCondition.isConditionSet()).isFalse(); } - - @Test - public void combineConditionsWithOr_allFalse_reportsNotMet() { - mCondition.fakeUpdateCondition(false); - - final Condition combinedCondition = mCondition.or( - new FakeCondition(/* initialValue= */ false)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isFalse(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithOr_allTrue_reportsMet() { - mCondition.fakeUpdateCondition(true); - - final Condition combinedCondition = mCondition.or( - new FakeCondition(/* initialValue= */ true)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isTrue(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithOr_singleTrue_reportsMet() { - mCondition.fakeUpdateCondition(false); - - final Condition combinedCondition = mCondition.or( - new FakeCondition(/* initialValue= */ true)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isTrue(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithOr_unknownAndTrue_reportsMet() { - mCondition.fakeUpdateCondition(true); - - // Combine with an unset condition. - final Condition combinedCondition = mCondition.or( - new FakeCondition(/* initialValue= */ null)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isTrue(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithOr_unknownAndFalse_reportsNotMet() { - mCondition.fakeUpdateCondition(false); - - // Combine with an unset condition. - final Condition combinedCondition = mCondition.or( - new FakeCondition(/* initialValue= */ null)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isFalse(); - assertThat(combinedCondition.isConditionMet()).isFalse(); - verify(callback, never()).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithAnd_allFalse_reportsNotMet() { - mCondition.fakeUpdateCondition(false); - - final Condition combinedCondition = mCondition.and( - new FakeCondition(/* initialValue= */ false)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isFalse(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithAnd_allTrue_reportsMet() { - mCondition.fakeUpdateCondition(true); - - final Condition combinedCondition = mCondition.and( - new FakeCondition(/* initialValue= */ true)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isTrue(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithAnd_singleTrue_reportsNotMet() { - mCondition.fakeUpdateCondition(true); - - final Condition combinedCondition = mCondition.and( - new FakeCondition(/* initialValue= */ false)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isFalse(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithAnd_unknownAndTrue_reportsNotMet() { - mCondition.fakeUpdateCondition(true); - - // Combine with an unset condition. - final Condition combinedCondition = mCondition.and( - new FakeCondition(/* initialValue= */ null)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isFalse(); - assertThat(combinedCondition.isConditionMet()).isFalse(); - verify(callback, never()).onConditionChanged(combinedCondition); - } - - @Test - public void combineConditionsWithAnd_unknownAndFalse_reportsMet() { - mCondition.fakeUpdateCondition(false); - - // Combine with an unset condition. - final Condition combinedCondition = mCondition.and( - new FakeCondition(/* initialValue= */ null)); - - final Condition.Callback callback = mock( - Condition.Callback.class); - combinedCondition.addCallback(callback); - - assertThat(combinedCondition.isConditionSet()).isTrue(); - assertThat(combinedCondition.isConditionMet()).isFalse(); - verify(callback, times(1)).onConditionChanged(combinedCondition); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java index 55a6d39d4644..a325cbf25ffe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java @@ -16,21 +16,19 @@ package com.android.systemui.shared.condition; +import kotlinx.coroutines.CoroutineScope; + /** * Fake implementation of {@link Condition}, and provides a way for tests to update * condition fulfillment. */ public class FakeCondition extends Condition { - FakeCondition() { - super(); - } - - FakeCondition(Boolean initialValue) { - super(initialValue, false); + FakeCondition(CoroutineScope scope) { + super(scope); } - FakeCondition(Boolean initialValue, boolean overriding) { - super(initialValue, overriding); + FakeCondition(CoroutineScope scope, Boolean initialValue, boolean overriding) { + super(scope, initialValue, overriding); } @Override @@ -41,6 +39,11 @@ public class FakeCondition extends Condition { public void stop() { } + @Override + protected int getStartStrategy() { + return START_EAGERLY; + } + public void fakeUpdateCondition(boolean isConditionMet) { updateCondition(isConditionMet); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt index 5fc0ffe42f55..8cb530c355bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt @@ -4,7 +4,7 @@ import android.testing.AndroidTestingRunner import android.util.DisplayMetrics import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.log.LogBuffer import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.LockscreenGestureLogger diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java index 5431eba8441c..c7ea09cb519d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java @@ -70,7 +70,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java index 9441d49d454a..d5689dcb3173 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java @@ -48,7 +48,7 @@ import com.android.settingslib.SignalIcon.MobileIconGroup; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.DataUsageController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.CarrierConfigTracker; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java index 4c1f0a8a1066..35b9814cd81d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java @@ -43,7 +43,7 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.DataUsageController; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.log.LogBuffer; +import com.android.systemui.log.LogBuffer; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.CarrierConfigTracker; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt index bef9fcb5697c..a37c38669bfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.statusbar.notification import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.statusbar.StatusBarState import com.google.common.truth.Truth.assertThat import org.junit.Before diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt index c282c1ef0cf6..b80b825d87dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -21,8 +21,6 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT -import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -51,7 +49,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_oneIcon_widthForOneIcon() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10) + iconContainer.setIconSize(10); assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f), /* actual= */ 30f) @@ -61,7 +59,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fourIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10) + iconContainer.setIconSize(10); assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f), /* actual= */ 60f) @@ -71,7 +69,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fiveIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10) + iconContainer.setIconSize(10); assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f), /* actual= */ 60f) } @@ -80,7 +78,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10) + iconContainer.setIconSize(10); val icon = mockStatusBarIcon() iconContainer.addView(icon) @@ -101,7 +99,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10) + iconContainer.setIconSize(10); val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -130,7 +128,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10) + iconContainer.setIconSize(10); val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -156,55 +154,6 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test - fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() { - iconContainer.setActualPaddingStart(0f) - iconContainer.setActualPaddingEnd(0f) - iconContainer.setActualLayoutWidth(30) - iconContainer.setIconSize(10) - - val iconOne = mockStatusBarIcon() - val iconTwo = mockStatusBarIcon() - val iconThree = mockStatusBarIcon() - - iconContainer.addView(iconOne) - iconContainer.addView(iconTwo) - iconContainer.addView(iconThree) - assertEquals(3, iconContainer.childCount) - - iconContainer.calculateIconXTranslations() - assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) - assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) - assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation) - assertFalse(iconContainer.areIconsOverflowing()) - } - - @Test - fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() { - iconContainer.setActualPaddingStart(0f) - iconContainer.setActualPaddingEnd(0f) - iconContainer.setActualLayoutWidth(35) - iconContainer.setIconSize(10) - - val iconOne = mockStatusBarIcon() - val iconTwo = mockStatusBarIcon() - val iconThree = mockStatusBarIcon() - val iconFour = mockStatusBarIcon() - - iconContainer.addView(iconOne) - iconContainer.addView(iconTwo) - iconContainer.addView(iconThree) - iconContainer.addView(iconFour) - assertEquals(4, iconContainer.childCount) - - iconContainer.calculateIconXTranslations() - assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) - assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) - assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState) - assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState) - assertTrue(iconContainer.areIconsOverflowing()) - } - - @Test fun shouldForceOverflow_appearingAboveSpeedBump_true() { val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 1, @@ -212,7 +161,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 1f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow) + assertTrue(forceOverflow); } @Test @@ -223,7 +172,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow) + assertTrue(forceOverflow); } @Test @@ -234,7 +183,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertFalse(forceOverflow) + assertFalse(forceOverflow); } @Test @@ -261,17 +210,6 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test - fun isOverflowing_lastChildXGreaterThanDotX_true() { - val isOverflowing = iconContainer.isOverflowing( - /* isLastChild= */ true, - /* translationX= */ 9f, - /* layoutEnd= */ 10f, - /* iconSize= */ 2f, - ) - assertTrue(isOverflowing) - } - - @Test fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, @@ -315,7 +253,7 @@ class NotificationIconContainerTest : SysuiTestCase() { assertTrue(isOverflowing) } - private fun mockStatusBarIcon(): StatusBarIconView { + private fun mockStatusBarIcon() : StatusBarIconView { val iconView = mock(StatusBarIconView::class.java) whenever(iconView.width).thenReturn(10) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt index ac3b28c72fa2..9bc49ae02436 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt @@ -20,7 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.android.systemui.statusbar.disableflags.DisableFlagsLogger import com.google.common.truth.Truth.assertThat import java.io.PrintWriter diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 03fafcb85325..21769922c899 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -54,9 +54,9 @@ import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.plugins.log.LogBuffer; -import com.android.systemui.plugins.log.LogcatEchoTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.ShadeViewController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt index 03cd94fa9d70..c43778a4ae51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.pipeline.shared.data.model import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.log.LogMessageImpl +import com.android.systemui.log.LogMessageImpl import com.google.common.truth.Truth.assertThat import org.junit.Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index d9d8b6345fcb..3b0d5120cca3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -585,8 +585,6 @@ public class SmartReplyViewTest extends SysuiTestCase { // devices. layout.setBaselineAligned(false); - final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - // Add smart replies Button previous = null; SmartReplyView.SmartReplies smartReplies = @@ -606,11 +604,7 @@ public class SmartReplyViewTest extends SysuiTestCase { if (previous != null) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) previous.getLayoutParams(); - if (isRtl) { - lp.leftMargin = mSpacing; - } else { - lp.rightMargin = mSpacing; - } + lp.setMarginEnd(mSpacing); } layout.addView(current); previous = current; @@ -634,11 +628,7 @@ public class SmartReplyViewTest extends SysuiTestCase { if (previous != null) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) previous.getLayoutParams(); - if (isRtl) { - lp.leftMargin = mSpacing; - } else { - lp.rightMargin = mSpacing; - } + lp.setMarginEnd(mSpacing); } layout.addView(current); previous = current; @@ -937,8 +927,8 @@ public class SmartReplyViewTest extends SysuiTestCase { .collect(Collectors.toList()); Button singleLineButton = buttons.get(0); Button doubleLineButton = buttons.get(1); - Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable - Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable + Drawable singleLineDrawable = singleLineButton.getCompoundDrawablesRelative()[0]; // start + Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawablesRelative()[0]; // start assertEquals(singleLineDrawable.getBounds().width(), doubleLineDrawable.getBounds().width()); assertEquals(singleLineDrawable.getBounds().height(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt index 2e66b205bfd5..451424927b23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.temporarydisplay import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.plugins.log.LogBuffer -import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.log.LogcatEchoTracker import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 0172eaf03fa7..c8db662a7f49 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -54,6 +54,7 @@ import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.companion.AssociationInfo; import android.companion.AssociationRequest; import android.companion.DeviceNotAssociatedException; @@ -234,7 +235,7 @@ public class CompanionDeviceManagerService extends SystemService { loadAssociationsFromDisk(); mAssociationStore.registerListener(mAssociationStoreChangeListener); - mDevicePresenceMonitor = new CompanionDevicePresenceMonitor( + mDevicePresenceMonitor = new CompanionDevicePresenceMonitor(mUserManager, mAssociationStore, mDevicePresenceCallback); mAssociationRequestsProcessor = new AssociationRequestsProcessor( @@ -322,6 +323,23 @@ public class CompanionDeviceManagerService extends SystemService { MINUTES.toMillis(10)); } + @Override + public void onUserUnlocked(@NonNull TargetUser user) { + // Notify and bind the app after the phone is unlocked. + final int userId = user.getUserIdentifier(); + final Set<BluetoothDevice> blueToothDevices = + mDevicePresenceMonitor.getPendingConnectedDevices().get(userId); + if (blueToothDevices != null) { + for (BluetoothDevice bluetoothDevice : blueToothDevices) { + for (AssociationInfo ai: + mAssociationStore.getAssociationsByAddress(bluetoothDevice.getAddress())) { + Slog.i(TAG, "onUserUnlocked, device id( " + ai.getId() + " ) is connected"); + mDevicePresenceMonitor.onBluetoothCompanionDeviceConnected(ai.getId()); + } + } + } + } + @NonNull AssociationInfo getAssociationWithCallerChecks( @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) { diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java index 6f99d8677646..e436e9300fb5 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java @@ -128,9 +128,9 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private static final class CallMetadataSyncConnectionIdentifier { private final int mAssociationId; - private final long mCallId; + private final String mCallId; - CallMetadataSyncConnectionIdentifier(int associationId, long callId) { + CallMetadataSyncConnectionIdentifier(int associationId, String callId) { mAssociationId = associationId; mCallId = callId; } @@ -139,7 +139,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { return mAssociationId; } - public long getCallId() { + public String getCallId() { return mCallId; } @@ -161,9 +161,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private abstract static class CallMetadataSyncConnectionCallback { - abstract void sendCallAction(int associationId, long callId, int action); - - abstract void sendStateChange(int associationId, long callId, int newState); + abstract void sendCallAction(int associationId, String callId, int action); } private static class CallMetadataSyncConnection extends Connection { @@ -184,7 +182,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { mCallback = callback; } - public long getCallId() { + public String getCallId() { return mCall.getId(); } @@ -205,22 +203,22 @@ public class CallMetadataSyncConnectionService extends ConnectionService { } final Bundle extras = new Bundle(); - extras.putLong(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId()); + extras.putString(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId()); putExtras(extras); int capabilities = getConnectionCapabilities(); - if (mCall.hasControl(android.companion.Telecom.Call.PUT_ON_HOLD)) { + if (mCall.hasControl(android.companion.Telecom.PUT_ON_HOLD)) { capabilities |= CAPABILITY_HOLD; } else { capabilities &= ~CAPABILITY_HOLD; } - if (mCall.hasControl(android.companion.Telecom.Call.MUTE)) { + if (mCall.hasControl(android.companion.Telecom.MUTE)) { capabilities |= CAPABILITY_MUTE; } else { capabilities &= ~CAPABILITY_MUTE; } mAudioManager.setMicrophoneMute( - mCall.hasControl(android.companion.Telecom.Call.UNMUTE)); + mCall.hasControl(android.companion.Telecom.UNMUTE)); if (capabilities != getConnectionCapabilities()) { setConnectionCapabilities(capabilities); } @@ -248,8 +246,8 @@ public class CallMetadataSyncConnectionService extends ConnectionService { int capabilities = getConnectionCapabilities(); final boolean hasHoldControl = mCall.hasControl( - android.companion.Telecom.Call.PUT_ON_HOLD) - || mCall.hasControl(android.companion.Telecom.Call.TAKE_OFF_HOLD); + android.companion.Telecom.PUT_ON_HOLD) + || mCall.hasControl(android.companion.Telecom.TAKE_OFF_HOLD); if (hasHoldControl != ((getConnectionCapabilities() & CAPABILITY_HOLD) == CAPABILITY_HOLD)) { if (hasHoldControl) { @@ -258,7 +256,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { capabilities &= ~CAPABILITY_HOLD; } } - final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.Call.MUTE); + final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.MUTE); if (hasMuteControl != ((getConnectionCapabilities() & CAPABILITY_MUTE) == CAPABILITY_MUTE)) { if (hasMuteControl) { @@ -268,7 +266,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { } } mAudioManager.setMicrophoneMute( - mCall.hasControl(android.companion.Telecom.Call.UNMUTE)); + mCall.hasControl(android.companion.Telecom.UNMUTE)); if (capabilities != getConnectionCapabilities()) { setConnectionCapabilities(capabilities); } @@ -276,12 +274,12 @@ public class CallMetadataSyncConnectionService extends ConnectionService { @Override public void onAnswer(int videoState) { - sendCallAction(android.companion.Telecom.Call.ACCEPT); + sendCallAction(android.companion.Telecom.ACCEPT); } @Override public void onReject() { - sendCallAction(android.companion.Telecom.Call.REJECT); + sendCallAction(android.companion.Telecom.REJECT); } @Override @@ -296,33 +294,28 @@ public class CallMetadataSyncConnectionService extends ConnectionService { @Override public void onSilence() { - sendCallAction(android.companion.Telecom.Call.SILENCE); + sendCallAction(android.companion.Telecom.SILENCE); } @Override public void onHold() { - sendCallAction(android.companion.Telecom.Call.PUT_ON_HOLD); + sendCallAction(android.companion.Telecom.PUT_ON_HOLD); } @Override public void onUnhold() { - sendCallAction(android.companion.Telecom.Call.TAKE_OFF_HOLD); + sendCallAction(android.companion.Telecom.TAKE_OFF_HOLD); } @Override public void onMuteStateChanged(boolean isMuted) { - sendCallAction(isMuted ? android.companion.Telecom.Call.MUTE - : android.companion.Telecom.Call.UNMUTE); + sendCallAction(isMuted ? android.companion.Telecom.MUTE + : android.companion.Telecom.UNMUTE); } @Override public void onDisconnect() { - sendCallAction(android.companion.Telecom.Call.END); - } - - @Override - public void onStateChanged(int state) { - mCallback.sendStateChange(mAssociationId, mCall.getId(), state); + sendCallAction(android.companion.Telecom.END); } private void sendCallAction(int action) { diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java index 1e4bb9a504ba..5b0c745a7173 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java @@ -33,14 +33,14 @@ import java.util.Set; /** A read-only snapshot of an {@link ContextSyncMessage}. */ class CallMetadataSyncData { - final Map<Long, CallMetadataSyncData.Call> mCalls = new HashMap<>(); + final Map<String, CallMetadataSyncData.Call> mCalls = new HashMap<>(); final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>(); public void addCall(CallMetadataSyncData.Call call) { mCalls.put(call.getId(), call); } - public boolean hasCall(long id) { + public boolean hasCall(String id) { return mCalls.containsKey(id); } @@ -57,7 +57,7 @@ class CallMetadataSyncData { } public static class Call implements Parcelable { - private long mId; + private String mId; private String mCallerId; private byte[] mAppIcon; private String mAppName; @@ -67,7 +67,7 @@ class CallMetadataSyncData { public static Call fromParcel(Parcel parcel) { final Call call = new Call(); - call.setId(parcel.readLong()); + call.setId(parcel.readString()); call.setCallerId(parcel.readString()); call.setAppIcon(parcel.readBlob()); call.setAppName(parcel.readString()); @@ -82,7 +82,7 @@ class CallMetadataSyncData { @Override public void writeToParcel(Parcel parcel, int parcelableFlags) { - parcel.writeLong(mId); + parcel.writeString(mId); parcel.writeString(mCallerId); parcel.writeBlob(mAppIcon); parcel.writeString(mAppName); @@ -94,7 +94,7 @@ class CallMetadataSyncData { } } - void setId(long id) { + void setId(String id) { mId = id; } @@ -122,7 +122,7 @@ class CallMetadataSyncData { mControls.add(control); } - long getId() { + String getId() { return mId; } @@ -157,7 +157,7 @@ class CallMetadataSyncData { @Override public boolean equals(Object other) { if (other instanceof CallMetadataSyncData.Call) { - return ((Call) other).getId() == getId(); + return mId != null && mId.equals(((Call) other).getId()); } return false; } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java index 443a732eb6f1..0c2373023177 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java @@ -41,7 +41,6 @@ import java.util.stream.Collectors; public class CallMetadataSyncInCallService extends InCallService { private static final String TAG = "CallMetadataIcs"; - private static final long NOT_VALID = -1L; private CompanionDeviceManagerServiceInternal mCdmsi; @@ -71,7 +70,7 @@ public class CallMetadataSyncInCallService extends InCallService { callMetadataSyncData.getRequests().iterator(); while (iterator.hasNext()) { final CallMetadataSyncData.Call call = iterator.next(); - if (call.getId() != 0) { + if (call.getId() != null) { // The call is already assigned an id; treat as control invocations. for (int control : call.getControls()) { processCallControlAction(call.getId(), control); @@ -81,41 +80,41 @@ public class CallMetadataSyncInCallService extends InCallService { } } - private void processCallControlAction(long crossDeviceCallId, + private void processCallControlAction(String crossDeviceCallId, int callControlAction) { final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId, mCurrentCalls.values()); switch (callControlAction) { - case android.companion.Telecom.Call.ACCEPT: + case android.companion.Telecom.ACCEPT: if (crossDeviceCall != null) { crossDeviceCall.doAccept(); } break; - case android.companion.Telecom.Call.REJECT: + case android.companion.Telecom.REJECT: if (crossDeviceCall != null) { crossDeviceCall.doReject(); } break; - case android.companion.Telecom.Call.SILENCE: + case android.companion.Telecom.SILENCE: doSilence(); break; - case android.companion.Telecom.Call.MUTE: + case android.companion.Telecom.MUTE: doMute(); break; - case android.companion.Telecom.Call.UNMUTE: + case android.companion.Telecom.UNMUTE: doUnmute(); break; - case android.companion.Telecom.Call.END: + case android.companion.Telecom.END: if (crossDeviceCall != null) { crossDeviceCall.doEnd(); } break; - case android.companion.Telecom.Call.PUT_ON_HOLD: + case android.companion.Telecom.PUT_ON_HOLD: if (crossDeviceCall != null) { crossDeviceCall.doPutOnHold(); } break; - case android.companion.Telecom.Call.TAKE_OFF_HOLD: + case android.companion.Telecom.TAKE_OFF_HOLD: if (crossDeviceCall != null) { crossDeviceCall.doTakeOffHold(); } @@ -171,12 +170,12 @@ public class CallMetadataSyncInCallService extends InCallService { @Nullable @VisibleForTesting - CrossDeviceCall getCallForId(long crossDeviceCallId, Collection<CrossDeviceCall> calls) { - if (crossDeviceCallId == NOT_VALID) { + CrossDeviceCall getCallForId(String crossDeviceCallId, Collection<CrossDeviceCall> calls) { + if (crossDeviceCallId == null) { return null; } for (CrossDeviceCall crossDeviceCall : calls) { - if (crossDeviceCall.getId() == crossDeviceCallId) { + if (crossDeviceCallId.equals(crossDeviceCall.getId())) { return crossDeviceCall; } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java index ac981d45561b..168068ec6497 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java @@ -23,7 +23,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.telecom.Call; import android.telecom.CallAudioState; import android.telecom.VideoProfile; @@ -34,7 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.ByteArrayOutputStream; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; +import java.util.UUID; /** Data holder for a telecom call and additional metadata. */ public class CrossDeviceCall { @@ -45,9 +44,7 @@ public class CrossDeviceCall { "com.android.companion.datatransfer.contextsync.extra.CALL_ID"; private static final int APP_ICON_BITMAP_DIMENSION = 256; - private static final AtomicLong sNextId = new AtomicLong(1); - - private final long mId; + private final String mId; private Call mCall; @VisibleForTesting boolean mIsEnterprise; @VisibleForTesting boolean mIsOtt; @@ -64,14 +61,14 @@ public class CrossDeviceCall { CallAudioState callAudioState) { this(packageManager, call.getDetails(), callAudioState); mCall = call; - final Bundle extras = new Bundle(); - extras.putLong(EXTRA_CALL_ID, mId); - call.putExtras(extras); + call.putExtra(EXTRA_CALL_ID, mId); } CrossDeviceCall(PackageManager packageManager, Call.Details callDetails, CallAudioState callAudioState) { - mId = sNextId.getAndIncrement(); + final String predefinedId = callDetails.getIntentExtras() != null + ? callDetails.getIntentExtras().getString(EXTRA_CALL_ID) : null; + mId = predefinedId != null ? predefinedId : UUID.randomUUID().toString(); mCallingAppPackageName = callDetails.getAccountHandle().getComponentName().getPackageName(); mIsOtt = (callDetails.getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED) @@ -145,7 +142,7 @@ public class CrossDeviceCall { if (mStatus == android.companion.Telecom.Call.RINGING) { mStatus = android.companion.Telecom.Call.RINGING_SILENCED; } - mControls.remove(android.companion.Telecom.Call.SILENCE); + mControls.remove(android.companion.Telecom.SILENCE); } @VisibleForTesting @@ -156,26 +153,26 @@ public class CrossDeviceCall { mControls.clear(); if (mStatus == android.companion.Telecom.Call.RINGING || mStatus == android.companion.Telecom.Call.RINGING_SILENCED) { - mControls.add(android.companion.Telecom.Call.ACCEPT); - mControls.add(android.companion.Telecom.Call.REJECT); + mControls.add(android.companion.Telecom.ACCEPT); + mControls.add(android.companion.Telecom.REJECT); if (mStatus == android.companion.Telecom.Call.RINGING) { - mControls.add(android.companion.Telecom.Call.SILENCE); + mControls.add(android.companion.Telecom.SILENCE); } } if (mStatus == android.companion.Telecom.Call.ONGOING || mStatus == android.companion.Telecom.Call.ON_HOLD) { - mControls.add(android.companion.Telecom.Call.END); + mControls.add(android.companion.Telecom.END); if (callDetails.can(Call.Details.CAPABILITY_HOLD)) { mControls.add( mStatus == android.companion.Telecom.Call.ON_HOLD - ? android.companion.Telecom.Call.TAKE_OFF_HOLD - : android.companion.Telecom.Call.PUT_ON_HOLD); + ? android.companion.Telecom.TAKE_OFF_HOLD + : android.companion.Telecom.PUT_ON_HOLD); } } if (mStatus == android.companion.Telecom.Call.ONGOING && callDetails.can( Call.Details.CAPABILITY_MUTE)) { - mControls.add(mIsMuted ? android.companion.Telecom.Call.UNMUTE - : android.companion.Telecom.Call.MUTE); + mControls.add(mIsMuted ? android.companion.Telecom.UNMUTE + : android.companion.Telecom.MUTE); } } @@ -212,7 +209,7 @@ public class CrossDeviceCall { } } - public long getId() { + public String getId() { return mId; } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java index adc5faf24f2c..e5ab963b6cbb 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java @@ -270,7 +270,7 @@ public class CrossDeviceSyncController { while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (pis.getFieldNumber()) { case (int) Telecom.Call.ID: - call.setId(pis.readLong(Telecom.Call.ID)); + call.setId(pis.readString(Telecom.Call.ID)); break; case (int) Telecom.Call.ORIGIN: final long originToken = pis.start(Telecom.Call.ORIGIN); @@ -336,7 +336,7 @@ public class CrossDeviceSyncController { } /** Create a call control message. */ - public static byte[] createCallControlMessage(long callId, int control) { + public static byte[] createCallControlMessage(String callId, int control) { final ProtoOutputStream pos = new ProtoOutputStream(); pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION); final long telecomToken = pos.start(ContextSyncMessage.TELECOM); diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java index f6b99b551ecb..a5410e448c7b 100644 --- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java +++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java @@ -27,17 +27,24 @@ import android.companion.AssociationInfo; import android.net.MacAddress; import android.os.Handler; import android.os.HandlerExecutor; +import android.os.UserHandle; +import android.os.UserManager; import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.server.companion.AssociationStore; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; @SuppressLint("LongLogTag") -class BluetoothCompanionDeviceConnectionListener +public class BluetoothCompanionDeviceConnectionListener extends BluetoothAdapter.BluetoothConnectionCallback implements AssociationStore.OnChangeListener { private static final String TAG = "CDM_BluetoothCompanionDeviceConnectionListener"; @@ -48,15 +55,25 @@ class BluetoothCompanionDeviceConnectionListener void onBluetoothCompanionDeviceDisconnected(int associationId); } + private final UserManager mUserManager; private final @NonNull AssociationStore mAssociationStore; private final @NonNull Callback mCallback; /** A set of ALL connected BT device (not only companion.) */ private final @NonNull Map<MacAddress, BluetoothDevice> mAllConnectedDevices = new HashMap<>(); - BluetoothCompanionDeviceConnectionListener(@NonNull AssociationStore associationStore, - @NonNull Callback callback) { + /** + * A structure hold the connected BT devices that are pending to be reported to the companion + * app when the user unlocks the local device per userId. + */ + @GuardedBy("mPendingConnectedDevices") + @NonNull + final SparseArray<Set<BluetoothDevice>> mPendingConnectedDevices = new SparseArray<>(); + + BluetoothCompanionDeviceConnectionListener(UserManager userManager, + @NonNull AssociationStore associationStore, @NonNull Callback callback) { mAssociationStore = associationStore; mCallback = callback; + mUserManager = userManager; } public void init(@NonNull BluetoothAdapter btAdapter) { @@ -76,12 +93,26 @@ class BluetoothCompanionDeviceConnectionListener if (DEBUG) Log.i(TAG, "onDevice_Connected() " + btDeviceToString(device)); final MacAddress macAddress = MacAddress.fromString(device.getAddress()); + final int userId = UserHandle.myUserId(); + if (mAllConnectedDevices.put(macAddress, device) != null) { if (DEBUG) Log.w(TAG, "Device " + btDeviceToString(device) + " is already connected."); return; } + // Try to bind and notify the app after the phone is unlocked. + if (!mUserManager.isUserUnlockingOrUnlocked(UserHandle.myUserId())) { + Slog.i(TAG, "Current user is not in unlocking or unlocked stage yet. Notify " + + "the application when the phone is unlocked"); + synchronized (mPendingConnectedDevices) { + Set<BluetoothDevice> bluetoothDevices = mPendingConnectedDevices.get( + userId, new HashSet<>()); + bluetoothDevices.add(device); + mPendingConnectedDevices.put(userId, bluetoothDevices); + } - onDeviceConnectivityChanged(device, true); + } else { + onDeviceConnectivityChanged(device, true); + } } /** @@ -98,6 +129,8 @@ class BluetoothCompanionDeviceConnectionListener } final MacAddress macAddress = MacAddress.fromString(device.getAddress()); + final int userId = UserHandle.myUserId(); + if (mAllConnectedDevices.remove(macAddress) == null) { if (DEBUG) { Log.w(TAG, "The device wasn't tracked as connected " + btDeviceToString(device)); @@ -105,6 +138,19 @@ class BluetoothCompanionDeviceConnectionListener return; } + // Do not need to report the connectivity since the user is not unlock the phone so + // that cdm is not bind with the app yet. + if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { + synchronized (mPendingConnectedDevices) { + Set<BluetoothDevice> bluetoothDevices = mPendingConnectedDevices.get(userId); + if (bluetoothDevices != null) { + bluetoothDevices.remove(device); + } + } + + return; + } + onDeviceConnectivityChanged(device, false); } diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 4010be922b2c..f6e9415a2a7e 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -23,13 +23,16 @@ import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.companion.AssociationInfo; import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.UserManager; import android.util.Log; +import android.util.SparseArray; import com.android.server.companion.AssociationStore; @@ -86,13 +89,12 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper = new SimulatedDevicePresenceSchedulerHelper(); - public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore, - @NonNull Callback callback) { + public CompanionDevicePresenceMonitor(UserManager userManager, + @NonNull AssociationStore associationStore, @NonNull Callback callback) { mAssociationStore = associationStore; mCallback = callback; - - mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(associationStore, - /* BluetoothCompanionDeviceConnectionListener.Callback */ this); + mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(userManager, + associationStore, /* BluetoothCompanionDeviceConnectionListener.Callback */ this); mBleScanner = new BleCompanionDeviceScanner(associationStore, /* BleCompanionDeviceScanner.Callback */ this); } @@ -298,6 +300,15 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange // what's needed. } + /** + * Return a set of devices that pending to report connectivity + */ + public SparseArray<Set<BluetoothDevice>> getPendingConnectedDevices() { + synchronized (mBtConnectionListener.mPendingConnectedDevices) { + return mBtConnectionListener.mPendingConnectedDevices; + } + } + private static void enforceCallerShellOrRoot() { final int callingUid = Binder.getCallingUid(); if (callingUid == SHELL_UID || callingUid == ROOT_UID) return; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1a5d4253b5ba..a992765c0411 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16060,6 +16060,8 @@ public class ActivityManagerService extends IActivityManager.Stub final int procState = uidRec != null ? uidRec.getSetProcState() : PROCESS_STATE_NONEXISTENT; + final int procAdj = uidRec != null + ? uidRec.getMinProcAdj() : ProcessList.INVALID_ADJ; final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; final int capability = uidRec != null ? uidRec.getSetCapability() : 0; final boolean ephemeral = uidRec != null ? uidRec.isEphemeral() : isEphemeralLocked(uid); @@ -16075,7 +16077,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final int enqueuedChange = mUidObserverController.enqueueUidChange( uidRec == null ? null : uidRec.pendingChange, - uid, change, procState, procStateSeq, capability, ephemeral); + uid, change, procState, procAdj, procStateSeq, capability, ephemeral); if (uidRec != null) { uidRec.setLastReportedChange(enqueuedChange); } diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index 5a4d315767ca..4a69f90d9fc0 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -67,7 +67,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; -import android.os.UserManager; import android.text.TextUtils; import android.util.EventLog; import android.util.IndentingPrintWriter; @@ -78,6 +77,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.os.TimeoutRecord; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; +import com.android.server.pm.UserJourneyLogger; import com.android.server.pm.UserManagerInternal; import dalvik.annotation.optimization.NeverCompile; @@ -1518,7 +1518,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { final UserInfo userInfo = (umInternal != null) ? umInternal.getUserInfo(r.userId) : null; if (userInfo != null) { - userType = UserManager.getUserTypeForStatsd(userInfo.userType); + userType = UserJourneyLogger.getUserTypeForStatsd(userInfo.userType); } Slog.i(TAG_BROADCAST, "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:" diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 3bc5de91b2bd..3e82d557c01a 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -1015,6 +1015,12 @@ public final class CachedAppOptimizer { private static native String getFreezerCheckPath(); /** + * Check if task_profiles.json includes valid freezer profiles and actions + * @return false if there are invalid profiles or actions + */ + private static native boolean isFreezerProfileValid(); + + /** * Determines whether the freezer is supported by this system */ public static boolean isFreezerSupported() { @@ -1031,16 +1037,19 @@ public final class CachedAppOptimizer { // Also check freezer binder ioctl Slog.d(TAG_AM, "Checking binder freezer ioctl"); getBinderFreezeInfo(Process.myPid()); - supported = true; + + // Check if task_profiles.json contains invalid profiles + Slog.d(TAG_AM, "Checking freezer profiles"); + supported = isFreezerProfileValid(); } else { - Slog.e(TAG_AM, "unexpected value in cgroup.freeze"); + Slog.e(TAG_AM, "Unexpected value in cgroup.freeze"); } } catch (java.io.FileNotFoundException e) { - Slog.w(TAG_AM, "cgroup.freeze not present"); + Slog.w(TAG_AM, "File cgroup.freeze not present"); } catch (RuntimeException e) { - Slog.w(TAG_AM, "unable to read freezer info"); + Slog.w(TAG_AM, "Unable to read freezer info"); } catch (Exception e) { - Slog.w(TAG_AM, "unable to read cgroup.freeze: " + e.toString()); + Slog.w(TAG_AM, "Unable to read cgroup.freeze: " + e.toString()); } if (fr != null) { diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java index 490a0232bfaa..79089074dd1c 100644 --- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java +++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java @@ -34,7 +34,7 @@ import android.app.ForegroundServiceDelegationOptions; import android.content.ComponentName; import android.content.pm.ServiceInfo; import android.util.ArrayMap; -import android.util.Log; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -192,13 +192,20 @@ public class ForegroundServiceTypeLoggerModule { final ArrayList<Integer> apiTypes = convertFgsTypeToApiTypes(record.foregroundServiceType); final UidState uidState = mUids.get(uid); if (uidState == null) { - Log.e(TAG, "FGS stop call being logged with no start call for UID " + uid); + Slog.wtfStack(TAG, "FGS stop call being logged with no start call for UID for UID " + + uid + + " in package " + record.packageName); return; } final ArrayList<Integer> apisFound = new ArrayList<>(); final ArrayList<Long> timestampsFound = new ArrayList<>(); for (int i = 0, size = apiTypes.size(); i < size; i++) { - int apiType = apiTypes.get(i); + final int apiType = apiTypes.get(i); + if (!uidState.mOpenWithFgsCount.contains(apiType)) { + Slog.wtfStack(TAG, "Logger should be tracking FGS types correctly for UID " + uid + + " in package " + record.packageName); + continue; + } // retrieve the eligible closed call // we only want to log if this is the only // open in flight call. If there are other calls, @@ -215,7 +222,8 @@ public class ForegroundServiceTypeLoggerModule { final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType = uidState.mRunningFgs.get(apiType); if (runningFgsOfType == null) { - Log.w(TAG, "Could not find appropriate running FGS for FGS stop"); + Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid + + " in package " + record.packageName); continue; } @@ -322,7 +330,7 @@ public class ForegroundServiceTypeLoggerModule { // it's not related to any FGS UidState uidState = mUids.get(uid); if (uidState == null) { - Log.w(TAG, "API event end called before start!"); + Slog.w(TAG, "API event end called before start!"); return -1; } if (uidState.mOpenWithFgsCount.contains(apiType)) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 438a08c44ef4..0417b8cfa2e2 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -644,6 +644,11 @@ class ProcessRecord implements WindowProcessListener { } } + @GuardedBy({"mService", "mProcLock"}) + int getSetAdj() { + return mState.getSetAdj(); + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) IApplicationThread getThread() { return mThread; diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java index 51cb9878c0b3..790cc7b87f80 100644 --- a/services/core/java/com/android/server/am/UidObserverController.java +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -96,7 +96,7 @@ public class UidObserverController { } int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, - long procStateSeq, int capability, boolean ephemeral) { + int procAdj, long procStateSeq, int capability, boolean ephemeral) { synchronized (mLock) { if (mPendingUidChanges.size() == 0) { if (DEBUG_UID_OBSERVERS) { @@ -117,6 +117,7 @@ public class UidObserverController { changeRecord.uid = uid; changeRecord.change = change; changeRecord.procState = procState; + changeRecord.procAdj = procAdj; changeRecord.procStateSeq = procStateSeq; changeRecord.capability = capability; changeRecord.ephemeral = ephemeral; @@ -344,7 +345,7 @@ public class UidObserverController { } if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROC_OOM_ADJ) != 0 && (change & UidRecord.CHANGE_PROCADJ) != 0) { - observer.onUidProcAdjChanged(item.uid); + observer.onUidProcAdjChanged(item.uid, item.procAdj); } } final int duration = (int) (SystemClock.uptimeMillis() - start); @@ -426,6 +427,7 @@ public class UidObserverController { public int uid; public int change; public int procState; + public int procAdj; public int capability; public boolean ephemeral; public long procStateSeq; @@ -435,6 +437,7 @@ public class UidObserverController { changeRecord.uid = uid; changeRecord.change = change; changeRecord.procState = procState; + changeRecord.procAdj = procAdj; changeRecord.capability = capability; changeRecord.ephemeral = ephemeral; changeRecord.procStateSeq = procStateSeq; diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index e39ac2b08479..993088ef106e 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -51,6 +51,12 @@ public final class UidRecord { private boolean mProcAdjChanged; @CompositeRWLock({"mService", "mProcLock"}) + private int mCurAdj; + + @CompositeRWLock({"mService", "mProcLock"}) + private int mSetAdj; + + @CompositeRWLock({"mService", "mProcLock"}) private int mCurCapability; @CompositeRWLock({"mService", "mProcLock"}) @@ -201,12 +207,24 @@ public final class UidRecord { mProcAdjChanged = false; } - @GuardedBy({"mService", "mProcLock"}) + @GuardedBy(anyOf = {"mService", "mProcLock"}) boolean getProcAdjChanged() { return mProcAdjChanged; } @GuardedBy(anyOf = {"mService", "mProcLock"}) + int getMinProcAdj() { + int minAdj = ProcessList.UNKNOWN_ADJ; + for (int i = mProcRecords.size() - 1; i >= 0; i--) { + int adj = mProcRecords.valueAt(i).getSetAdj(); + if (adj < minAdj) { + minAdj = adj; + } + } + return minAdj; + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) int getCurCapability() { return mCurCapability; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b2fdee7a6f89..06af2ce9d655 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -46,13 +46,24 @@ import static com.android.server.am.UserState.STATE_BOOTING; import static com.android.server.am.UserState.STATE_RUNNING_LOCKED; import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED; import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INVALID_SESSION_ID; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKED_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKING_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE; import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND_VISIBLE; import static com.android.server.pm.UserManagerInternal.USER_START_MODE_FOREGROUND; import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString; import static com.android.server.pm.UserManagerInternal.userStartModeToString; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -123,6 +134,8 @@ import com.android.server.LocalServices; import com.android.server.SystemService.UserCompletedEventType; import com.android.server.SystemServiceManager; import com.android.server.am.UserState.KeyEvictedCallback; +import com.android.server.pm.UserJourneyLogger; +import com.android.server.pm.UserJourneyLogger.UserJourneySession; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerInternal.UserLifecycleListener; import com.android.server.pm.UserManagerInternal.UserStartMode; @@ -138,7 +151,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -221,75 +233,6 @@ class UserController implements Handler.Callback { // TODO(b/197344658): Increase to 10s or 15s once we have a switch-UX-is-done invocation too. private static final int USER_COMPLETED_EVENT_DELAY_MS = 5 * 1000; - // Used for statsd logging with UserLifecycleJourneyReported + UserLifecycleEventOccurred atoms - private static final long INVALID_SESSION_ID = 0; - - // The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd - private static final int USER_JOURNEY_UNKNOWN = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN; - private static final int USER_JOURNEY_USER_SWITCH_FG = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG; - private static final int USER_JOURNEY_USER_SWITCH_UI = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI; - private static final int USER_JOURNEY_USER_START = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; - private static final int USER_JOURNEY_USER_CREATE = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; - private static final int USER_JOURNEY_USER_STOP = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; - @IntDef(prefix = { "USER_JOURNEY" }, value = { - USER_JOURNEY_UNKNOWN, - USER_JOURNEY_USER_SWITCH_FG, - USER_JOURNEY_USER_SWITCH_UI, - USER_JOURNEY_USER_START, - USER_JOURNEY_USER_CREATE, - USER_JOURNEY_USER_STOP - }) - @interface UserJourney {} - - // The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd - private static final int USER_LIFECYCLE_EVENT_UNKNOWN = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN; - private static final int USER_LIFECYCLE_EVENT_SWITCH_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER; - private static final int USER_LIFECYCLE_EVENT_START_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER; - private static final int USER_LIFECYCLE_EVENT_CREATE_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; - private static final int USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__USER_RUNNING_LOCKED; - private static final int USER_LIFECYCLE_EVENT_UNLOCKING_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; - private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; - private static final int USER_LIFECYCLE_EVENT_STOP_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; - @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = { - USER_LIFECYCLE_EVENT_UNKNOWN, - USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_START_USER, - USER_LIFECYCLE_EVENT_CREATE_USER, - USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, - USER_LIFECYCLE_EVENT_UNLOCKING_USER, - USER_LIFECYCLE_EVENT_UNLOCKED_USER, - USER_LIFECYCLE_EVENT_STOP_USER - }) - @interface UserLifecycleEvent {} - - // User lifecyle event state, defined in the UserLifecycleEventOccurred atom for statsd - private static final int USER_LIFECYCLE_EVENT_STATE_BEGIN = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN; - private static final int USER_LIFECYCLE_EVENT_STATE_FINISH = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH; - private static final int USER_LIFECYCLE_EVENT_STATE_NONE = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE; - @IntDef(prefix = { "USER_LIFECYCLE_EVENT_STATE" }, value = { - USER_LIFECYCLE_EVENT_STATE_BEGIN, - USER_LIFECYCLE_EVENT_STATE_FINISH, - USER_LIFECYCLE_EVENT_STATE_NONE, - }) - @interface UserLifecycleEventState {} - /** * Maximum number of users we allow to be running at a time, including system user. * @@ -420,13 +363,6 @@ class UserController implements Handler.Callback { private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>(); /** - * {@link UserIdInt} to {@link UserJourneySession} mapping used for statsd logging for the - * UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms. - */ - @GuardedBy("mUserIdToUserJourneyMap") - private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>(); - - /** * Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet- * unreported user-starting events have transpired for the given user. */ @@ -621,8 +557,9 @@ class UserController implements Handler.Callback { // but we might immediately step into RUNNING below if the user // storage is already unlocked. if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, - USER_LIFECYCLE_EVENT_STATE_NONE); + mInjector.getUserJourneyLogger() + .logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + EVENT_STATE_NONE); mInjector.getUserManagerInternal().setUserState(userId, uss.state); // Do not report secondary users, runtime restarts or first boot/upgrade if (userId == UserHandle.USER_SYSTEM @@ -694,8 +631,9 @@ class UserController implements Handler.Callback { private boolean finishUserUnlocking(final UserState uss) { final int userId = uss.mHandle.getIdentifier(); EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKING, userId); - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + mInjector.getUserJourneyLogger() + .logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER, + EVENT_STATE_BEGIN); // If the user key hasn't been unlocked yet, we cannot proceed. if (!StorageManager.isUserKeyUnlocked(userId)) return false; synchronized (mLock) { @@ -1073,9 +1011,7 @@ class UserController implements Handler.Callback { return; } - logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP); - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(userId, USER_JOURNEY_USER_STOP); if (stopUserCallback != null) { uss.mStopCallbacks.add(stopUserCallback); @@ -1138,9 +1074,16 @@ class UserController implements Handler.Callback { synchronized (mLock) { if (uss.state != UserState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_NONE); - clearSessionId(userId); + UserJourneySession session = mInjector.getUserJourneyLogger() + .logUserJourneyFinishWithError(-1, getUserInfo(userId), + USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } else { + mInjector.getUserJourneyLogger() + .logUserJourneyFinishWithError(-1, getUserInfo(userId), + USER_JOURNEY_USER_STOP, ERROR_CODE_INVALID_SESSION_ID); + } return; } uss.setState(UserState.STATE_SHUTDOWN); @@ -1247,9 +1190,11 @@ class UserController implements Handler.Callback { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - clearSessionId(userId); + UserJourneySession session = mInjector.getUserJourneyLogger() + .logUserJourneyFinish(-1, userInfo, USER_JOURNEY_USER_STOP); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } if (lockUser) { dispatchUserLocking(userIdToLock, keyEvictedCallbacks); @@ -1259,9 +1204,11 @@ class UserController implements Handler.Callback { // which was paused while the SHUTDOWN flow of the user was in progress. resumePendingUserStarts(userId); } else { - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_NONE); - clearSessionId(userId); + UserJourneySession session = mInjector.getUserJourneyLogger() + .finishAndClearIncompleteUserJourney(userId, USER_JOURNEY_USER_STOP); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } } } @@ -3135,10 +3082,7 @@ class UserController implements Handler.Callback { public boolean handleMessage(Message msg) { switch (msg.what) { case START_USER_SWITCH_FG_MSG: - logUserJourneyInfo(getUserInfo(getCurrentUserId()), getUserInfo(msg.arg1), - USER_JOURNEY_USER_SWITCH_FG); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_SWITCH_FG); startUserInForeground(msg.arg1); break; case REPORT_USER_SWITCH_MSG: @@ -3160,18 +3104,15 @@ class UserController implements Handler.Callback { mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, Integer.toString(msg.arg1), msg.arg1); - logUserJourneyInfo(null, getUserInfo(msg.arg1), USER_JOURNEY_USER_START); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_START); mInjector.onUserStarting(/* userId= */ msg.arg1); scheduleOnUserCompletedEvent(msg.arg1, UserCompletedEventType.EVENT_TYPE_USER_STARTING, USER_COMPLETED_EVENT_DELAY_MS); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - clearSessionId(msg.arg1, USER_JOURNEY_USER_START); + mInjector.getUserJourneyLogger().logUserJourneyFinish(-1 , getUserInfo(msg.arg1), + USER_JOURNEY_USER_START); break; case USER_UNLOCK_MSG: final int userId = msg.arg1; @@ -3180,10 +3121,11 @@ class UserController implements Handler.Callback { FgThread.getHandler().post(() -> { mInjector.loadUserRecents(userId); }); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKING_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + + mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1, + USER_LIFECYCLE_EVENT_UNLOCKING_USER, EVENT_STATE_FINISH); + mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1, + USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_BEGIN); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("finishUserUnlocked-" + userId); @@ -3199,9 +3141,9 @@ class UserController implements Handler.Callback { // (No need to acquire lock to read mCurrentUserId since it is volatile.) // TODO: Find something to wait for in the case of a profile. mCurrentUserId == msg.arg1 ? USER_COMPLETED_EVENT_DELAY_MS : 1000); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - clearSessionId(msg.arg1); + mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1, + USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_FINISH); + // Unlocking user is not a journey no need to clear sessionId break; case USER_CURRENT_MSG: mInjector.batteryStatsServiceNoteEvent( @@ -3224,22 +3166,24 @@ class UserController implements Handler.Callback { break; case REPORT_USER_SWITCH_COMPLETE_MSG: dispatchUserSwitchComplete(msg.arg1, msg.arg2); - logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); + UserJourneySession session = mInjector.getUserJourneyLogger() + .logUserSwitchJourneyFinish(msg.arg1, getUserInfo(msg.arg2)); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } break; case REPORT_LOCKED_BOOT_COMPLETE_MSG: dispatchLockedBootComplete(msg.arg1); break; case START_USER_SWITCH_UI_MSG: final Pair<UserInfo, UserInfo> fromToUserPair = (Pair<UserInfo, UserInfo>) msg.obj; - logUserJourneyInfo(fromToUserPair.first, fromToUserPair.second, - USER_JOURNEY_USER_SWITCH_UI); - logUserLifecycleEvent(fromToUserPair.second.id, USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(fromToUserPair.second.id, USER_JOURNEY_USER_SWITCH_UI); showUserSwitchDialog(fromToUserPair); break; case CLEAR_USER_JOURNEY_SESSION_MSG: - logAndClearSessionId(msg.arg1); + mInjector.getUserJourneyLogger() + .finishAndClearIncompleteUserJourney(msg.arg1, msg.arg2); + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, msg.obj); break; case COMPLETE_USER_SWITCH_MSG: completeUserSwitch(msg.arg1, msg.arg2); @@ -3317,123 +3261,29 @@ class UserController implements Handler.Callback { * statsd helper method for logging the start of a user journey via a UserLifecycleEventOccurred * atom given the originating and targeting users for the journey. */ - private void logUserJourneyInfo(UserInfo origin, UserInfo target, @UserJourney int journey) { - final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - synchronized (mUserIdToUserJourneyMap) { - UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(target.id); - if (userJourneySession != null) { - // TODO(b/157007231): Move this logic to a separate class/file. - if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI - || userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG) - && (journey == USER_JOURNEY_USER_START - || journey == USER_JOURNEY_USER_STOP)) { - /* - * There is already a user switch journey, and a user start or stop journey for - * the same target user received. New journey is most likely a part of user - * switch journey so no need to create a new journey. - */ - if (DEBUG_MU) { - Slogf.d(TAG, journey + " not logged as it is expected to be part of " - + userJourneySession.mJourney); - } - return; - } - /* - * Possible reasons for this condition to be true: - * - A user switch journey is received while another user switch journey is in - * process for the same user. - * - A user switch journey is received while user start journey is in process for - * the same user. - * - A user start journey is received while another user start journey is in process - * for the same user. - * In all cases potentially an incomplete, timed-out session or multiple - * simultaneous requests. It is not possible to keep track of multiple sessions for - * the same user, so previous session is abandoned. - */ - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, - userJourneySession.mSessionId, target.id, USER_LIFECYCLE_EVENT_UNKNOWN, - USER_LIFECYCLE_EVENT_STATE_NONE); - } - + private void logUserJourneyBegin(int targetId, + @UserJourneyLogger.UserJourney int journey) { + UserJourneySession oldSession = mInjector.getUserJourneyLogger() + .finishAndClearIncompleteUserJourney(targetId, journey); + if (oldSession != null) { if (DEBUG_MU) { Slogf.d(TAG, - "Starting a new journey: " + journey + " with session id: " + newSessionId); + "Starting a new journey: " + journey + " with session id: " + + oldSession); } - - userJourneySession = new UserJourneySession(newSessionId, journey); - mUserIdToUserJourneyMap.put(target.id, userJourneySession); /* - * User lifecyle journey would be complete when {@code #clearSessionId} is called after - * the last expected lifecycle event for the journey. It may be possible that the last - * event is not called, e.g., user not unlocked after user switching. In such cases user - * journey is cleared after {@link USER_JOURNEY_TIMEOUT}. + * User lifecycle journey would be complete when {@code #clearSessionId} is called + * after the last expected lifecycle event for the journey. It may be possible that + * the last event is not called, e.g., user not unlocked after user switching. In such + * cases user journey is cleared after {@link USER_JOURNEY_TIMEOUT}. */ - mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG); - mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG, - target.id, /* arg2= */ 0), USER_JOURNEY_TIMEOUT_MS); - } - - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId, - journey, origin != null ? origin.id : -1, - target.id, UserManager.getUserTypeForStatsd(target.userType), target.flags); - } - - /** - * statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd - * atom. - */ - private void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event, - @UserLifecycleEventState int eventState) { - final long sessionId; - synchronized (mUserIdToUserJourneyMap) { - final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId); - if (userJourneySession == null || userJourneySession.mSessionId == INVALID_SESSION_ID) { - Slogf.w(TAG, "UserLifecycleEvent " + event - + " received without an active userJourneySession."); - return; - } - sessionId = userJourneySession.mSessionId; - } - - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, eventState); - } - - /** - * Clears the {@link UserJourneySession} for a given {@link UserIdInt} and {@link UserJourney}. - */ - private void clearSessionId(@UserIdInt int userId, @UserJourney int journey) { - synchronized (mUserIdToUserJourneyMap) { - final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId); - if (userJourneySession != null && userJourneySession.mJourney == journey) { - clearSessionId(userId); - } + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, oldSession); } - } + UserJourneySession newSession = mInjector.getUserJourneyLogger() + .logUserJourneyBegin(targetId, journey); + mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG, + targetId, /* arg2= */ journey, newSession), USER_JOURNEY_TIMEOUT_MS); - /** - * Clears the {@link UserJourneySession} for a given {@link UserIdInt}. - */ - private void clearSessionId(@UserIdInt int userId) { - synchronized (mUserIdToUserJourneyMap) { - mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG); - mUserIdToUserJourneyMap.delete(userId); - } - } - - /** - * Log a final event of the {@link UserJourneySession} and clear it. - */ - private void logAndClearSessionId(@UserIdInt int userId) { - synchronized (mUserIdToUserJourneyMap) { - final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId); - if (userJourneySession != null) { - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, - userJourneySession.mSessionId, userId, USER_LIFECYCLE_EVENT_UNKNOWN, - USER_LIFECYCLE_EVENT_STATE_NONE); - } - clearSessionId(userId); - } } private BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( @@ -3471,23 +3321,6 @@ class UserController implements Handler.Callback { return mLastUserUnlockingUptime; } - /** - * Helper class to store user journey and session id. - * - * <p> User journey tracks a chain of user lifecycle events occurring during different user - * activities such as user start, user switch, and user creation. - */ - // TODO(b/157007231): Move this class and user journey tracking logic to a separate file. - private static class UserJourneySession { - final long mSessionId; - @UserJourney final int mJourney; - - UserJourneySession(long sessionId, @UserJourney int journey) { - mJourney = journey; - mSessionId = sessionId; - } - } - private static class UserProgressListener extends IProgressListener.Stub { private volatile long mUnlockStarted; @Override @@ -3562,6 +3395,10 @@ class UserController implements Handler.Callback { return new Handler(mService.mUiHandler.getLooper(), callback); } + protected UserJourneyLogger getUserJourneyLogger() { + return getUserManager().getUserJourneyLogger(); + } + protected Context getContext() { return mService.mContext; } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index ada92f5545c2..bc4e8df2a4ad 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -88,14 +88,14 @@ import java.util.concurrent.atomic.AtomicBoolean; private final @NonNull AudioSystemAdapter mAudioSystem; /** ID for Communication strategy retrieved form audio policy manager */ - private int mCommunicationStrategyId = -1; + /*package*/ int mCommunicationStrategyId = -1; /** ID for Accessibility strategy retrieved form audio policy manager */ private int mAccessibilityStrategyId = -1; /** Active communication device reported by audio policy manager */ - private AudioDeviceInfo mActiveCommunicationDevice; + /*package*/ AudioDeviceInfo mActiveCommunicationDevice; /** Last preferred device set for communication strategy */ private AudioDeviceAttributes mPreferredCommunicationDevice; @@ -755,6 +755,19 @@ import java.util.concurrent.atomic.AtomicBoolean; mIsLeOutput = false; } + BtDeviceInfo(@NonNull BtDeviceInfo src, int state) { + mDevice = src.mDevice; + mState = state; + mProfile = src.mProfile; + mSupprNoisy = src.mSupprNoisy; + mVolume = src.mVolume; + mIsLeOutput = src.mIsLeOutput; + mEventSource = src.mEventSource; + mAudioSystemDevice = src.mAudioSystemDevice; + mMusicDevice = src.mMusicDevice; + mCodec = src.mCodec; + } + // redefine equality op so we can match messages intended for this device @Override public boolean equals(Object o) { @@ -821,7 +834,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * @param info struct with the (dis)connection information */ /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) { - if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null + if (data.mPreviousDevice != null && data.mPreviousDevice.equals(data.mNewDevice)) { final String name = TextUtils.emptyIfNull(data.mNewDevice.getName()); new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR @@ -830,7 +843,8 @@ import java.util.concurrent.atomic.AtomicBoolean; .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) .record(); synchronized (mDeviceStateLock) { - postBluetoothA2dpDeviceConfigChange(data.mNewDevice); + postBluetoothDeviceConfigChange(createBtDeviceInfo(data, data.mNewDevice, + BluetoothProfile.STATE_CONNECTED)); } } else { synchronized (mDeviceStateLock) { @@ -1064,8 +1078,8 @@ import java.util.concurrent.atomic.AtomicBoolean; new AudioModeInfo(mode, pid, uid)); } - /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { - sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); + /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) { + sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info); } /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode, @@ -1092,21 +1106,21 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ int setPreferredDevicesForStrategySync(int strategy, @NonNull List<AudioDeviceAttributes> devices) { - return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices); + return mDeviceInventory.setPreferredDevicesForStrategyAndSave(strategy, devices); } /*package*/ int removePreferredDevicesForStrategySync(int strategy) { - return mDeviceInventory.removePreferredDevicesForStrategySync(strategy); + return mDeviceInventory.removePreferredDevicesForStrategyAndSave(strategy); } /*package*/ int setDeviceAsNonDefaultForStrategySync(int strategy, @NonNull AudioDeviceAttributes device) { - return mDeviceInventory.setDeviceAsNonDefaultForStrategySync(strategy, device); + return mDeviceInventory.setDeviceAsNonDefaultForStrategyAndSave(strategy, device); } /*package*/ int removeDeviceAsNonDefaultForStrategySync(int strategy, @NonNull AudioDeviceAttributes device) { - return mDeviceInventory.removeDeviceAsNonDefaultForStrategySync(strategy, device); + return mDeviceInventory.removeDeviceAsNonDefaultForStrategyAndSave(strategy, device); } /*package*/ void registerStrategyPreferredDevicesDispatcher( @@ -1131,11 +1145,11 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { - return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices); + return mDeviceInventory.setPreferredDevicesForCapturePresetAndSave(capturePreset, devices); } /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) { - return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset); + return mDeviceInventory.clearPreferredDevicesForCapturePresetAndSave(capturePreset); } /*package*/ void registerCapturePresetDevicesRoleDispatcher( @@ -1322,6 +1336,10 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state); } + /*package*/ void postNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) { + sendLMsgNoDelay(MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED, SENDMSG_QUEUE, btDevice); + } + /*package*/ static final class CommunicationDeviceInfo { final @NonNull IBinder mCb; // Identifies the requesting client for death handler final int mPid; // Requester process ID @@ -1397,9 +1415,11 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, + boolean connect, @Nullable BluetoothDevice btDevice) { synchronized (mDeviceStateLock) { - return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/); + return mDeviceInventory.handleDeviceConnection( + attributes, connect, false /*for test*/, btDevice); } } @@ -1640,13 +1660,10 @@ import java.util.concurrent.atomic.AtomicBoolean; (String) msg.obj, msg.arg1); } break; - case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; + case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: synchronized (mDeviceStateLock) { - final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice); - mDeviceInventory.onBluetoothA2dpDeviceConfigChange( - new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), - BtHelper.EVENT_DEVICE_CONFIG_CHANGE); + mDeviceInventory.onBluetoothDeviceConfigChange( + (BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE); } break; case MSG_BROADCAST_AUDIO_BECOMING_NOISY: @@ -1810,6 +1827,10 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_IL_SET_LEAUDIO_SUSPENDED: { setLeAudioSuspended((msg.arg1 == 1), false /*internal*/, (String) msg.obj); } break; + case MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED: { + final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; + BtHelper.onNotifyPreferredAudioProfileApplied(btDevice); + } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1845,7 +1866,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; // process change of A2DP device configuration, obj is BluetoothDevice - private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11; + private static final int MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE = 11; private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12; private static final int MSG_REPORT_NEW_ROUTES = 13; @@ -1887,13 +1908,15 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SET_A2DP_SUSPENDED = 50; private static final int MSG_IL_SET_LEAUDIO_SUSPENDED = 51; + private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52; + private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_IL_BTLEAUDIO_TIMEOUT: - case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: + case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: @@ -1985,7 +2008,7 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_IL_BTLEAUDIO_TIMEOUT: - case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: + case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: if (sLastDeviceConnectMsgTime >= time) { // add a little delay to make sure messages are ordered as expected time = sLastDeviceConnectMsgTime + 30; @@ -2005,7 +2028,7 @@ import java.util.concurrent.atomic.AtomicBoolean; static { MESSAGES_MUTE_MUSIC = new HashSet<>(); MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE); + MESSAGES_MUTE_MUSIC.add(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE); MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT); MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE); } @@ -2026,7 +2049,7 @@ import java.util.concurrent.atomic.AtomicBoolean; // Do not mute on bluetooth event if music is playing on a wired headset. if ((message == MSG_L_SET_BT_ACTIVE_DEVICE || message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT - || message == MSG_L_A2DP_DEVICE_CONFIG_CHANGE) + || message == MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE) && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) && hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET, mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) { @@ -2165,18 +2188,19 @@ import java.util.concurrent.atomic.AtomicBoolean; if (preferredCommunicationDevice == null) { AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice(); if (defaultDevice != null) { - setPreferredDevicesForStrategySync( + mDeviceInventory.setPreferredDevicesForStrategy( mCommunicationStrategyId, Arrays.asList(defaultDevice)); - setPreferredDevicesForStrategySync( + mDeviceInventory.setPreferredDevicesForStrategy( mAccessibilityStrategyId, Arrays.asList(defaultDevice)); } else { - removePreferredDevicesForStrategySync(mCommunicationStrategyId); - removePreferredDevicesForStrategySync(mAccessibilityStrategyId); + mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId); + mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId); } + mDeviceInventory.applyConnectedDevicesRoles(); } else { - setPreferredDevicesForStrategySync( + mDeviceInventory.setPreferredDevicesForStrategy( mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice)); - setPreferredDevicesForStrategySync( + mDeviceInventory.setPreferredDevicesForStrategy( mAccessibilityStrategyId, Arrays.asList(preferredCommunicationDevice)); } onUpdatePhoneStrategyDevice(preferredCommunicationDevice); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 228bc87cc20b..773df3720ed3 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -34,26 +34,36 @@ import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.IStrategyNonDefaultDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; +import android.media.MediaRecorder.AudioSource; +import android.media.audiopolicy.AudioProductStrategy; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.SafeCloseable; import android.os.Binder; +import android.os.Bundle; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.utils.EventLogger; +import com.google.android.collect.Sets; + import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -175,18 +185,26 @@ public class AudioDeviceInventory { final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers = new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>(); + final List<AudioProductStrategy> mStrategies; + /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { - mDeviceBroker = broker; - mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); + this(broker, AudioSystemAdapter.getDefaultAdapter()); } //----------------------------------------------------------- /** for mocking only, allows to inject AudioSystem adapter */ /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) { - mDeviceBroker = null; - mAudioSystem = audioSystem; + this(null, audioSystem); } + private AudioDeviceInventory(@Nullable AudioDeviceBroker broker, + @Nullable AudioSystemAdapter audioSystem) { + mDeviceBroker = broker; + mAudioSystem = audioSystem; + mStrategies = AudioProductStrategy.getAudioProductStrategies(); + mBluetoothDualModeEnabled = SystemProperties.getBoolean( + "persist.bluetooth.enable_dual_mode_audio", false); + } /*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; } @@ -203,8 +221,13 @@ public class AudioDeviceInventory { int mDeviceCodecFormat; final UUID mSensorUuid; + /** Disabled operating modes for this device. Use a negative logic so that by default + * an empty list means all modes are allowed. + * See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */ + @NonNull ArraySet<String> mDisabledModes = new ArraySet(0); + DeviceInfo(int deviceType, String deviceName, String deviceAddress, - int deviceCodecFormat, UUID sensorUuid) { + int deviceCodecFormat, @Nullable UUID sensorUuid) { mDeviceType = deviceType; mDeviceName = deviceName == null ? "" : deviceName; mDeviceAddress = deviceAddress == null ? "" : deviceAddress; @@ -212,11 +235,31 @@ public class AudioDeviceInventory { mSensorUuid = sensorUuid; } + void setModeDisabled(String mode) { + mDisabledModes.add(mode); + } + void setModeEnabled(String mode) { + mDisabledModes.remove(mode); + } + boolean isModeEnabled(String mode) { + return !mDisabledModes.contains(mode); + } + boolean isOutputOnlyModeEnabled() { + return isModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); + } + boolean isDuplexModeEnabled() { + return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); + } + DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) { this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null); } + DeviceInfo(int deviceType, String deviceName, String deviceAddress) { + this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT); + } + @Override public String toString() { return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType) @@ -224,7 +267,8 @@ public class AudioDeviceInventory { + ") name:" + mDeviceName + " addr:" + mDeviceAddress + " codec: " + Integer.toHexString(mDeviceCodecFormat) - + " sensorUuid: " + Objects.toString(mSensorUuid) + "]"; + + " sensorUuid: " + Objects.toString(mSensorUuid) + + " disabled modes: " + mDisabledModes + "]"; } @NonNull String getKey() { @@ -276,9 +320,18 @@ public class AudioDeviceInventory { pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) + " (" + AudioSystem.getDeviceName(keyType) + ") addr:" + valueAddress); }); + pw.println("\n" + prefix + "Preferred devices for capture preset:"); mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { pw.println(" " + prefix + "capturePreset:" + capturePreset + " devices:" + devices); }); + pw.println("\n" + prefix + "Applied devices roles for strategies:"); + mAppliedStrategyRoles.forEach((key, devices) -> { + pw.println(" " + prefix + "strategy: " + key.first + + " role:" + key.second + " devices:" + devices); }); + pw.println("\n" + prefix + "Applied devices roles for presets:"); + mAppliedPresetRoles.forEach((key, devices) -> { + pw.println(" " + prefix + "preset: " + key.first + + " role:" + key.second + " devices:" + devices); }); } //------------------------------------------------------------ @@ -299,15 +352,16 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } + mAppliedStrategyRoles.clear(); + applyConnectedDevicesRoles_l(); } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, devices) -> { - mAudioSystem.setDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); }); + setPreferredDevicesForStrategy(strategy, devices); }); } synchronized (mNonDefaultDevices) { mNonDefaultDevices.forEach((strategy, devices) -> { - mAudioSystem.setDevicesRoleForStrategy( + addDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); }); } synchronized (mPreferredDevicesForCapturePreset) { @@ -380,8 +434,7 @@ public class AudioDeviceInventory { btInfo.mVolume * 10, btInfo.mAudioSystemDevice, "onSetBtActiveDevice"); } - makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), - "onSetBtActiveDevice", btInfo.mCodec); + makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice"); } break; case BluetoothProfile.HEARING_AID: @@ -397,10 +450,7 @@ public class AudioDeviceInventory { if (switchToUnavailable) { makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice); } else if (switchToAvailable) { - makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), - streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10, - btInfo.mAudioSystemDevice, - "onSetBtActiveDevice"); + makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice"); } break; default: throw new IllegalArgumentException("Invalid profile " @@ -411,30 +461,30 @@ public class AudioDeviceInventory { @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothA2dpDeviceConfigChange( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { + /*package*/ void onBluetoothDeviceConfigChange( + @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) { MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId - + "onBluetoothA2dpDeviceConfigChange") - .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); + + "onBluetoothDeviceConfigChange") + .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event)); - final BluetoothDevice btDevice = btInfo.getBtDevice(); + final BluetoothDevice btDevice = btInfo.mDevice; if (btDevice == null) { mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record(); return; } if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice); + Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice); } - int a2dpVolume = btInfo.getVolume(); - @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec(); + int volume = btInfo.mVolume; + @AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec; String address = btDevice.getAddress(); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "onBluetoothA2dpDeviceConfigChange addr=" + address - + " event=" + BtHelper.a2dpDeviceEventToString(event))); + "onBluetoothDeviceConfigChange addr=" + address + + " event=" + BtHelper.deviceEventToString(event))); synchronized (mDevicesLock) { if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) { @@ -449,53 +499,53 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { - Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange"); + Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange"); mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } mmi.set(MediaMetrics.Property.ADDRESS, address) .set(MediaMetrics.Property.ENCODING, - AudioSystem.audioFormatToString(a2dpCodec)) - .set(MediaMetrics.Property.INDEX, a2dpVolume) + AudioSystem.audioFormatToString(audioCodec)) + .set(MediaMetrics.Property.INDEX, volume) .set(MediaMetrics.Property.NAME, di.mDeviceName); - if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) { - // Device is connected - if (a2dpVolume != -1) { - mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, - // convert index to internal representation in VolumeStreamState - a2dpVolume * 10, - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - "onBluetoothA2dpDeviceConfigChange"); - } - } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { - if (di.mDeviceCodecFormat != a2dpCodec) { - di.mDeviceCodecFormat = a2dpCodec; - mConnectedDevices.replace(key, di); - } - } - final int res = mAudioSystem.handleDeviceConfigChange( - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, - BtHelper.getName(btDevice), a2dpCodec); - if (res != AudioSystem.AUDIO_STATUS_OK) { - AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "APM handleDeviceConfigChange failed for A2DP device addr=" + address - + " codec=" + AudioSystem.audioFormatToString(a2dpCodec)) - .printLog(TAG)); + if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { + boolean a2dpCodecChange = false; + if (btInfo.mProfile == BluetoothProfile.A2DP) { + if (di.mDeviceCodecFormat != audioCodec) { + di.mDeviceCodecFormat = audioCodec; + mConnectedDevices.replace(key, di); + a2dpCodecChange = true; + } + final int res = mAudioSystem.handleDeviceConfigChange( + btInfo.mAudioSystemDevice, address, + BtHelper.getName(btDevice), audioCodec); + + if (res != AudioSystem.AUDIO_STATUS_OK) { + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "APM handleDeviceConfigChange failed for A2DP device addr=" + + address + " codec=" + + AudioSystem.audioFormatToString(audioCodec)) + .printLog(TAG)); + + // force A2DP device disconnection in case of error so that AudioService + // state is consistent with audio policy manager state + setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo, + BluetoothProfile.STATE_DISCONNECTED)); + } else { + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "APM handleDeviceConfigChange success for A2DP device addr=" + + address + + " codec=" + AudioSystem.audioFormatToString(audioCodec)) + .printLog(TAG)); - int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); - // force A2DP device disconnection in case of error so that AudioService state is - // consistent with audio policy manager state - setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice, - BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED, - musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); - } else { - AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "APM handleDeviceConfigChange success for A2DP device addr=" + address - + " codec=" + AudioSystem.audioFormatToString(a2dpCodec)) - .printLog(TAG)); + } + } + if (!a2dpCodecChange) { + updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/); + } } } mmi.record(); @@ -578,7 +628,7 @@ public class AudioDeviceInventory { } if (!handleDeviceConnection(wdcs.mAttributes, - wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) { + wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) { // change of connection state failed, bailout mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") .record(); @@ -710,43 +760,51 @@ public class AudioDeviceInventory { //------------------------------------------------------------ // preferred/non-default device(s) - /*package*/ int setPreferredDevicesForStrategySync(int strategy, + /*package*/ int setPreferredDevicesForStrategyAndSave(int strategy, @NonNull List<AudioDeviceAttributes> devices) { - int status = AudioSystem.ERROR; + final int status = setPreferredDevicesForStrategy(strategy, devices); + if (status == AudioSystem.SUCCESS) { + mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices); + } + return status; + } + /*package*/ int setPreferredDevicesForStrategy(int strategy, + @NonNull List<AudioDeviceAttributes> devices) { + int status = AudioSystem.ERROR; try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( - "setPreferredDevicesForStrategySync, strategy: " + strategy + "setPreferredDevicesForStrategy, strategy: " + strategy + " devices: " + devices)).printLog(TAG)); - status = mAudioSystem.setDevicesRoleForStrategy( + status = setDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); } + return status; + } + /*package*/ int removePreferredDevicesForStrategyAndSave(int strategy) { + final int status = removePreferredDevicesForStrategy(strategy); if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices); + mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy); } return status; } - /*package*/ int removePreferredDevicesForStrategySync(int strategy) { + /*package*/ int removePreferredDevicesForStrategy(int strategy) { int status = AudioSystem.ERROR; try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( - "removePreferredDevicesForStrategySync, strategy: " + "removePreferredDevicesForStrategy, strategy: " + strategy)).printLog(TAG)); - status = mAudioSystem.clearDevicesRoleForStrategy( + status = clearDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_PREFERRED); } - - if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy); - } return status; } - /*package*/ int setDeviceAsNonDefaultForStrategySync(int strategy, + /*package*/ int setDeviceAsNonDefaultForStrategyAndSave(int strategy, @NonNull AudioDeviceAttributes device) { int status = AudioSystem.ERROR; @@ -755,9 +813,9 @@ public class AudioDeviceInventory { devices.add(device); AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( - "setDeviceAsNonDefaultForStrategySync, strategy: " + strategy + "setDeviceAsNonDefaultForStrategyAndSave, strategy: " + strategy + " device: " + device)).printLog(TAG)); - status = mAudioSystem.setDevicesRoleForStrategy( + status = addDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); } @@ -767,7 +825,7 @@ public class AudioDeviceInventory { return status; } - /*package*/ int removeDeviceAsNonDefaultForStrategySync(int strategy, + /*package*/ int removeDeviceAsNonDefaultForStrategyAndSave(int strategy, @NonNull AudioDeviceAttributes device) { int status = AudioSystem.ERROR; @@ -776,10 +834,10 @@ public class AudioDeviceInventory { devices.add(device); AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( - "removeDeviceAsNonDefaultForStrategySync, strategy: " + "removeDeviceAsNonDefaultForStrategyAndSave, strategy: " + strategy + " devices: " + device)).printLog(TAG)); - status = mAudioSystem.removeDevicesRoleForStrategy( + status = removeDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); } @@ -810,35 +868,72 @@ public class AudioDeviceInventory { mNonDefDevDispatchers.unregister(dispatcher); } - /*package*/ int setPreferredDevicesForCapturePresetSync( + /*package*/ int setPreferredDevicesForCapturePresetAndSave( int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { - int status = AudioSystem.ERROR; + final int status = setPreferredDevicesForCapturePreset(capturePreset, devices); + if (status == AudioSystem.SUCCESS) { + mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices); + } + return status; + } + private int setPreferredDevicesForCapturePreset( + int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { + int status = AudioSystem.ERROR; try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { - status = mAudioSystem.setDevicesRoleForCapturePreset( + status = setDevicesRoleForCapturePreset( capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); } + return status; + } + /*package*/ int clearPreferredDevicesForCapturePresetAndSave(int capturePreset) { + final int status = clearPreferredDevicesForCapturePreset(capturePreset); if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices); + mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset); } return status; } - /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) { + private int clearPreferredDevicesForCapturePreset(int capturePreset) { int status = AudioSystem.ERROR; try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { - status = mAudioSystem.clearDevicesRoleForCapturePreset( + status = clearDevicesRoleForCapturePreset( capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED); } - - if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset); - } return status; } + private int addDevicesRoleForCapturePreset(int capturePreset, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return addDevicesRole(mAppliedPresetRoles, (p, r, d) -> { + return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); + }, capturePreset, role, devices); + } + + private int removeDevicesRoleForCapturePreset(int capturePreset, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return removeDevicesRole(mAppliedPresetRoles, (p, r, d) -> { + return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); + }, capturePreset, role, devices); + } + + private int setDevicesRoleForCapturePreset(int capturePreset, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> { + return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); + }, (p, r, d) -> { + return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); + }, capturePreset, role, devices); + } + + private int clearDevicesRoleForCapturePreset(int capturePreset, int role) { + return clearDevicesRole(mAppliedPresetRoles, (p, r, d) -> { + return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); + }, capturePreset, role); + } + /*package*/ void registerCapturePresetDevicesRoleDispatcher( @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { mDevRoleCapturePresetDispatchers.register(dispatcher); @@ -849,7 +944,208 @@ public class AudioDeviceInventory { mDevRoleCapturePresetDispatchers.unregister(dispatcher); } - //----------------------------------------------------------------------- + private int addDevicesRoleForStrategy(int strategy, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return addDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { + return mAudioSystem.setDevicesRoleForStrategy(s, r, d); + }, strategy, role, devices); + } + + private int removeDevicesRoleForStrategy(int strategy, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return removeDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { + return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); + }, strategy, role, devices); + } + + private int setDevicesRoleForStrategy(int strategy, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return setDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { + return mAudioSystem.setDevicesRoleForStrategy(s, r, d); + }, (s, r, d) -> { + return mAudioSystem.clearDevicesRoleForStrategy(s, r); + }, strategy, role, devices); + } + + private int clearDevicesRoleForStrategy(int strategy, int role) { + return clearDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { + return mAudioSystem.clearDevicesRoleForStrategy(s, r); + }, strategy, role); + } + + //------------------------------------------------------------ + // Cache for applied roles for strategies and devices. The cache avoids reapplying the + // same list of devices for a given role and strategy and the corresponding systematic + // redundant work in audio policy manager and audio flinger. + // The key is the pair <Strategy , Role> and the value is the current list of devices. + + private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> + mAppliedStrategyRoles = new ArrayMap<>(); + + // Cache for applied roles for capture presets and devices. The cache avoids reapplying the + // same list of devices for a given role and capture preset and the corresponding systematic + // redundant work in audio policy manager and audio flinger. + // The key is the pair <Preset , Role> and the value is the current list of devices. + private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> + mAppliedPresetRoles = new ArrayMap<>(); + + interface AudioSystemInterface { + int deviceRoleAction(int usecase, int role, @Nullable List<AudioDeviceAttributes> devices); + } + + private int addDevicesRole( + ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, + AudioSystemInterface asi, + int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) { + synchronized (rolesMap) { + Pair<Integer, Integer> key = new Pair<>(useCase, role); + List<AudioDeviceAttributes> roleDevices = new ArrayList<>(); + List<AudioDeviceAttributes> appliedDevices = new ArrayList<>(); + + if (rolesMap.containsKey(key)) { + roleDevices = rolesMap.get(key); + for (AudioDeviceAttributes device : devices) { + if (!roleDevices.contains(device)) { + appliedDevices.add(device); + } + } + } else { + appliedDevices.addAll(devices); + } + if (appliedDevices.isEmpty()) { + return AudioSystem.SUCCESS; + } + final int status = asi.deviceRoleAction(useCase, role, appliedDevices); + if (status == AudioSystem.SUCCESS) { + roleDevices.addAll(appliedDevices); + rolesMap.put(key, roleDevices); + } + return status; + } + } + + private int removeDevicesRole( + ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, + AudioSystemInterface asi, + int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) { + synchronized (rolesMap) { + Pair<Integer, Integer> key = new Pair<>(useCase, role); + if (!rolesMap.containsKey(key)) { + return AudioSystem.SUCCESS; + } + List<AudioDeviceAttributes> roleDevices = rolesMap.get(key); + List<AudioDeviceAttributes> appliedDevices = new ArrayList<>(); + for (AudioDeviceAttributes device : devices) { + if (roleDevices.contains(device)) { + appliedDevices.add(device); + } + } + if (appliedDevices.isEmpty()) { + return AudioSystem.SUCCESS; + } + final int status = asi.deviceRoleAction(useCase, role, appliedDevices); + if (status == AudioSystem.SUCCESS) { + roleDevices.removeAll(appliedDevices); + if (roleDevices.isEmpty()) { + rolesMap.remove(key); + } else { + rolesMap.put(key, roleDevices); + } + } + return status; + } + } + + private int setDevicesRole( + ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, + AudioSystemInterface addOp, + AudioSystemInterface clearOp, + int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) { + synchronized (rolesMap) { + Pair<Integer, Integer> key = new Pair<>(useCase, role); + List<AudioDeviceAttributes> roleDevices = new ArrayList<>(); + List<AudioDeviceAttributes> appliedDevices = new ArrayList<>(); + + if (rolesMap.containsKey(key)) { + roleDevices = rolesMap.get(key); + boolean equal = false; + if (roleDevices.size() == devices.size()) { + roleDevices.retainAll(devices); + equal = roleDevices.size() == devices.size(); + } + if (!equal) { + clearOp.deviceRoleAction(useCase, role, null); + roleDevices.clear(); + appliedDevices.addAll(devices); + } + } else { + appliedDevices.addAll(devices); + } + if (appliedDevices.isEmpty()) { + return AudioSystem.SUCCESS; + } + final int status = addOp.deviceRoleAction(useCase, role, appliedDevices); + if (status == AudioSystem.SUCCESS) { + roleDevices.addAll(appliedDevices); + rolesMap.put(key, roleDevices); + } + return status; + } + } + + private int clearDevicesRole( + ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, + AudioSystemInterface asi, int useCase, int role) { + synchronized (rolesMap) { + Pair<Integer, Integer> key = new Pair<>(useCase, role); + if (!rolesMap.containsKey(key)) { + return AudioSystem.SUCCESS; + } + final int status = asi.deviceRoleAction(useCase, role, null); + if (status == AudioSystem.SUCCESS) { + rolesMap.remove(key); + } + return status; + } + } + + @GuardedBy("mDevicesLock") + private void purgeDevicesRoles_l() { + purgeRoles(mAppliedStrategyRoles, (s, r, d) -> { + return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); }); + purgeRoles(mAppliedPresetRoles, (p, r, d) -> { + return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); }); + } + + @GuardedBy("mDevicesLock") + private void purgeRoles( + ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, + AudioSystemInterface asi) { + synchronized (rolesMap) { + Iterator<Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole = + rolesMap.entrySet().iterator(); + while (itRole.hasNext()) { + Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry = + itRole.next(); + Pair<Integer, Integer> keyRole = entry.getKey(); + Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator(); + while (itDev.hasNext()) { + AudioDeviceAttributes ada = itDev.next(); + final String devKey = DeviceInfo.makeDeviceListKey(ada.getInternalType(), + ada.getAddress()); + if (mConnectedDevices.get(devKey) == null) { + asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada)); + itDev.remove(); + } + } + if (rolesMap.get(keyRole).isEmpty()) { + itRole.remove(); + } + } + } + } + +//----------------------------------------------------------------------- /** * Check if a device is in the list of connected devices @@ -871,10 +1167,11 @@ public class AudioDeviceInventory { * @param connect true if connection * @param isForTesting if true, not calling AudioSystem for the connection as this is * just for testing + * @param btDevice the corresponding Bluetooth device when relevant. * @return false if an error was reported by AudioSystem */ /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect, - boolean isForTesting) { + boolean isForTesting, @Nullable BluetoothDevice btDevice) { int device = attributes.getInternalType(); String address = attributes.getAddress(); String deviceName = attributes.getName(); @@ -889,6 +1186,7 @@ public class AudioDeviceInventory { .set(MediaMetrics.Property.MODE, connect ? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT) .set(MediaMetrics.Property.NAME, deviceName); + boolean status = false; synchronized (mDevicesLock) { final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); if (AudioService.DEBUG_DEVICES) { @@ -916,24 +1214,31 @@ public class AudioDeviceInventory { .record(); return false; } - mConnectedDevices.put(deviceKey, new DeviceInfo( - device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); + mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); - mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); - return true; + status = true; } else if (!connect && isConnected) { mAudioSystem.setDeviceConnectionState(attributes, AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); + status = true; + } + if (status) { + if (AudioSystem.isBluetoothScoDevice(device)) { + updateBluetoothPreferredModes_l(connect ? btDevice : null /*connectedDevice*/); + if (!connect) { + purgeDevicesRoles_l(); + } + } mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); - return true; + } else { + Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey + + ", deviceSpec=" + di + ", connect=" + connect); + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); } - Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey - + ", deviceSpec=" + di + ", connect=" + connect); } - mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); - return false; + return status; } @@ -1142,15 +1447,20 @@ public class AudioDeviceInventory { // Internal utilities @GuardedBy("mDevicesLock") - private void makeA2dpDeviceAvailable(String address, String name, String eventSource, - int a2dpCodec) { + private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo, + String eventSource) { + final String address = btInfo.mDevice.getAddress(); + final String name = BtHelper.getName(btInfo.mDevice); + final int a2dpCodec = btInfo.mCodec; + // enable A2DP before notifying A2DP connection to avoid unnecessary processing in // audio policy manager mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource); // at this point there could be another A2DP device already connected in APM, but it // doesn't matter as this new one will overwrite the previous one - final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name), + AudioDeviceAttributes ada = new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name); + final int res = mAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec); // TODO: log in MediaMetrics once distinction between connection failure and @@ -1172,8 +1482,7 @@ public class AudioDeviceInventory { // The convention for head tracking sensors associated with A2DP devices is to // use a UUID derived from the MAC address as follows: // time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address - UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes( - new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address)); + UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec, sensorUuid); final String diKey = di.getKey(); @@ -1184,6 +1493,208 @@ public class AudioDeviceInventory { mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/); + + updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/); + } + + static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER, + AudioSource.VOICE_RECOGNITION, AudioSource.VOICE_COMMUNICATION, + AudioSource.UNPROCESSED, AudioSource.VOICE_PERFORMANCE, AudioSource.HOTWORD}; + + // reflects system property persist.bluetooth.enable_dual_mode_audio + final boolean mBluetoothDualModeEnabled; + /** + * Goes over all connected Bluetooth devices and set the audio policy device role to DISABLED + * or not according to their own and other devices modes. + * The top priority is given to LE devices, then SCO ,then A2DP. + */ + @GuardedBy("mDevicesLock") + private void applyConnectedDevicesRoles_l() { + if (!mBluetoothDualModeEnabled) { + return; + } + DeviceInfo leOutDevice = + getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET); + DeviceInfo leInDevice = + getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET); + DeviceInfo a2dpDevice = + getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET); + DeviceInfo scoOutDevice = + getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET); + DeviceInfo scoInDevice = + getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET); + boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled()); + boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled()) + || (leInDevice != null && leInDevice.isDuplexModeEnabled()); + AudioDeviceAttributes communicationDevice = + mDeviceBroker.mActiveCommunicationDevice == null + ? null : ((mDeviceBroker.isInCommunication() + && mDeviceBroker.mActiveCommunicationDevice != null) + ? new AudioDeviceAttributes(mDeviceBroker.mActiveCommunicationDevice) + : null); + + if (AudioService.DEBUG_DEVICES) { + Log.i(TAG, "applyConnectedDevicesRoles_l\n - leOutDevice: " + leOutDevice + + "\n - leInDevice: " + leInDevice + + "\n - a2dpDevice: " + a2dpDevice + + "\n - scoOutDevice: " + scoOutDevice + + "\n - scoInDevice: " + scoInDevice + + "\n - disableA2dp: " + disableA2dp + + ", disableSco: " + disableSco); + } + + for (DeviceInfo di : mConnectedDevices.values()) { + if (!AudioSystem.isBluetoothDevice(di.mDeviceType)) { + continue; + } + AudioDeviceAttributes ada = + new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, di.mDeviceName); + if (AudioService.DEBUG_DEVICES) { + Log.i(TAG, " + checking Device: " + ada); + } + if (ada.equalTypeAddress(communicationDevice)) { + continue; + } + + if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) { + for (AudioProductStrategy strategy : mStrategies) { + boolean disable = false; + if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) { + if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { + disable = disableSco || !di.isDuplexModeEnabled(); + } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { + disable = !di.isDuplexModeEnabled(); + } + } else { + if (AudioSystem.isBluetoothA2dpOutDevice(di.mDeviceType)) { + disable = disableA2dp || !di.isOutputOnlyModeEnabled(); + } else if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { + disable = disableSco || !di.isOutputOnlyModeEnabled(); + } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { + disable = !di.isOutputOnlyModeEnabled(); + } + } + if (AudioService.DEBUG_DEVICES) { + Log.i(TAG, " - strategy: " + strategy.getId() + + ", disable: " + disable); + } + if (disable) { + addDevicesRoleForStrategy(strategy.getId(), + AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); + } else { + removeDevicesRoleForStrategy(strategy.getId(), + AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); + } + } + } + if (AudioSystem.isBluetoothInDevice(di.mDeviceType)) { + for (int capturePreset : CAPTURE_PRESETS) { + boolean disable = false; + if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { + disable = disableSco || !di.isDuplexModeEnabled(); + } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { + disable = !di.isDuplexModeEnabled(); + } + if (AudioService.DEBUG_DEVICES) { + Log.i(TAG, " - capturePreset: " + capturePreset + + ", disable: " + disable); + } + if (disable) { + addDevicesRoleForCapturePreset(capturePreset, + AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); + } else { + removeDevicesRoleForCapturePreset(capturePreset, + AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); + } + } + } + } + } + + /* package */ void applyConnectedDevicesRoles() { + synchronized (mDevicesLock) { + applyConnectedDevicesRoles_l(); + } + } + + @GuardedBy("mDevicesLock") + int checkProfileIsConnected(int profile) { + switch (profile) { + case BluetoothProfile.HEADSET: + if (getFirstConnectedDeviceOfTypes( + AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null + || getFirstConnectedDeviceOfTypes( + AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) { + return profile; + } + break; + case BluetoothProfile.A2DP: + if (getFirstConnectedDeviceOfTypes( + AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) { + return profile; + } + break; + case BluetoothProfile.LE_AUDIO: + case BluetoothProfile.LE_AUDIO_BROADCAST: + if (getFirstConnectedDeviceOfTypes( + AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null + || getFirstConnectedDeviceOfTypes( + AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) { + return profile; + } + break; + default: + break; + } + return 0; + } + + @GuardedBy("mDevicesLock") + private void updateBluetoothPreferredModes_l(BluetoothDevice connectedDevice) { + if (!mBluetoothDualModeEnabled) { + return; + } + HashSet<String> processedAddresses = new HashSet<>(0); + for (DeviceInfo di : mConnectedDevices.values()) { + if (!AudioSystem.isBluetoothDevice(di.mDeviceType) + || processedAddresses.contains(di.mDeviceAddress)) { + continue; + } + Bundle preferredProfiles = BtHelper.getPreferredAudioProfiles(di.mDeviceAddress); + if (AudioService.DEBUG_DEVICES) { + Log.i(TAG, "updateBluetoothPreferredModes_l processing device address: " + + di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles); + } + for (DeviceInfo di2 : mConnectedDevices.values()) { + if (!AudioSystem.isBluetoothDevice(di2.mDeviceType) + || !di.mDeviceAddress.equals(di2.mDeviceAddress)) { + continue; + } + int profile = BtHelper.getProfileFromType(di2.mDeviceType); + if (profile == 0) { + continue; + } + int preferredProfile = checkProfileIsConnected( + preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX)); + if (preferredProfile == profile || preferredProfile == 0) { + di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); + } else { + di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); + } + preferredProfile = checkProfileIsConnected( + preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY)); + if (preferredProfile == profile || preferredProfile == 0) { + di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); + } else { + di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); + } + } + processedAddresses.add(di.mDeviceAddress); + } + applyConnectedDevicesRoles_l(); + if (connectedDevice != null) { + mDeviceBroker.postNotifyPreferredAudioProfileApplied(connectedDevice); + } } @GuardedBy("mDevicesLock") @@ -1231,6 +1742,8 @@ public class AudioDeviceInventory { // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/); mmi.record(); + updateBluetoothPreferredModes_l(null /*connectedDevice*/); + purgeDevicesRoles_l(); } @GuardedBy("mDevicesLock") @@ -1260,8 +1773,7 @@ public class AudioDeviceInventory { AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), - new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", - address, AudioSystem.AUDIO_FORMAT_DEFAULT)); + new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address)); } @GuardedBy("mDevicesLock") @@ -1287,8 +1799,7 @@ public class AudioDeviceInventory { AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address), - new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, - address, AudioSystem.AUDIO_FORMAT_DEFAULT)); + new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address)); mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postApplyVolumeOnDevice(streamType, AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); @@ -1326,29 +1837,56 @@ public class AudioDeviceInventory { * @return true if a DEVICE_OUT_HEARING_AID is connected, false otherwise. */ boolean isHearingAidConnected() { + return getFirstConnectedDeviceOfTypes( + Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null; + } + + /** + * Returns a DeviceInfo for the first connected device matching one of the supplied types + */ + private DeviceInfo getFirstConnectedDeviceOfTypes(Set<Integer> internalTypes) { + List<DeviceInfo> devices = getConnectedDevicesOfTypes(internalTypes); + return devices.isEmpty() ? null : devices.get(0); + } + + /** + * Returns a list of connected devices matching one of the supplied types + */ + private List<DeviceInfo> getConnectedDevicesOfTypes(Set<Integer> internalTypes) { + ArrayList<DeviceInfo> devices = new ArrayList<>(); synchronized (mDevicesLock) { for (DeviceInfo di : mConnectedDevices.values()) { - if (di.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) { - return true; + if (internalTypes.contains(di.mDeviceType)) { + devices.add(di); } } - return false; } + return devices; + } + + /* package */ AudioDeviceAttributes getDeviceOfType(int type) { + DeviceInfo di = getFirstConnectedDeviceOfTypes(Sets.newHashSet(type)); + return di == null ? null : new AudioDeviceAttributes( + di.mDeviceType, di.mDeviceAddress, di.mDeviceName); } @GuardedBy("mDevicesLock") - private void makeLeAudioDeviceAvailable(String address, String name, int streamType, - int volumeIndex, int device, String eventSource) { + private void makeLeAudioDeviceAvailable( + AudioDeviceBroker.BtDeviceInfo btInfo, int streamType, String eventSource) { + final String address = btInfo.mDevice.getAddress(); + final String name = BtHelper.getName(btInfo.mDevice); + final int volumeIndex = btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10; + final int device = btInfo.mAudioSystemDevice; + if (device != AudioSystem.DEVICE_NONE) { /* Audio Policy sees Le Audio similar to A2DP. Let's make sure * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set */ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); - final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( - device, address, name), - AudioSystem.DEVICE_STATE_AVAILABLE, - AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name); + final int res = AudioSystem.setDeviceConnectionState(ada, + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( "APM failed to make available LE Audio device addr=" + address @@ -1359,12 +1897,13 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( "LE Audio device addr=" + address + " now available").printLog(TAG)); } - // Reset LEA suspend state each time a new sink is connected mDeviceBroker.clearLeAudioSuspended(); + UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), - new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); + new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT, + sensorUuid)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false); } @@ -1380,6 +1919,8 @@ public class AudioDeviceInventory { final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType); mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType); mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable"); + + updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/); } @GuardedBy("mDevicesLock") @@ -1404,6 +1945,8 @@ public class AudioDeviceInventory { } setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/); + updateBluetoothPreferredModes_l(null /*connectedDevice*/); + purgeDevicesRoles_l(); } @GuardedBy("mDevicesLock") @@ -1739,18 +2282,6 @@ public class AudioDeviceInventory { } } - /* package */ AudioDeviceAttributes getDeviceOfType(int type) { - synchronized (mDevicesLock) { - for (DeviceInfo di : mConnectedDevices.values()) { - if (di.mDeviceType == type) { - return new AudioDeviceAttributes( - di.mDeviceType, di.mDeviceAddress, di.mDeviceName); - } - } - } - return null; - } - //---------------------------------------------------------- // For tests only @@ -1761,10 +2292,12 @@ public class AudioDeviceInventory { */ @VisibleForTesting public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - device.getAddress()); - synchronized (mDevicesLock) { - return (mConnectedDevices.get(key) != null); + for (DeviceInfo di : getConnectedDevicesOfTypes( + Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) { + if (di.mDeviceAddress.equals(device.getAddress())) { + return true; + } } + return false; } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index d066340c42b2..43438942a060 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -445,7 +445,7 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, } /** - * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, int[], String[])} + * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, List)} * @param capturePreset * @param role * @param devicesToRemove @@ -458,6 +458,19 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, } /** + * Same as {@link AudioSystem#addDevicesRoleForCapturePreset(int, int, List)} + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param devices the list of devices to be added as role for the given capture preset + * @return {@link #SUCCESS} if successfully added + */ + public int addDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { + invalidateRoutingCache(); + return AudioSystem.addDevicesRoleForCapturePreset(capturePreset, role, devices); + } + + /** * Same as {@link AudioSystem#} * @param capturePreset * @param role diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 8c27c3ecfd87..e46c3cc4a285 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -33,6 +33,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.BluetoothProfileConnectionInfo; import android.os.Binder; +import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -150,60 +151,12 @@ public class BtHelper { } } - //---------------------------------------------------------------------- - /*package*/ static class BluetoothA2dpDeviceInfo { - private final @NonNull BluetoothDevice mBtDevice; - private final int mVolume; - private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; - - BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) { - this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT); - } - - BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) { - mBtDevice = btDevice; - mVolume = volume; - mCodec = codec; - } - - public @NonNull BluetoothDevice getBtDevice() { - return mBtDevice; - } - - public int getVolume() { - return mVolume; - } - - public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() { - return mCodec; - } - - // redefine equality op so we can match messages intended for this device - @Override - public boolean equals(Object o) { - if (o == null) { - return false; - } - if (this == o) { - return true; - } - if (o instanceof BluetoothA2dpDeviceInfo) { - return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice()); - } - return false; - } - - - } - // A2DP device events /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0; - /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1; - /*package*/ static String a2dpDeviceEventToString(int event) { + /*package*/ static String deviceEventToString(int event) { switch (event) { case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE"; - case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE"; default: return new String("invalid event:" + event); } @@ -620,11 +573,12 @@ public class BtHelper { return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice); } - private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) { + private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) { if (btDevice == null) { return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""); } String address = btDevice.getAddress(); + String name = getName(btDevice); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } @@ -646,7 +600,7 @@ public class BtHelper { + " btClass: " + (btClass == null ? "Unknown" : btClass) + " nativeType: " + nativeType + " address: " + address); } - return new AudioDeviceAttributes(nativeType, address); + return new AudioDeviceAttributes(nativeType, address, name); } private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { @@ -655,12 +609,9 @@ public class BtHelper { } int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice); - String btDeviceName = getName(btDevice); boolean result = false; if (isActive) { - result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( - audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName), - isActive); + result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice); } else { int[] outDeviceTypes = { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, @@ -669,14 +620,14 @@ public class BtHelper { }; for (int outDeviceType : outDeviceTypes) { result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( - outDeviceType, audioDevice.getAddress(), btDeviceName), - isActive); + outDeviceType, audioDevice.getAddress(), audioDevice.getName()), + isActive, btDevice); } } // handleDeviceConnection() && result to make sure the method get executed result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( - inDevice, audioDevice.getAddress(), btDeviceName), - isActive) && result; + inDevice, audioDevice.getAddress(), audioDevice.getName()), + isActive, btDevice) && result; return result; } @@ -973,6 +924,30 @@ public class BtHelper { } } + /*package */ static int getProfileFromType(int deviceType) { + if (AudioSystem.isBluetoothA2dpOutDevice(deviceType)) { + return BluetoothProfile.A2DP; + } else if (AudioSystem.isBluetoothScoDevice(deviceType)) { + return BluetoothProfile.HEADSET; + } else if (AudioSystem.isBluetoothLeDevice(deviceType)) { + return BluetoothProfile.LE_AUDIO; + } + return 0; // 0 is not a valid profile + } + + /*package */ static Bundle getPreferredAudioProfiles(String address) { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address)); + } + + /** + * Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices + * have been applied. + */ + public static void onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) { + BluetoothAdapter.getDefaultAdapter().notifyActiveDeviceChangeApplied(btDevice); + } + /** * Returns the string equivalent for the btDeviceClass class. */ diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1dc2725feb1b..c678a92af13a 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -85,6 +85,7 @@ import android.net.NetworkScore; import android.net.NetworkSpecifier; import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; +import android.net.TransportInfo; import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.Uri; @@ -107,6 +108,8 @@ import android.net.ipsec.ike.exceptions.IkeNetworkLostException; import android.net.ipsec.ike.exceptions.IkeNonProtocolException; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.ipsec.ike.exceptions.IkeTimeoutException; +import android.net.vcn.VcnGatewayConnectionConfig; +import android.net.vcn.VcnTransportInfo; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -453,22 +456,22 @@ public class Vpn { private static class CarrierConfigInfo { public final String mccMnc; - public final int keepaliveDelayMs; + public final int keepaliveDelaySec; public final int encapType; public final int ipVersion; - CarrierConfigInfo(String mccMnc, int keepaliveDelayMs, + CarrierConfigInfo(String mccMnc, int keepaliveDelaySec, int encapType, int ipVersion) { this.mccMnc = mccMnc; - this.keepaliveDelayMs = keepaliveDelayMs; + this.keepaliveDelaySec = keepaliveDelaySec; this.encapType = encapType; this.ipVersion = ipVersion; } @Override public String toString() { - return "CarrierConfigInfo(" + mccMnc + ") [keepaliveDelayMs=" + keepaliveDelayMs + return "CarrierConfigInfo(" + mccMnc + ") [keepaliveDelaySec=" + keepaliveDelaySec + ", encapType=" + encapType + ", ipVersion=" + ipVersion + "]"; } } @@ -3556,39 +3559,63 @@ public class Vpn { } private int guessEspIpVersionForNetwork() { - final CarrierConfigInfo carrierconfig = getCarrierConfig(); + if (mUnderlyingNetworkCapabilities.getTransportInfo() instanceof VcnTransportInfo) { + Log.d(TAG, "Running over VCN, esp IP version is auto"); + return ESP_IP_VERSION_AUTO; + } + final CarrierConfigInfo carrierconfig = getCarrierConfigForUnderlyingNetwork(); final int ipVersion = (carrierconfig != null) ? carrierconfig.ipVersion : ESP_IP_VERSION_AUTO; if (carrierconfig != null) { - Log.d(TAG, "Get customized IP version(" + ipVersion + ") on SIM(" + Log.d(TAG, "Get customized IP version (" + ipVersion + ") on SIM (mccmnc=" + carrierconfig.mccMnc + ")"); } return ipVersion; } private int guessEspEncapTypeForNetwork() { - final CarrierConfigInfo carrierconfig = getCarrierConfig(); + if (mUnderlyingNetworkCapabilities.getTransportInfo() instanceof VcnTransportInfo) { + Log.d(TAG, "Running over VCN, encap type is auto"); + return ESP_ENCAP_TYPE_AUTO; + } + final CarrierConfigInfo carrierconfig = getCarrierConfigForUnderlyingNetwork(); final int encapType = (carrierconfig != null) ? carrierconfig.encapType : ESP_ENCAP_TYPE_AUTO; if (carrierconfig != null) { - Log.d(TAG, "Get customized encap type(" + encapType + ") on SIM(" + Log.d(TAG, "Get customized encap type (" + encapType + ") on SIM (mccmnc=" + carrierconfig.mccMnc + ")"); } return encapType; } + private int guessNattKeepaliveTimerForNetwork() { - final CarrierConfigInfo carrierconfig = getCarrierConfig(); - final int natKeepalive = (carrierconfig != null) - ? carrierconfig.keepaliveDelayMs : AUTOMATIC_KEEPALIVE_DELAY_SECONDS; + final TransportInfo transportInfo = mUnderlyingNetworkCapabilities.getTransportInfo(); + if (transportInfo instanceof VcnTransportInfo) { + final int nattKeepaliveSec = + ((VcnTransportInfo) transportInfo).getMinUdpPort4500NatTimeoutSeconds(); + Log.d(TAG, "Running over VCN, keepalive timer : " + nattKeepaliveSec + "s"); + if (VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET + != nattKeepaliveSec) { + return nattKeepaliveSec; + } + // else fall back to carrier config, if any + } + final CarrierConfigInfo carrierconfig = getCarrierConfigForUnderlyingNetwork(); + final int nattKeepaliveSec = (carrierconfig != null) + ? carrierconfig.keepaliveDelaySec : AUTOMATIC_KEEPALIVE_DELAY_SECONDS; if (carrierconfig != null) { - Log.d(TAG, "Get customized keepalive(" + natKeepalive + ") on SIM(" + Log.d(TAG, "Get customized keepalive (" + nattKeepaliveSec + "s) on SIM (mccmnc=" + carrierconfig.mccMnc + ")"); } - return natKeepalive; + return nattKeepaliveSec; } - private CarrierConfigInfo getCarrierConfig() { + /** + * Returns the carrier config for the underlying network, or null if not a cell network. + */ + @Nullable + private CarrierConfigInfo getCarrierConfigForUnderlyingNetwork() { final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities); if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { Log.d(TAG, "Underlying network is not a cellular network"); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 29c5adaea844..97e7f6f41703 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1148,12 +1148,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.userId = userId; info.installerPackageName = mInstallSource.mInstallerPackageName; info.installerAttributionTag = mInstallSource.mInstallerAttributionTag; + info.resolvedBaseCodePath = null; if (mContext.checkCallingOrSelfPermission( Manifest.permission.READ_INSTALLED_SESSION_PATHS) - == PackageManager.PERMISSION_GRANTED && mResolvedBaseFile != null) { - info.resolvedBaseCodePath = mResolvedBaseFile.getAbsolutePath(); - } else { - info.resolvedBaseCodePath = null; + == PackageManager.PERMISSION_GRANTED) { + File file = mResolvedBaseFile; + if (file == null) { + // Try to guess mResolvedBaseFile file. + final List<File> addedFiles = getAddedApksLocked(); + if (addedFiles.size() > 0) { + file = addedFiles.get(0); + } + } + if (file != null) { + info.resolvedBaseCodePath = file.getAbsolutePath(); + } } info.progress = progress; info.sealed = mSealed; @@ -1355,9 +1364,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private String[] getStageDirContentsLocked() { + if (stageDir == null) { + return EmptyArray.STRING; + } String[] result = stageDir.list(); if (result == null) { - result = EmptyArray.STRING; + return EmptyArray.STRING; } return result; } diff --git a/services/core/java/com/android/server/pm/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java new file mode 100644 index 000000000000..f48a1669c259 --- /dev/null +++ b/services/core/java/com/android/server/pm/UserJourneyLogger.java @@ -0,0 +1,552 @@ +/* + * 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.pm; + +import static android.os.UserManager.USER_TYPE_FULL_DEMO; +import static android.os.UserManager.USER_TYPE_FULL_GUEST; +import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED; +import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; +import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; +import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; +import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; +import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS; + +import static com.android.internal.util.FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.UserInfo; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * This class is logging User Lifecycle statsd events and synchronise User Lifecycle Journeys + * by making sure all events are called in correct order and errors are reported in case of + * unexpected journeys. This class also makes sure that all user sub-journeys are logged so + * for example User Switch also log User Start Journey. + */ +public class UserJourneyLogger { + + public static final int ERROR_CODE_INVALID_SESSION_ID = 0; + public static final int ERROR_CODE_UNSPECIFIED = -1; + /* + * Possible reasons for ERROR_CODE_INCOMPLETE_OR_TIMEOUT to occur: + * - A user switch journey is received while another user switch journey is in + * process for the same user. + * - A user switch journey is received while user start journey is in process for + * the same user. + * - A user start journey is received while another user start journey is in process + * for the same user. + * In all cases potentially an incomplete, timed-out session or multiple + * simultaneous requests. It is not possible to keep track of multiple sessions for + * the same user, so previous session is abandoned. + */ + public static final int ERROR_CODE_INCOMPLETE_OR_TIMEOUT = 2; + public static final int ERROR_CODE_ABORTED = 3; + public static final int ERROR_CODE_NULL_USER_INFO = 4; + public static final int ERROR_CODE_USER_ALREADY_AN_ADMIN = 5; + public static final int ERROR_CODE_USER_IS_NOT_AN_ADMIN = 6; + + @IntDef(prefix = {"ERROR_CODE"}, value = { + ERROR_CODE_UNSPECIFIED, + ERROR_CODE_INCOMPLETE_OR_TIMEOUT, + ERROR_CODE_ABORTED, + ERROR_CODE_NULL_USER_INFO, + ERROR_CODE_USER_ALREADY_AN_ADMIN, + ERROR_CODE_USER_IS_NOT_AN_ADMIN, + ERROR_CODE_INVALID_SESSION_ID + }) + public @interface UserJourneyErrorCode { + } + + // The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd + public static final int USER_JOURNEY_UNKNOWN = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN; + public static final int USER_JOURNEY_USER_SWITCH_FG = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG; + public static final int USER_JOURNEY_USER_SWITCH_UI = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI; + public static final int USER_JOURNEY_USER_START = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; + public static final int USER_JOURNEY_USER_CREATE = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; + public static final int USER_JOURNEY_USER_STOP = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; + public static final int USER_JOURNEY_USER_REMOVE = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE; + public static final int USER_JOURNEY_GRANT_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN; + public static final int USER_JOURNEY_REVOKE_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN; + + @IntDef(prefix = {"USER_JOURNEY"}, value = { + USER_JOURNEY_UNKNOWN, + USER_JOURNEY_USER_SWITCH_FG, + USER_JOURNEY_USER_SWITCH_UI, + USER_JOURNEY_USER_START, + USER_JOURNEY_USER_STOP, + USER_JOURNEY_USER_CREATE, + USER_JOURNEY_USER_REMOVE, + USER_JOURNEY_GRANT_ADMIN, + USER_JOURNEY_REVOKE_ADMIN + }) + public @interface UserJourney { + } + + + // The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd + public static final int USER_LIFECYCLE_EVENT_UNKNOWN = + USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN; + public static final int USER_LIFECYCLE_EVENT_SWITCH_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER; + public static final int USER_LIFECYCLE_EVENT_START_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER; + public static final int USER_LIFECYCLE_EVENT_CREATE_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; + public static final int USER_LIFECYCLE_EVENT_REMOVE_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; + public static final int USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__USER_RUNNING_LOCKED; + public static final int USER_LIFECYCLE_EVENT_UNLOCKING_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; + public static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; + public static final int USER_LIFECYCLE_EVENT_STOP_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; + public static final int USER_LIFECYCLE_EVENT_GRANT_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; + public static final int USER_LIFECYCLE_EVENT_REVOKE_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; + + @IntDef(prefix = {"USER_LIFECYCLE_EVENT"}, value = { + USER_LIFECYCLE_EVENT_UNKNOWN, + USER_LIFECYCLE_EVENT_SWITCH_USER, + USER_LIFECYCLE_EVENT_START_USER, + USER_LIFECYCLE_EVENT_CREATE_USER, + USER_LIFECYCLE_EVENT_REMOVE_USER, + USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + USER_LIFECYCLE_EVENT_UNLOCKING_USER, + USER_LIFECYCLE_EVENT_UNLOCKED_USER, + USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_GRANT_ADMIN, + USER_LIFECYCLE_EVENT_REVOKE_ADMIN + }) + public @interface UserLifecycleEvent { + } + + // User lifecycle event state, defined in the UserLifecycleEventOccurred atom for statsd + public static final int EVENT_STATE_BEGIN = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN; + public static final int EVENT_STATE_FINISH = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH; + public static final int EVENT_STATE_NONE = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE; + public static final int EVENT_STATE_CANCEL = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__CANCEL; + public static final int EVENT_STATE_ERROR = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR; + + @IntDef(prefix = {"EVENT_STATE"}, value = { + EVENT_STATE_BEGIN, + EVENT_STATE_FINISH, + EVENT_STATE_NONE, + EVENT_STATE_CANCEL, + EVENT_STATE_ERROR, + }) + public @interface UserLifecycleEventState { + } + + private static final int USER_ID_KEY_MULTIPLICATION = 100; + + private final Object mLock = new Object(); + + /** + * {@link UserIdInt} and {@link UserJourney} to {@link UserJourneySession} mapping used for + * statsd logging for the UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms. + */ + @GuardedBy("mLock") + private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>(); + + /** + * Returns event equivalent of given journey + */ + @UserLifecycleEvent + private static int journeyToEvent(@UserJourney int journey) { + switch (journey) { + case USER_JOURNEY_USER_SWITCH_UI: + case USER_JOURNEY_USER_SWITCH_FG: + return USER_LIFECYCLE_EVENT_SWITCH_USER; + case USER_JOURNEY_USER_START: + return USER_LIFECYCLE_EVENT_START_USER; + case USER_JOURNEY_USER_CREATE: + return USER_LIFECYCLE_EVENT_CREATE_USER; + case USER_JOURNEY_USER_STOP: + return USER_LIFECYCLE_EVENT_STOP_USER; + case USER_JOURNEY_USER_REMOVE: + return USER_LIFECYCLE_EVENT_REMOVE_USER; + case USER_JOURNEY_GRANT_ADMIN: + return USER_LIFECYCLE_EVENT_GRANT_ADMIN; + case USER_JOURNEY_REVOKE_ADMIN: + return USER_LIFECYCLE_EVENT_REVOKE_ADMIN; + default: + return USER_LIFECYCLE_EVENT_UNKNOWN; + } + } + + /** + * Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to + * the user type. + * Changes to this method require changes in CTS file + * com.android.cts.packagemanager.stats.device.UserInfoUtil + * which is duplicate for CTS tests purposes. + */ + public static int getUserTypeForStatsd(@NonNull String userType) { + switch (userType) { + case USER_TYPE_FULL_SYSTEM: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM; + case USER_TYPE_FULL_SECONDARY: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY; + case USER_TYPE_FULL_GUEST: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST; + case USER_TYPE_FULL_DEMO: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO; + case USER_TYPE_FULL_RESTRICTED: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED; + case USER_TYPE_PROFILE_MANAGED: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED; + case USER_TYPE_SYSTEM_HEADLESS: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS; + case USER_TYPE_PROFILE_CLONE: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE; + default: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN; + } + } + + /** + * Map error code to the event finish state. + */ + @UserLifecycleEventState + private static int errorToFinishState(@UserJourneyErrorCode int errorCode) { + switch (errorCode) { + case ERROR_CODE_ABORTED: + return EVENT_STATE_CANCEL; + case ERROR_CODE_UNSPECIFIED: + return EVENT_STATE_FINISH; + default: + return EVENT_STATE_ERROR; + } + } + + /** + * Simply logging USER_LIFECYCLE_JOURNEY_REPORTED if session exists. + * If session does not exist then it logs ERROR_CODE_INVALID_SESSION_ID + */ + @VisibleForTesting + public void logUserLifecycleJourneyReported(@Nullable UserJourneySession session, + @UserJourney int journey, @UserIdInt int originalUserId, @UserIdInt int targetUserId, + int userType, int userFlags, @UserJourneyErrorCode int errorCode) { + if (session == null) { + writeUserLifecycleJourneyReported(-1, journey, originalUserId, targetUserId, + userType, userFlags, ERROR_CODE_INVALID_SESSION_ID); + } else { + writeUserLifecycleJourneyReported( + session.mSessionId, journey, originalUserId, targetUserId, userType, userFlags, + errorCode); + } + } + + /** + * Helper method for spy testing + */ + @VisibleForTesting + public void writeUserLifecycleJourneyReported(long sessionId, int journey, int originalUserId, + int targetUserId, int userType, int userFlags, int errorCode) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, + sessionId, journey, originalUserId, targetUserId, userType, userFlags, + errorCode); + } + + /** + * Simply logging USER_LIFECYCLE_EVENT_OCCURRED if session exists. + * If session does not exist then it logs ERROR_CODE_INVALID_SESSION_ID + * and EVENT_STATE_ERROR + */ + @VisibleForTesting + public void logUserLifecycleEventOccurred(UserJourneySession session, + @UserIdInt int targetUserId, @UserLifecycleEvent int event, + @UserLifecycleEventState int state, @UserJourneyErrorCode int errorCode) { + if (session == null) { + writeUserLifecycleEventOccurred(-1, targetUserId, event, + EVENT_STATE_ERROR, ERROR_CODE_INVALID_SESSION_ID); + } else { + writeUserLifecycleEventOccurred(session.mSessionId, targetUserId, event, state, + errorCode); + } + } + + /** + * Helper method for spy testing + */ + @VisibleForTesting + public void writeUserLifecycleEventOccurred(long sessionId, int userId, int event, int state, + int errorCode) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, + sessionId, userId, event, state, errorCode); + } + + /** + * statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd + * atom. It finds the user journey session for target user id and logs it as that journey. + */ + public void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event, + @UserLifecycleEventState int eventState) { + final UserJourneySession userJourneySession = findUserJourneySession(userId); + logUserLifecycleEventOccurred(userJourneySession, userId, + event, eventState, UserJourneyLogger.ERROR_CODE_UNSPECIFIED); + } + + /** + * Returns first user session from mUserIdToUserJourneyMap for given user id, + * or null if user id was not found in mUserIdToUserJourneyMap. + */ + private @Nullable UserJourneySession findUserJourneySession(@UserIdInt int userId) { + synchronized (mLock) { + final int keyMapSize = mUserIdToUserJourneyMap.size(); + for (int i = 0; i < keyMapSize; i++) { + int key = mUserIdToUserJourneyMap.keyAt(i); + if (key / USER_ID_KEY_MULTIPLICATION == userId) { + return mUserIdToUserJourneyMap.get(key); + } + } + } + return null; + } + + /** + * Returns unique id for user and journey. For example if user id = 11 and journey = 7 + * then unique key = 11 * 100 + 7 = 1107 + */ + private int getUserJourneyKey(@UserIdInt int targetUserId, @UserJourney int journey) { + // We leave 99 for user journeys ids. + return (targetUserId * USER_ID_KEY_MULTIPLICATION) + journey; + } + + /** + * Special use case when user journey incomplete or timeout and current user is unclear + */ + @VisibleForTesting + public UserJourneySession finishAndClearIncompleteUserJourney(@UserIdInt int targetUserId, + @UserJourney int journey) { + synchronized (mLock) { + final int key = getUserJourneyKey(targetUserId, journey); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + logUserLifecycleEventOccurred( + userJourneySession, + targetUserId, + journeyToEvent(userJourneySession.mJourney), + EVENT_STATE_ERROR, + UserJourneyLogger.ERROR_CODE_INCOMPLETE_OR_TIMEOUT); + + logUserLifecycleJourneyReported( + userJourneySession, + journey, + /* originalUserId= */ -1, + targetUserId, + getUserTypeForStatsd(""), -1, + ERROR_CODE_INCOMPLETE_OR_TIMEOUT); + mUserIdToUserJourneyMap.remove(key); + + return userJourneySession; + } + } + return null; + } + + /** + * Log user journey event and report finishing without error + */ + public UserJourneySession logUserJourneyFinish(@UserIdInt int originalUserId, + UserInfo targetUser, @UserJourney int journey) { + return logUserJourneyFinishWithError(originalUserId, targetUser, journey, + ERROR_CODE_UNSPECIFIED); + } + + /** + * Special case when it is unknown which user switch journey was used and checking both + */ + @VisibleForTesting + public UserJourneySession logUserSwitchJourneyFinish(@UserIdInt int originalUserId, + UserInfo targetUser) { + synchronized (mLock) { + final int key_fg = getUserJourneyKey(targetUser.id, USER_JOURNEY_USER_SWITCH_FG); + final int key_ui = getUserJourneyKey(targetUser.id, USER_JOURNEY_USER_SWITCH_UI); + + if (mUserIdToUserJourneyMap.contains(key_fg)) { + return logUserJourneyFinish(originalUserId, targetUser, + USER_JOURNEY_USER_SWITCH_FG); + } + + if (mUserIdToUserJourneyMap.contains(key_ui)) { + return logUserJourneyFinish(originalUserId, targetUser, + USER_JOURNEY_USER_SWITCH_UI); + } + + return null; + } + } + + /** + * Log user journey event and report finishing with error + */ + public UserJourneySession logUserJourneyFinishWithError(@UserIdInt int originalUserId, + UserInfo targetUser, @UserJourney int journey, @UserJourneyErrorCode int errorCode) { + synchronized (mLock) { + final int state = errorToFinishState(errorCode); + final int key = getUserJourneyKey(targetUser.id, journey); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + logUserLifecycleEventOccurred( + userJourneySession, targetUser.id, + journeyToEvent(userJourneySession.mJourney), + state, + errorCode); + + logUserLifecycleJourneyReported( + userJourneySession, + journey, originalUserId, targetUser.id, + getUserTypeForStatsd(targetUser.userType), + targetUser.flags, + errorCode); + mUserIdToUserJourneyMap.remove(key); + + return userJourneySession; + } + } + return null; + } + + /** + * Log event and report finish when user is null. This is edge case when UserInfo + * can not be passed because it is null, therefore all information are passed as arguments. + */ + public UserJourneySession logNullUserJourneyError(@UserJourney int journey, + @UserIdInt int currentUserId, @UserIdInt int targetUserId, String targetUserType, + int targetUserFlags) { + synchronized (mLock) { + final int key = getUserJourneyKey(targetUserId, journey); + final UserJourneySession session = mUserIdToUserJourneyMap.get(key); + + logUserLifecycleEventOccurred( + session, targetUserId, journeyToEvent(journey), + EVENT_STATE_ERROR, + ERROR_CODE_NULL_USER_INFO); + + logUserLifecycleJourneyReported( + session, journey, currentUserId, targetUserId, + getUserTypeForStatsd(targetUserType), targetUserFlags, + ERROR_CODE_NULL_USER_INFO); + + mUserIdToUserJourneyMap.remove(key); + return session; + } + } + + /** + * Log for user creation finish event and report. This is edge case when target user id is + * different in begin event and finish event as it is unknown what is user id + * until it has been created. + */ + public UserJourneySession logUserCreateJourneyFinish(@UserIdInt int originalUserId, + UserInfo targetUser) { + synchronized (mLock) { + // we do not know user id until we create new user which is why we use -1 + // as user id to create and find session, but we log correct id. + final int key = getUserJourneyKey(-1, USER_JOURNEY_USER_CREATE); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + logUserLifecycleEventOccurred( + userJourneySession, targetUser.id, + USER_LIFECYCLE_EVENT_CREATE_USER, + EVENT_STATE_FINISH, + ERROR_CODE_UNSPECIFIED); + + logUserLifecycleJourneyReported( + userJourneySession, + USER_JOURNEY_USER_CREATE, originalUserId, targetUser.id, + getUserTypeForStatsd(targetUser.userType), + targetUser.flags, + ERROR_CODE_UNSPECIFIED); + mUserIdToUserJourneyMap.remove(key); + + return userJourneySession; + } + } + return null; + } + + /** + * Adds new UserJourneySession to mUserIdToUserJourneyMap and log UserJourneyEvent Begin state + */ + public UserJourneySession logUserJourneyBegin(@UserIdInt int targetId, + @UserJourney int journey) { + final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); + synchronized (mLock) { + final int key = getUserJourneyKey(targetId, journey); + final UserJourneySession userJourneySession = + new UserJourneySession(newSessionId, journey); + mUserIdToUserJourneyMap.append(key, userJourneySession); + + logUserLifecycleEventOccurred( + userJourneySession, targetId, + journeyToEvent(userJourneySession.mJourney), + EVENT_STATE_BEGIN, + ERROR_CODE_UNSPECIFIED); + + return userJourneySession; + } + } + + /** + * Helper class to store user journey and session id. + * + * <p> User journey tracks a chain of user lifecycle events occurring during different user + * activities such as user start, user switch, and user creation. + */ + public static class UserJourneySession { + public final long mSessionId; + @UserJourney + public final int mJourney; + + @VisibleForTesting + public UserJourneySession(long sessionId, @UserJourney int journey) { + mJourney = journey; + mSessionId = sessionId; + } + } +} diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 5f8efe29459d..b92cdde5910f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -23,6 +23,15 @@ import static android.os.UserManager.DISALLOW_USER_SWITCH; import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY; import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_ALREADY_AN_ADMIN; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_IS_NOT_AN_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE; + import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; @@ -162,7 +171,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -519,6 +527,8 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUserLifecycleListeners") private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>(); + private final UserJourneyLogger mUserJourneyLogger = new UserJourneyLogger(); + private final LockPatternUtils mLockPatternUtils; private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK = @@ -1580,45 +1590,56 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); - final long sessionId = logGrantAdminJourneyBegin(userId); + mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN); UserInfo info; synchronized (mPackagesLock) { synchronized (mUsersLock) { info = getUserInfoLU(userId); } - if (info == null || info.isAdmin()) { - // Exit if no user found with that id, or the user is already an Admin. - logUserJourneyError(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN, - userId); + if (info == null) { + // Exit if no user found with that id, + mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN, + getCurrentUserId(), userId, /* userType */ "", /* userFlags */ -1); + return; + } else if (info.isAdmin()) { + // Exit if the user is already an Admin. + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), info, + USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_USER_ALREADY_AN_ADMIN); return; } info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(getUserDataLU(info.id)); } - logGrantAdminJourneyFinish(sessionId, userId, info.userType, info.flags); + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), info, + USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_UNSPECIFIED); } @Override public void revokeUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("revoke admin privileges"); - final long sessionId = logRevokeAdminJourneyBegin(userId); + mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN); UserData user; synchronized (mPackagesLock) { synchronized (mUsersLock) { user = getUserDataLU(userId); - if (user == null || !user.info.isAdmin()) { - // Exit if no user found with that id, or the user is not an Admin. - logUserJourneyError(sessionId, FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN, - userId); + if (user == null) { + // Exit if no user found with that id + mUserJourneyLogger.logNullUserJourneyError( + USER_JOURNEY_REVOKE_ADMIN, + getCurrentUserId(), userId, "", -1); + return; + } else if (!user.info.isAdmin()) { + // Exit if no user is not an Admin. + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), user.info, + USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_NOT_AN_ADMIN); return; } user.info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(user); } } - logRevokeAdminJourneyFinish(sessionId, userId, user.info.userType, user.info.flags); + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), user.info, + USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_UNSPECIFIED); } /** @@ -4700,16 +4721,20 @@ public class UserManagerService extends IUserManager.Stub { final int noneUserId = -1; final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("createUser-" + flags); - final long sessionId = logUserCreateJourneyBegin(noneUserId); + mUserJourneyLogger.logUserJourneyBegin(noneUserId, USER_JOURNEY_USER_CREATE); UserInfo newUser = null; try { newUser = createUserInternalUncheckedNoTracing(name, userType, flags, parentId, preCreate, disallowedPackages, t, token); return newUser; } finally { - logUserCreateJourneyFinish(sessionId, - newUser != null ? newUser.id : noneUserId, userType, flags, - newUser != null); + if (newUser != null) { + mUserJourneyLogger.logUserCreateJourneyFinish(getCurrentUserId(), newUser); + } else { + mUserJourneyLogger.logNullUserJourneyError( + USER_JOURNEY_USER_CREATE, + getCurrentUserId(), noneUserId, userType, flags); + } t.traceEnd(); } } @@ -5198,137 +5223,6 @@ public class UserManagerService extends IUserManager.Stub { && !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED); } - private long logUserCreateJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - userId); - } - - private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags, boolean finish) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - userId, userType, flags, finish); - } - - private long logUserRemoveJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, - userId); - } - - private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags, boolean finish) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, - userId, userType, flags, finish); - } - - private long logGrantAdminJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN, - userId); - } - - private void logGrantAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN, - userId, userType, flags, true); - } - - private long logRevokeAdminJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN, - userId); - } - - private void logRevokeAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN, - userId, userType, flags, true); - } - - private void logUserJourneyFinish(long sessionId, int journey, @UserIdInt int userId, - String userType, @UserInfoFlag int flags, boolean finish) { - - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, - journey, /* origin_user= */ getCurrentUserId(), userId, - UserManager.getUserTypeForStatsd(userType), flags); - - int event; - switch (journey) { - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; - break; - default: - throw new IllegalArgumentException("Journey " + journey + " not expected."); - } - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, - finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH - : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); - } - - private long logUserJourneyBegin(int journey, @UserIdInt int userId) { - final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - - // log the event atom to indicate the event start - int event; - switch (journey) { - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; - break; - default: - throw new IllegalArgumentException("Journey " + journey + " not expected."); - } - - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); - return sessionId; - } - - private void logUserJourneyError(long sessionId, int journey, @UserIdInt int userId) { - - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, - journey, /* origin_user= */ getCurrentUserId(), userId); - - int event; - switch (journey) { - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; - break; - default: - throw new IllegalArgumentException("Journey " + journey + " not expected."); - } - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR); - } - /** Register callbacks for statsd pulled atoms. */ private void registerStatsCallbacks() { final StatsManager statsManager = mContext.getSystemService(StatsManager.class); @@ -5352,7 +5246,8 @@ public class UserManagerService extends IUserManager.Stub { if (size > 1) { for (int idx = 0; idx < size; idx++) { final UserInfo user = users.get(idx); - final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType); + final int userTypeStandard = mUserJourneyLogger + .getUserTypeForStatsd(user.userType); final String userTypeCustom = (userTypeStandard == FrameworkStatsLog .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ? @@ -5635,7 +5530,7 @@ public class UserManagerService extends IUserManager.Stub { writeUserLP(userData); } - final long sessionId = logUserRemoveJourneyBegin(userId); + mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_USER_REMOVE); try { mAppOpsService.removeUser(userId); @@ -5657,13 +5552,17 @@ public class UserManagerService extends IUserManager.Stub { @Override public void userStopped(int userIdParam) { finishRemoveUser(userIdParam); - logUserRemoveJourneyFinish(sessionId, userIdParam, - userData.info.userType, userData.info.flags, true); + int originUserId = UserManagerService.this.getCurrentUserId(); + mUserJourneyLogger.logUserJourneyFinishWithError(originUserId, + userData.info, USER_JOURNEY_USER_REMOVE, + ERROR_CODE_UNSPECIFIED); } @Override public void userStopAborted(int userIdParam) { - logUserRemoveJourneyFinish(sessionId, userIdParam, - userData.info.userType, userData.info.flags, false); + int originUserId = UserManagerService.this.getCurrentUserId(); + mUserJourneyLogger.logUserJourneyFinishWithError(originUserId, + userData.info, USER_JOURNEY_USER_REMOVE, + ERROR_CODE_ABORTED); } }); } catch (RemoteException e) { @@ -7297,9 +7196,9 @@ public class UserManagerService extends IUserManager.Stub { final UserInfo userInfo = getUserInfo(userIds[i]); if (userInfo == null) { // Not possible because the input user ids should all be valid - userTypes[i] = UserManager.getUserTypeForStatsd(""); + userTypes[i] = mUserJourneyLogger.getUserTypeForStatsd(""); } else { - userTypes[i] = UserManager.getUserTypeForStatsd(userInfo.userType); + userTypes[i] = mUserJourneyLogger.getUserTypeForStatsd(userInfo.userType); } } return userTypes; @@ -7536,4 +7435,11 @@ public class UserManagerService extends IUserManager.Stub { .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser); } + /** + * Returns instance of {@link com.android.server.pm.UserJourneyLogger}. + */ + public UserJourneyLogger getUserJourneyLogger() { + return mUserJourneyLogger; + } + } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9ff98be6f5cd..f8954b7c7f95 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -5690,8 +5690,14 @@ public final class PowerManagerService extends SystemService } if (eventTime > now) { - Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now); - throw new IllegalArgumentException("event time must not be in the future"); + Slog.wtf(TAG, "Event cannot be newer than the current time (" + + "now=" + now + + ", eventTime=" + eventTime + + ", displayId=" + displayId + + ", event=" + PowerManager.userActivityEventToString(event) + + ", flags=" + flags + + ")"); + return; } final int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 9020cb3405a2..45c7c9ad4477 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2445,12 +2445,36 @@ public class WallpaperManagerService extends IWallpaperManager.Stub /** * TODO(multi-display) Extends this method with specific display. - * Propagate ambient state to wallpaper engine. + * Propagate ambient state to wallpaper engine(s). * * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise. * @param animationDuration Duration of the animation, or 0 when immediate. */ public void setInAmbientMode(boolean inAmbientMode, long animationDuration) { + if (mIsLockscreenLiveWallpaperEnabled) { + List<IWallpaperEngine> engines = new ArrayList<>(); + synchronized (mLock) { + mInAmbientMode = inAmbientMode; + for (WallpaperData data : getActiveWallpapers()) { + if (data.connection.mInfo == null + || data.connection.mInfo.supportsAmbientMode()) { + // TODO(multi-display) Extends this method with specific display. + IWallpaperEngine engine = data.connection + .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; + if (engine != null) engines.add(engine); + } + } + } + for (IWallpaperEngine engine : engines) { + try { + engine.setInAmbientMode(inAmbientMode, animationDuration); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to set ambient mode", e); + } + } + return; + } + final IWallpaperEngine engine; synchronized (mLock) { mInAmbientMode = inAmbientMode; @@ -2475,10 +2499,25 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } /** - * Propagate a wake event to the wallpaper engine. + * Propagate a wake event to the wallpaper engine(s). */ public void notifyWakingUp(int x, int y, @NonNull Bundle extras) { synchronized (mLock) { + if (mIsLockscreenLiveWallpaperEnabled) { + for (WallpaperData data : getActiveWallpapers()) { + data.connection.forEachDisplayConnector(displayConnector -> { + if (displayConnector.mEngine != null) { + try { + displayConnector.mEngine.dispatchWallpaperCommand( + WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to dispatch COMMAND_WAKING_UP", e); + } + } + }); + } + return; + } final WallpaperData data = mWallpaperMap.get(mCurrentUserId); if (data != null && data.connection != null) { data.connection.forEachDisplayConnector( @@ -2497,10 +2536,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } /** - * Propagate a sleep event to the wallpaper engine. + * Propagate a sleep event to the wallpaper engine(s). */ public void notifyGoingToSleep(int x, int y, @NonNull Bundle extras) { synchronized (mLock) { + if (mIsLockscreenLiveWallpaperEnabled) { + for (WallpaperData data : getActiveWallpapers()) { + data.connection.forEachDisplayConnector(displayConnector -> { + if (displayConnector.mEngine != null) { + try { + displayConnector.mEngine.dispatchWallpaperCommand( + WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1, + extras); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to dispatch COMMAND_GOING_TO_SLEEP", e); + } + } + }); + } + return; + } final WallpaperData data = mWallpaperMap.get(mCurrentUserId); if (data != null && data.connection != null) { data.connection.forEachDisplayConnector( @@ -2520,11 +2575,27 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } /** - * Propagates screen turned on event to wallpaper engine. + * Propagates screen turned on event to wallpaper engine(s). */ @Override public void notifyScreenTurnedOn(int displayId) { synchronized (mLock) { + if (mIsLockscreenLiveWallpaperEnabled) { + for (WallpaperData data : getActiveWallpapers()) { + if (data.connection.containsDisplay(displayId)) { + final IWallpaperEngine engine = data.connection + .getDisplayConnectorOrCreate(displayId).mEngine; + if (engine != null) { + try { + engine.onScreenTurnedOn(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to notify that the screen turned on", e); + } + } + } + } + return; + } final WallpaperData data = mWallpaperMap.get(mCurrentUserId); if (data != null && data.connection != null @@ -2545,11 +2616,27 @@ public class WallpaperManagerService extends IWallpaperManager.Stub /** - * Propagate screen turning on event to wallpaper engine. + * Propagate screen turning on event to wallpaper engine(s). */ @Override public void notifyScreenTurningOn(int displayId) { synchronized (mLock) { + if (mIsLockscreenLiveWallpaperEnabled) { + for (WallpaperData data : getActiveWallpapers()) { + if (data.connection.containsDisplay(displayId)) { + final IWallpaperEngine engine = data.connection + .getDisplayConnectorOrCreate(displayId).mEngine; + if (engine != null) { + try { + engine.onScreenTurningOn(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to notify that the screen is turning on", e); + } + } + } + } + return; + } final WallpaperData data = mWallpaperMap.get(mCurrentUserId); if (data != null && data.connection != null @@ -2576,6 +2663,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return true; } + private WallpaperData[] getActiveWallpapers() { + WallpaperData systemWallpaper = mWallpaperMap.get(mCurrentUserId); + WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId); + boolean systemValid = systemWallpaper != null && systemWallpaper.connection != null; + boolean lockValid = lockWallpaper != null && lockWallpaper.connection != null; + return systemValid && lockValid ? new WallpaperData[]{systemWallpaper, lockWallpaper} + : systemValid ? new WallpaperData[]{systemWallpaper} + : lockValid ? new WallpaperData[]{lockWallpaper} + : new WallpaperData[0]; + } + private IWallpaperEngine getEngine(int which, int userId, int displayId) { WallpaperData wallpaperData = findWallpaperAtDisplay(userId, displayId); if (wallpaperData == null) return null; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 25e5dacb25e3..26b40b4c09ee 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -582,6 +582,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mPauseSchedulePendingForPip = false; + // Gets set to indicate that the activity is currently being auto-pipped. + boolean mAutoEnteringPip = false; + private void updateEnterpriseThumbnailDrawable(Context context) { DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); mEnterpriseThumbnailDrawable = dpm.getResources().getDrawable( @@ -4908,9 +4911,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTransitionController.setStatusBarTransitionDelay( mPendingRemoteAnimation.getStatusBarTransitionDelay()); } else { - if (mPendingOptions == null - || mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) { - // Scene transition will run on the client side. + if (mPendingOptions == null) { + return; + } else if (mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) { + // Scene transition will run on the client side, so just notify transition + // controller but don't clear the animation information from the options since they + // need to be sent to the animating activity. + mTransitionController.setOverrideAnimation( + AnimationOptions.makeSceneTransitionAnimOptions(), null, null); return; } applyOptionsAnimation(mPendingOptions, intent); @@ -6094,8 +6102,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token, PauseActivityItem.obtain(finishing, false /* userLeaving */, - configChangeFlags, false /* dontReport */, - false /* autoEnteringPip */)); + configChangeFlags, false /* dontReport */, mAutoEnteringPip)); } catch (Exception e) { Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a0ea1c3dbdbf..f86df2aa9bed 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3592,15 +3592,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + boolean enterPictureInPictureMode(@NonNull ActivityRecord r, + @NonNull PictureInPictureParams params, boolean fromClient) { + return enterPictureInPictureMode(r, params, fromClient, false /* isAutoEnter */); + } + /** * Puts the given activity in picture in picture mode if possible. * * @param fromClient true if this comes from a client call (eg. Activity.enterPip). + * @param isAutoEnter true if this comes from an automatic pip-enter. * @return true if the activity is now in picture-in-picture mode, or false if it could not * enter picture-in-picture mode. */ boolean enterPictureInPictureMode(@NonNull ActivityRecord r, - @NonNull PictureInPictureParams params, boolean fromClient) { + @NonNull PictureInPictureParams params, boolean fromClient, boolean isAutoEnter) { // If the activity is already in picture in picture mode, then just return early if (r.inPinnedWindowingMode()) { return true; @@ -3635,6 +3641,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } r.setPictureInPictureParams(params); + r.mAutoEnteringPip = isAutoEnter; mRootWindowContainer.moveActivityToPinnedRootTask(r, null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", transition); @@ -3643,6 +3650,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.getTask().schedulePauseActivity(r, false /* userLeaving */, false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip"); } + r.mAutoEnteringPip = false; } }; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index ce4362853b23..be52e5a4566b 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1095,11 +1095,9 @@ public class DisplayPolicy { } else { overrideProviders = null; } - final @InsetsType int type = provider.getType(); - final int id = InsetsSource.createId( - provider.getOwner(), provider.getIndex(), type); - mDisplayContent.getInsetsStateController().getOrCreateSourceProvider(id, type) - .setWindowContainer(win, frameProvider, overrideProviders); + mDisplayContent.getInsetsStateController().getOrCreateSourceProvider( + provider.getId(), provider.getType()).setWindowContainer( + win, frameProvider, overrideProviders); mInsetsSourceWindowsExceptIme.add(win); } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index fe13b87a079a..ddf96c53323d 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -311,16 +311,13 @@ class InsetsPolicy { state.removeSource(ID_IME); } else if (attrs.providedInsets != null) { for (InsetsFrameProvider provider : attrs.providedInsets) { - final int id = InsetsSource.createId( - provider.getOwner(), provider.getIndex(), provider.getType()); - final @InsetsType int type = provider.getType(); - if ((type & WindowInsets.Type.systemBars()) == 0) { + if ((provider.getType() & WindowInsets.Type.systemBars()) == 0) { continue; } if (state == originalState) { state = new InsetsState(state); } - state.removeSource(id); + state.removeSource(provider.getId()); } } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 08a6358afbc7..5f6d66011768 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -269,6 +269,8 @@ class KeyguardController { TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc); updateKeyguardSleepToken(); + // Make the home wallpaper visible + dc.mWallpaperController.showHomeWallpaperInTransition(); // Some stack visibility might change (e.g. docked stack) mRootWindowContainer.resumeFocusedTasksTopActivities(); mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 7bd3b3253614..a1e497e1e099 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -607,9 +607,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { recordDisplay(wc.getDisplayContent()); if (info.mShowWallpaper) { // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set. - final WindowState wallpaper = - wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper(); - if (wallpaper != null) { + final List<WindowState> wallpapers = + wc.getDisplayContent().mWallpaperController.getAllTopWallpapers(); + for (int i = wallpapers.size() - 1; i >= 0; i--) { + WindowState wallpaper = wallpapers.get(i); collect(wallpaper.mToken); } } @@ -954,8 +955,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // to the transient activity. ar.supportsEnterPipOnTaskSwitch = true; } + // Make sure this activity can enter pip under the current circumstances. + // `enterPictureInPicture` internally checks, but with beforeStopping=false which + // is specifically for non-auto-enter. + if (!ar.checkEnterPictureInPictureState("enterPictureInPictureMode", + true /* beforeStopping */)) { + return false; + } return mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs, - false /* fromClient */); + false /* fromClient */, true /* isAutoEnter */); } // Legacy pip-entry (not via isAutoEnterEnabled). @@ -1317,6 +1325,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // ActivityRecord#canShowWindows() may reject to show its window. The visibility also // needs to be updated for STATE_ABORT. commitVisibleActivities(transaction); + commitVisibleWallpapers(); // Fall-back to the default display if there isn't one participating. final DisplayContent primaryDisplay = !mTargetDisplays.isEmpty() ? mTargetDisplays.get(0) @@ -1349,6 +1358,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } // Resolve the animating targets from the participants. mTargets = calculateTargets(mParticipants, mChanges); + // Check whether the participants were animated from back navigation. mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets); final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction); @@ -1625,6 +1635,30 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } + /** + * Reset waitingToshow for all wallpapers, and commit the visibility of the visible ones + */ + private void commitVisibleWallpapers() { + boolean showWallpaper = shouldWallpaperBeVisible(); + for (int i = mParticipants.size() - 1; i >= 0; --i) { + final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken(); + if (wallpaper != null) { + wallpaper.waitingToShow = false; + if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) { + wallpaper.commitVisibility(showWallpaper); + } + } + } + } + + private boolean shouldWallpaperBeVisible() { + for (int i = mParticipants.size() - 1; i >= 0; --i) { + WindowContainer participant = mParticipants.valueAt(i); + if (participant.showWallpaper()) return true; + } + return false; + } + // TODO(b/188595497): Remove after migrating to shell. /** @see RecentsAnimationController#attachNavigationBarToApp */ private void handleLegacyRecentsStartBehavior(DisplayContent dc, TransitionInfo info) { diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 7cb6b46666f9..c9316bf6e972 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -813,6 +813,10 @@ class TransitionController { } ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record); mPlayingTransitions.remove(record); + if (!inTransition()) { + // reset track-count now since shell-side is idle. + mTrackCount = 0; + } updateRunningRemoteAnimation(record, false /* isPlaying */); record.finishTransition(); for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) { @@ -825,10 +829,9 @@ class TransitionController { } } mRunningLock.doNotifyLocked(); - // Run state-validation checks when no transitions are active anymore. + // Run state-validation checks when no transitions are active anymore (Note: sometimes + // finish can start a transition, so check afterwards -- eg. pip). if (!inTransition()) { - // Can reset track-count now that everything is idle. - mTrackCount = 0; validateStates(); mAtm.mWindowManager.onAnimationFinished(); } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 7ceac4fe7f16..edafe0606b13 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -124,24 +124,19 @@ class WallpaperController { final boolean mIsLockscreenLiveWallpaperEnabled; - private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> { - if ((w.mAttrs.type == TYPE_WALLPAPER)) { - if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) { - WallpaperWindowToken token = w.mToken.asWallpaperToken(); - if (token == null) { - Slog.w(TAG, "Window " + w + " has wallpaper type but not wallpaper token"); - return false; - } - if (!token.canShowWhenLocked() && mDisplayContent.isKeyguardLocked()) { - return false; - } - mFindResults.setTopWallpaper(w); - mFindResults.resetTopWallpaper = false; + private final Consumer<WindowState> mFindWallpapers = w -> { + if (w.mAttrs.type == TYPE_WALLPAPER) { + WallpaperWindowToken token = w.mToken.asWallpaperToken(); + if (token.canShowWhenLocked() && !mFindResults.hasTopShowWhenLockedWallpaper()) { + mFindResults.setTopShowWhenLockedWallpaper(w); + } else if (!token.canShowWhenLocked() + && !mFindResults.hasTopHideWhenLockedWallpaper()) { + mFindResults.setTopHideWhenLockedWallpaper(w); } - return false; } + }; - mFindResults.resetTopWallpaper = true; + private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> { if (!w.mTransitionController.isShellTransitionsEnabled()) { if (w.mActivityRecord != null && !w.mActivityRecord.isVisible() && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) { @@ -344,6 +339,31 @@ class WallpaperController { } } + /** + * Change the visibility if wallpaper is home screen only. + * This is called during the keyguard unlocking transition + * (see {@link KeyguardController#keyguardGoingAway(int, int)}) and thus assumes that if the + * system wallpaper is shared with lock, then it needs no animation. + */ + public void showHomeWallpaperInTransition() { + updateWallpaperWindowsTarget(mFindResults); + + if (!mFindResults.hasTopShowWhenLockedWallpaper()) { + Slog.w(TAG, "There is no wallpaper for the lock screen"); + return; + } + WindowState hideWhenLocked = mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper; + WindowState showWhenLocked = mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper; + if (!mFindResults.hasTopHideWhenLockedWallpaper()) { + // Shared wallpaper, ensure its visibility + showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindows(true); + } else { + // Separate lock and home wallpapers: show home wallpaper and hide lock + hideWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(true); + showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(false); + } + } + void hideDeferredWallpapersIfNeededLegacy() { for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { final WallpaperWindowToken token = mWallpaperTokens.get(i); @@ -668,13 +688,26 @@ class WallpaperController { mFindResults.setUseTopWallpaperAsTarget(true); } + mDisplayContent.forAllWindows(mFindWallpapers, true /* traverseTopToBottom */); mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */); if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) { - mFindResults.setWallpaperTarget(mFindResults.topWallpaper); + mFindResults.setWallpaperTarget( + mFindResults.getTopWallpaper(mDisplayContent.isKeyguardLocked())); } } + List<WindowState> getAllTopWallpapers() { + ArrayList<WindowState> wallpapers = new ArrayList<>(2); + if (mFindResults.hasTopShowWhenLockedWallpaper()) { + wallpapers.add(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper); + } + if (mFindResults.hasTopHideWhenLockedWallpaper()) { + wallpapers.add(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper); + } + return wallpapers; + } + private boolean isFullscreen(WindowManager.LayoutParams attrs) { return attrs.x == 0 && attrs.y == 0 && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT; @@ -760,10 +793,16 @@ class WallpaperController { result.setWallpaperTarget(wallpaperTarget); } + /** + * Change the visibility of the top wallpaper to {@param visibility} and hide all the others. + */ private void updateWallpaperTokens(boolean visibility, boolean locked) { + WindowState topWallpaper = mFindResults.getTopWallpaper(locked); + WallpaperWindowToken topWallpaperToken = + topWallpaper == null ? null : topWallpaper.mToken.asWallpaperToken(); for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); - token.updateWallpaperWindows(visibility && (!locked || token.canShowWhenLocked())); + token.updateWallpaperWindows(visibility && (token == topWallpaperToken)); } } @@ -801,8 +840,10 @@ class WallpaperController { } } - // Keep both wallpapers visible unless the keyguard is locked (then hide private wp) - updateWallpaperTokens(visible, mDisplayContent.isKeyguardLocked()); + if (!mDisplayContent.isKeyguardGoingAway() || !mIsLockscreenLiveWallpaperEnabled) { + // When keyguard goes away, KeyguardController handles the visibility + updateWallpaperTokens(visible, mDisplayContent.isKeyguardLocked()); + } if (DEBUG_WALLPAPER) { Slog.v(TAG, "adjustWallpaperWindows: wallpaper visibility " + visible @@ -1019,14 +1060,52 @@ class WallpaperController { /** Helper class for storing the results of a wallpaper target find operation. */ final private static class FindWallpaperTargetResult { - WindowState topWallpaper = null; + + static final class TopWallpaper { + // A wp that can be visible on home screen only + WindowState mTopHideWhenLockedWallpaper = null; + // A wallpaper that has permission to be visible on lock screen (lock or shared wp) + WindowState mTopShowWhenLockedWallpaper = null; + + void reset() { + mTopHideWhenLockedWallpaper = null; + mTopShowWhenLockedWallpaper = null; + } + } + + TopWallpaper mTopWallpaper = new TopWallpaper(); boolean useTopWallpaperAsTarget = false; WindowState wallpaperTarget = null; - boolean resetTopWallpaper = false; boolean isWallpaperTargetForLetterbox = false; - void setTopWallpaper(WindowState win) { - topWallpaper = win; + void setTopHideWhenLockedWallpaper(WindowState win) { + if (DEBUG_WALLPAPER) { + Slog.v(TAG, "setTopHideWhenLockedWallpaper " + win); + } + mTopWallpaper.mTopHideWhenLockedWallpaper = win; + } + + void setTopShowWhenLockedWallpaper(WindowState win) { + if (DEBUG_WALLPAPER) { + Slog.v(TAG, "setTopShowWhenLockedWallpaper " + win); + } + mTopWallpaper.mTopShowWhenLockedWallpaper = win; + } + + boolean hasTopHideWhenLockedWallpaper() { + return mTopWallpaper.mTopHideWhenLockedWallpaper != null; + } + + boolean hasTopShowWhenLockedWallpaper() { + return mTopWallpaper.mTopShowWhenLockedWallpaper != null; + } + + WindowState getTopWallpaper(boolean isKeyguardLocked) { + if (!isKeyguardLocked && hasTopHideWhenLockedWallpaper()) { + return mTopWallpaper.mTopHideWhenLockedWallpaper; + } else { + return mTopWallpaper.mTopShowWhenLockedWallpaper; + } } void setWallpaperTarget(WindowState win) { @@ -1042,10 +1121,9 @@ class WallpaperController { } void reset() { - topWallpaper = null; + mTopWallpaper.reset(); wallpaperTarget = null; useTopWallpaperAsTarget = false; - resetTopWallpaper = false; isWallpaperTargetForLetterbox = false; } } diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 1ffee05d20ec..5ea8f65b759c 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -128,6 +128,20 @@ class WallpaperWindowToken extends WindowToken { } } + /** + * Update the visibility of the token to {@param visible}. If a transition will collect the + * wallpaper, then the visibility will be committed during the execution of the transition. + * + * waitingToShow is reset at the beginning of the transition: + * {@link Transition#onTransactionReady(int, SurfaceControl.Transaction)} + */ + void updateWallpaperWindowsInTransition(boolean visible) { + if (mTransitionController.isCollecting() && mVisibleRequested != visible) { + waitingToShow = true; + } + updateWallpaperWindows(visible); + } + void updateWallpaperWindows(boolean visible) { if (mVisibleRequested != visible) { ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b", @@ -199,11 +213,11 @@ class WallpaperWindowToken extends WindowToken { } /** - * Commits the visibility of this token. This will directly update the visibility without - * regard for other state (like being in a transition). + * Commits the visibility of this token. This will directly update the visibility unless the + * wallpaper is in a transition. */ void commitVisibility(boolean visible) { - if (visible == isVisible()) return; + if (visible == isVisible() || waitingToShow) return; ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "commitVisibility: %s: visible=%b mVisibleRequested=%b", this, diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a5cdd0b43eb0..3ccf183920d3 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -435,8 +435,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mLocalInsetsSources == null) { mLocalInsetsSources = new SparseArray<>(); } - final int id = InsetsSource.createId( - provider.getOwner(), provider.getIndex(), provider.getType()); + final int id = provider.getId(); if (mLocalInsetsSources.get(id) != null) { if (DEBUG) { Slog.d(TAG, "The local insets source for this " + provider @@ -457,8 +456,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return; } - final int id = InsetsSource.createId( - provider.getOwner(), provider.getIndex(), provider.getType()); + final int id = provider.getId(); if (mLocalInsetsSources.get(id) == null) { if (DEBUG) { Slog.d(TAG, "Given " + provider + " doesn't have a local insets source."); diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 4af685e42246..0488247ec78b 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -561,6 +561,14 @@ static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIE return env->NewStringUTF(path.c_str()); } +static jboolean com_android_server_am_CachedAppOptimizer_isFreezerProfileValid(JNIEnv* env) { + int uid = getuid(); + int pid = getpid(); + + return isProfileValidForProcess("Frozen", uid, pid) && + isProfileValidForProcess("Unfrozen", uid, pid); +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"cancelCompaction", "()V", @@ -578,7 +586,9 @@ static const JNINativeMethod sMethods[] = { {"getBinderFreezeInfo", "(I)I", (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}, {"getFreezerCheckPath", "()Ljava/lang/String;", - (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath}}; + (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath}, + {"isFreezerProfileValid", "()Z", + (void*)com_android_server_am_CachedAppOptimizer_isFreezerProfileValid}}; int register_android_server_am_CachedAppOptimizer(JNIEnv* env) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index cf49dcf8004e..0c4830afafcc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -684,6 +684,38 @@ final class DevicePolicyEngine { } } + <V> void transferPolicies(EnforcingAdmin oldAdmin, EnforcingAdmin newAdmin) { + Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); + for (PolicyKey policy : globalPolicies) { + PolicyState<?> policyState = mGlobalPolicies.get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { + PolicyDefinition<V> policyDefinition = + (PolicyDefinition<V>) policyState.getPolicyDefinition(); + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); + setGlobalPolicy(policyDefinition, newAdmin, policyValue); + } + } + + for (int i = 0; i < mLocalPolicies.size(); i++) { + int userId = mLocalPolicies.keyAt(i); + Set<PolicyKey> localPolicies = new HashSet<>( + mLocalPolicies.get(userId).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { + PolicyDefinition<V> policyDefinition = + (PolicyDefinition<V>) policyState.getPolicyDefinition(); + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); + setLocalPolicy(policyDefinition, newAdmin, policyValue, userId); + } + } + } + + removePoliciesForAdmin(oldAdmin); + } + private Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdminLocked( Map<PolicyKey, PolicyState<?>> policies, EnforcingAdmin admin) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 675ebd3ddd60..3578b16d62c6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3882,6 +3882,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver); final int oldAdminUid = adminToTransfer.getUid(); + if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { + EnforcingAdmin oldAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + outgoingReceiver, userHandle, adminToTransfer); + EnforcingAdmin newAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + incomingReceiver, userHandle, adminToTransfer); + + mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin); + } + adminToTransfer.transfer(incomingDeviceInfo); policy.mAdminMap.remove(outgoingReceiver); policy.mAdminMap.put(incomingReceiver, adminToTransfer); @@ -6051,7 +6062,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void lockNow(int flags, String callerPackageName, boolean parent) { CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(callerPackageName); } else { caller = getCallerIdentity(); @@ -6063,7 +6074,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin admin; // Make sure the caller has any active admin with the right policy or // the required permission. - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { admin = enforcePermissionAndGetEnforcingAdmin( /* admin= */ null, /* permission= */ MANAGE_DEVICE_POLICY_LOCK, @@ -8917,13 +8928,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // The effect of this policy is device-wide. enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -8951,13 +8962,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforceCanQuery(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL); } else { Objects.requireNonNull(who, "ComponentName is null"); @@ -8980,13 +8991,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // The effect of this policy is device-wide. EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, @@ -9026,13 +9037,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // The effect of this policy is device-wide. enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -9335,7 +9346,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); @@ -9345,7 +9356,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userHandle = caller.getUserId(); int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle; synchronized (getLockObject()) { - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // SUPPORT USES_POLICY_DISABLE_KEYGUARD_FEATURES EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_KEYGUARD, caller.getPackageName(), @@ -9424,13 +9435,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (who != null) { - if (isPermissionCheckFlagEnabled()) { - EnforcingAdmin admin = getEnforcingAdminForCaller( - who, who.getPackageName()); + if (isUnicornFlagEnabled()) { + EnforcingAdmin admin = getEnforcingAdminForPackage( + who, who.getPackageName(), userHandle); Integer features = mDevicePolicyEngine.getLocalPolicySetByAdmin( PolicyDefinition.KEYGUARD_DISABLED_FEATURES, admin, affectedUserId); + return features == null ? 0 : features; } else { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); @@ -9438,7 +9450,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { Integer features = mDevicePolicyEngine.getResolvedPolicy( PolicyDefinition.KEYGUARD_DISABLED_FEATURES, affectedUserId); @@ -9999,10 +10011,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "clearDeviceOwner can only be called by the device owner"); } enforceUserUnlocked(deviceOwnerUserId); - DevicePolicyData policy = getUserData(deviceOwnerUserId); - if (policy.mPasswordTokenHandle != 0) { - mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, deviceOwnerUserId); - } final ActiveAdmin admin = getDeviceOwnerAdminLocked(); mInjector.binderWithCleanCallingIdentity(() -> { @@ -10057,6 +10065,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } final DevicePolicyData policyData = getUserData(userId); policyData.mCurrentInputMethodSet = false; + if (policyData.mPasswordTokenHandle != 0) { + mLockPatternUtils.removeEscrowToken(policyData.mPasswordTokenHandle, userId); + policyData.mPasswordTokenHandle = 0; + } saveSettingsLocked(userId); mPolicyCache.onUserRemoved(userId); final DevicePolicyData systemPolicyData = getUserData(UserHandle.USER_SYSTEM); @@ -10748,7 +10760,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @VisibleForTesting boolean hasDeviceIdAccessUnchecked(String packageName, int uid) { final int userId = UserHandle.getUserId(uid); - if (isPermissionCheckFlagEnabled()) { + // TODO(b/280048070): Introduce a permission to handle device ID access + if (isPermissionCheckFlagEnabled() + && !(isUidProfileOwnerLocked(uid) || isUidDeviceOwnerLocked(uid))) { return hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES, packageName, userId); } else { ComponentName deviceOwner = getDeviceOwnerComponent(true); @@ -11635,7 +11649,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, @@ -13062,7 +13076,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, @@ -13132,7 +13146,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); ActiveAdmin admin; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_PACKAGE_STATE, @@ -13229,7 +13243,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforcePermission( MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), @@ -13835,7 +13849,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean hidden, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { // TODO: We need to ensure the delegate with DELEGATION_PACKAGE_ACCESS can do this enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); } else { @@ -13854,7 +13868,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean result; synchronized (getLockObject()) { if (parent) { - if (!isPermissionCheckFlagEnabled()) { + if (!isPolicyEngineForFinanceFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice( caller.getUserId()) && isManagedProfile(caller.getUserId())); @@ -13871,7 +13885,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)", packageName, hidden, userId); } - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); mDevicePolicyEngine.setLocalPolicy( PolicyDefinition.APPLICATION_HIDDEN(packageName), @@ -13910,7 +13924,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { // TODO: Also support DELEGATION_PACKAGE_ACCESS enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); } else { @@ -13922,7 +13936,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (parent) { - if (!isPermissionCheckFlagEnabled()) { + if (!isPolicyEngineForFinanceFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()) && isManagedProfile(caller.getUserId())); @@ -14114,13 +14128,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceMaxStringLength(accountType, "account type"); CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } synchronized (getLockObject()) { - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { int affectedUser = getAffectedUser(parent); EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, @@ -14183,7 +14197,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { CallerIdentity caller; Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); final ArraySet<String> resultSet = new ArraySet<>(); - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { int affectedUser = parent ? getProfileParentId(userId) : userId; caller = getCallerIdentity(callerPackageName); if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, @@ -15554,12 +15568,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public boolean setStatusBarDisabled(ComponentName who, String callerPackageName, boolean disabled) { CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -15570,7 +15584,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - if (!isPermissionCheckFlagEnabled()) { + if (!isUnicornFlagEnabled()) { Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), "Admin " + who + " is neither the device owner or affiliated " + "user's profile owner."); @@ -15629,7 +15643,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isStatusBarDisabled(String callerPackage) { final CallerIdentity caller = getCallerIdentity(callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforceCanQuery( MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId()); } else { @@ -15639,7 +15653,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - if (!isPermissionCheckFlagEnabled()) { + if (!isUnicornFlagEnabled()) { Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), "Admin " + callerPackage + " is neither the device owner or affiliated user's profile owner."); @@ -16800,7 +16814,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } EnforcingAdmin enforcingAdmin; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, @@ -16971,7 +16985,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public int getPermissionGrantState(ComponentName admin, String callerPackage, String packageName, String permission) throws RemoteException { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforceCanQuery(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, caller.getPackageName(), caller.getUserId()); } else { @@ -19109,14 +19123,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("token must be at least 32-byte long"); } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); } final int userId = caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19172,7 +19186,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); @@ -19180,7 +19194,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userId = caller.getUserId(); boolean result = false; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19219,14 +19233,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); } int userId = caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19268,7 +19282,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Objects.requireNonNull(token); CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); @@ -19278,7 +19292,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean result = false; final String password = passwordOrNull != null ? passwordOrNull : ""; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19309,7 +19323,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (result) { - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { DevicePolicyEventLogger .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN) .setAdmin(callerPackageName) @@ -22930,6 +22944,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_LOCATION, MANAGE_DEVICE_POLICY_LOCK, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, + MANAGE_DEVICE_POLICY_CERTIFICATES, MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, MANAGE_DEVICE_POLICY_PACKAGE_STATE, @@ -23566,6 +23581,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin); } + private EnforcingAdmin getEnforcingAdminForPackage(@Nullable ComponentName who, + String packageName, int userId) { + ActiveAdmin admin; + if (who != null) { + if (isDeviceOwner(who, userId) || isProfileOwner(who, userId)) { + synchronized (getLockObject()) { + admin = getActiveAdminUncheckedLocked(who, userId); + } + if (admin != null) { + return EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userId, admin); + } + } else { + // Check for non-DPC active admins. + admin = getActiveAdminUncheckedLocked(who, userId); + if (admin != null) { + return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin); + } + } + } + + admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId); + return EnforcingAdmin.createEnforcingAdmin(packageName, userId, admin); + } + private int getAffectedUser(boolean calledOnParent) { int callingUserId = mInjector.userHandleGetCallingUserId(); return calledOnParent ? getProfileParentId(callingUserId) : callingUserId; @@ -23621,6 +23660,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DEFAULT_KEEP_PROFILES_RUNNING_FLAG); } + private boolean isUnicornFlagEnabled() { + return false; + } + private boolean isWorkProfileTelephonyEnabled() { return isWorkProfileTelephonyDevicePolicyManagerFlagEnabled() && isWorkProfileTelephonySubscriptionManagerFlagEnabled(); diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index f05b1d47ac0b..475966ea00b8 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -20,9 +20,11 @@ import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; @@ -53,7 +55,8 @@ public final class ProfcollectForwardingService extends SystemService { public static final String LOG_TAG = "ProfcollectForwardingService"; private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); - + private static final String INTENT_UPLOAD_PROFILES = + "com.android.server.profcollect.UPLOAD_PROFILES"; private static final long BG_PROCESS_PERIOD = TimeUnit.HOURS.toMillis(4); // every 4 hours. private IProfCollectd mIProfcollect; @@ -66,6 +69,16 @@ public final class ProfcollectForwardingService extends SystemService { } }; + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == INTENT_UPLOAD_PROFILES) { + Log.d(LOG_TAG, "Received broadcast to pack and upload reports"); + packAndUploadReport(); + } + } + }; + public ProfcollectForwardingService(Context context) { super(context); @@ -73,6 +86,10 @@ public final class ProfcollectForwardingService extends SystemService { throw new AssertionError("only one service instance allowed"); } sSelfService = this; + + final IntentFilter filter = new IntentFilter(); + filter.addAction(INTENT_UPLOAD_PROFILES); + context.registerReceiver(mBroadcastReceiver, filter); } /** @@ -296,7 +313,7 @@ public final class ProfcollectForwardingService extends SystemService { } if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) { - packProfileReport(); + packAndUploadReport(); } } @@ -307,7 +324,7 @@ public final class ProfcollectForwardingService extends SystemService { }); } - private void packProfileReport() { + private void packAndUploadReport() { if (mIProfcollect == null) { return; } diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java index f788c92b24b2..46974cf72381 100644 --- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java @@ -28,6 +28,8 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; +import static com.android.server.am.ProcessList.UNKNOWN_ADJ; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -81,7 +83,7 @@ public class UidObserverControllerTest { public void testEnqueueUidChange() { int change = mUidObserverController.enqueueUidChange(null, TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, - PROCESS_CAPABILITY_ALL, 0, false); + UNKNOWN_ADJ, PROCESS_CAPABILITY_ALL, 0, false); assertEquals("expected=ACTIVE,actual=" + changeToStr(change), UidRecord.CHANGE_ACTIVE, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, @@ -91,8 +93,8 @@ public class UidObserverControllerTest { final ChangeRecord record2 = new ChangeRecord(); change = mUidObserverController.enqueueUidChange(record2, TEST_UID2, - UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, - 99, true); + UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, UNKNOWN_ADJ, + PROCESS_CAPABILITY_NONE, 99, true); assertEquals("expected=ACTIVE,actual=" + changeToStr(change), UidRecord.CHANGE_CACHED, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, @@ -101,7 +103,8 @@ public class UidObserverControllerTest { PROCESS_CAPABILITY_NONE, 99, true, record2); change = mUidObserverController.enqueueUidChange(record1, TEST_UID1, - UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false); + UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, UNKNOWN_ADJ, + PROCESS_CAPABILITY_ALL, 0, false); assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change), UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index d12741ac8bd6..cbc7dc2f2fe5 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -100,6 +100,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.am.UserState.KeyEvictedCallback; +import com.android.server.pm.UserJourneyLogger; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.wm.WindowManagerService; @@ -1074,6 +1075,8 @@ public class UserControllerTest { private final KeyguardManager mKeyguardManagerMock; private final LockPatternUtils mLockPatternUtilsMock; + private final UserJourneyLogger mUserJourneyLoggerMock; + private final Context mCtx; TestInjector(Context ctx) { @@ -1090,6 +1093,7 @@ public class UserControllerTest { mKeyguardManagerMock = mock(KeyguardManager.class); when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); mLockPatternUtilsMock = mock(LockPatternUtils.class); + mUserJourneyLoggerMock = mock(UserJourneyLogger.class); } @Override @@ -1220,6 +1224,11 @@ public class UserControllerTest { void onSystemUserVisibilityChanged(boolean visible) { Log.i(TAG, "onSystemUserVisibilityChanged(" + visible + ")"); } + + @Override + protected UserJourneyLogger getUserJourneyLogger() { + return mUserJourneyLoggerMock; + } } private static class TestHandler extends Handler { diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java index c5a9af7d909d..dcd06c9cc716 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java @@ -30,7 +30,7 @@ public class CallMetadataSyncDataTest { @Test public void call_writeToParcel_fromParcel_reconstructsSuccessfully() { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); - final long id = 5; + final String id = "5"; final String callerId = "callerId"; final byte[] appIcon = "appIcon".getBytes(); final String appName = "appName"; diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java index a488ab446aff..afddf3c9be29 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java @@ -47,24 +47,24 @@ public class CallMetadataSyncInCallServiceTest { @Test public void getCallForId_invalid() { - when(mMockCrossDeviceCall.getId()).thenReturn(-1L); - final CrossDeviceCall call = mSyncInCallService.getCallForId(-1L, + when(mMockCrossDeviceCall.getId()).thenReturn(null); + final CrossDeviceCall call = mSyncInCallService.getCallForId(null, List.of(mMockCrossDeviceCall)); assertWithMessage("Unexpectedly found a match for call id").that(call).isNull(); } @Test public void getCallForId_noMatch() { - when(mMockCrossDeviceCall.getId()).thenReturn(5L); - final CrossDeviceCall call = mSyncInCallService.getCallForId(1L, + when(mMockCrossDeviceCall.getId()).thenReturn("123abc"); + final CrossDeviceCall call = mSyncInCallService.getCallForId("abc123", List.of(mMockCrossDeviceCall)); assertWithMessage("Unexpectedly found a match for call id").that(call).isNull(); } @Test public void getCallForId_hasMatch() { - when(mMockCrossDeviceCall.getId()).thenReturn(5L); - final CrossDeviceCall call = mSyncInCallService.getCallForId(5L, + when(mMockCrossDeviceCall.getId()).thenReturn("123abc"); + final CrossDeviceCall call = mSyncInCallService.getCallForId("123abc", List.of(mMockCrossDeviceCall)); assertWithMessage("Unexpectedly did not find a match for call id").that(call).isNotNull(); } diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java index 9d42a5bf05d2..5a0646c0e0e5 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java @@ -62,9 +62,9 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT, - android.companion.Telecom.Call.SILENCE)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT, + android.companion.Telecom.SILENCE)); } @Test @@ -77,9 +77,9 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); } @Test @@ -92,8 +92,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ON_HOLD); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.TAKE_OFF_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.TAKE_OFF_HOLD)); } @Test @@ -106,8 +106,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE)); } @Test @@ -120,8 +120,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.PUT_ON_HOLD)); } @Test @@ -134,17 +134,17 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status for ringing state").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING); assertWithMessage("Wrong controls for ringing state").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT, - android.companion.Telecom.Call.SILENCE)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT, + android.companion.Telecom.SILENCE)); crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE)); assertWithMessage("Wrong status for active state").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls for active state").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); } @Test @@ -158,8 +158,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING_SILENCED); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT)); } @Test @@ -173,9 +173,9 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java index eec026ccfc8a..25b0ae486230 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java @@ -81,7 +81,7 @@ public class CrossDeviceSyncControllerTest { @Test public void processTelecomDataFromSync_createCallUpdateMessage_hasCalls() { - when(mMockCrossDeviceCall.getId()).thenReturn(5L); + when(mMockCrossDeviceCall.getId()).thenReturn("123abc"); final String callerId = "Firstname Lastname"; when(mMockCrossDeviceCall.getReadableCallerId(anyBoolean())).thenReturn(callerId); final String appName = "AppName"; @@ -90,9 +90,9 @@ public class CrossDeviceSyncControllerTest { when(mMockCrossDeviceCall.getCallingAppIcon()).thenReturn(appIcon.getBytes()); when(mMockCrossDeviceCall.getStatus()).thenReturn(android.companion.Telecom.Call.RINGING); final Set<Integer> controls = Set.of( - android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT, - android.companion.Telecom.Call.SILENCE); + android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT, + android.companion.Telecom.SILENCE); when(mMockCrossDeviceCall.getControls()).thenReturn(controls); final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage( new HashSet<>(List.of(mMockCrossDeviceCall)), @@ -103,35 +103,33 @@ public class CrossDeviceSyncControllerTest { callMetadataSyncData.getCalls()).hasSize(1); final CallMetadataSyncData.Call call = callMetadataSyncData.getCalls().stream().findAny().orElseThrow(); - assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L); + assertWithMessage("Wrong id").that(call.getId()).isEqualTo("123abc"); assertWithMessage("Wrong app icon").that(new String(call.getAppIcon())).isEqualTo(appIcon); assertWithMessage("Wrong app name").that(call.getAppName()).isEqualTo(appName); assertWithMessage("Wrong caller id").that(call.getCallerId()).isEqualTo(callerId); assertWithMessage("Wrong status").that(call.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING); assertWithMessage("Wrong controls").that(call.getControls()).isEqualTo(controls); - assertWithMessage("Unexpectedly has requests").that( - callMetadataSyncData.getRequests()).isEmpty(); } @Test public void processTelecomDataFromMessage_createCallControlMessage_hasCallControlRequest() { final byte[] data = CrossDeviceSyncController.createCallControlMessage( - /* callId= */ 5L, /* status= */ android.companion.Telecom.Call.ACCEPT); + /* callId= */ "123abc", /* status= */ android.companion.Telecom.ACCEPT); final CallMetadataSyncData callMetadataSyncData = mCrossDeviceSyncController.processTelecomDataFromSync(data); assertWithMessage("Wrong number of requests").that( callMetadataSyncData.getRequests()).hasSize(1); final CallMetadataSyncData.Call call = callMetadataSyncData.getRequests().stream().findAny().orElseThrow(); - assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L); + assertWithMessage("Wrong id").that(call.getId()).isEqualTo("123abc"); assertWithMessage("Wrong app icon").that(call.getAppIcon()).isNull(); assertWithMessage("Wrong app name").that(call.getAppName()).isNull(); assertWithMessage("Wrong caller id").that(call.getCallerId()).isNull(); assertWithMessage("Wrong status").that(call.getStatus()) .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS); assertWithMessage("Wrong controls").that(call.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT)); assertWithMessage("Unexpectedly has active calls").that( callMetadataSyncData.getCalls()).isEmpty(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 1e342f580745..57755a9525fc 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1512,6 +1512,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Validates that when the device owner is removed, the reset password token is cleared */ @Test + @Ignore("b/277916462") public void testClearDeviceOwner_clearResetPasswordToken() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -2602,6 +2603,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetApplicationHiddenWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2627,6 +2629,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception { final int MANAGED_PROFILE_USER_ID = CALLER_USER_HANDLE; final int MANAGED_PROFILE_ADMIN_UID = @@ -4373,6 +4376,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4384,6 +4388,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; setupProfileOwnerOnUser0(); @@ -4395,6 +4400,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, @@ -4404,6 +4410,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -5377,6 +5384,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testResetPasswordWithToken() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5411,6 +5419,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void resetPasswordWithToken_NumericPin() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5431,6 +5440,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void resetPasswordWithToken_EmptyPassword() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -7251,6 +7261,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testCanProfileOwnerResetPasswordWhenLocked() throws Exception { setDeviceEncryptionPerUser(); setupProfileOwner(); @@ -7314,6 +7325,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception { setupProfileOwner(); @@ -7333,6 +7345,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile() throws Exception { mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java new file mode 100644 index 000000000000..20e2692cb747 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java @@ -0,0 +1,580 @@ +/* + * 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.pm; + +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INCOMPLETE_OR_TIMEOUT; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_NULL_USER_INFO; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_CANCEL; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_ERROR; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_CREATE_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_REMOVE_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_REVOKE_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_START_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_STOP_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.pm.UserInfo; +import android.platform.test.annotations.Presubmit; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.FrameworkStatsLog; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class UserJourneyLoggerTest { + + public static final int FULL_USER_ADMIN_FLAG = 0x00000402; + private UserJourneyLogger mUserJourneyLogger; + + @Before + public void setup() throws Exception { + mUserJourneyLogger = spy(new UserJourneyLogger()); + } + + @Test + public void testUserStartLifecycleJourneyReported() { + final UserLifecycleJourneyReportedCaptor report1 = new UserLifecycleJourneyReportedCaptor(); + final UserJourneyLogger.UserJourneySession session = new UserJourneyLogger + .UserJourneySession(10, USER_JOURNEY_USER_START); + + report1.captureLogAndAssert(mUserJourneyLogger, session, + USER_JOURNEY_USER_START, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, 1, + ERROR_CODE_UNSPECIFIED); + } + + + @Test + public void testUserLifecycleEventOccurred() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = new UserJourneyLogger + .UserJourneySession(10, USER_JOURNEY_USER_START); + + report1.captureLogAndAssert(mUserJourneyLogger, session, 0, + USER_LIFECYCLE_EVENT_START_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED); + } + + @Test + public void testLogUserLifecycleEvent() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_START_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logUserLifecycleEvent(10, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + EVENT_STATE_NONE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + EVENT_STATE_NONE, ERROR_CODE_UNSPECIFIED, 2); + } + + + @Test + public void testCreateUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(-1, USER_JOURNEY_USER_CREATE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, -1, + USER_LIFECYCLE_EVENT_CREATE_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserCreateJourneyFinish(0, targetUser); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_CREATE_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_CREATE, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testRemoveUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_REMOVE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REMOVE_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_USER_REMOVE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REMOVE_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_REMOVE, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testStartUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_START, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testStopUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_STOP, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testAbortStopUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + + mUserJourneyLogger.logUserJourneyFinishWithError(-1, targetUser, + USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED); + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_CANCEL, ERROR_CODE_ABORTED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_STOP, -1, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_ABORTED, 1); + } + + @Test + public void testIncompleteStopUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.finishAndClearIncompleteUserJourney(10, USER_JOURNEY_USER_STOP); + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_ERROR, + ERROR_CODE_INCOMPLETE_OR_TIMEOUT, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_STOP, -1, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN, + -1, // information about user are incomplete + ERROR_CODE_INCOMPLETE_OR_TIMEOUT, 1); + } + + @Test + public void testGrantAdminUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_GRANT_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_GRANT_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_GRANT_ADMIN, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testNullUserErrorGrantAdminUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + + UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_GRANT_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN, + 0, 10, "", -1); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_ERROR, ERROR_CODE_NULL_USER_INFO, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_GRANT_ADMIN, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN, + -1, ERROR_CODE_NULL_USER_INFO, 1); + } + + @Test + public void testRevokeAdminUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REVOKE_ADMIN, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REVOKE_ADMIN, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000400, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testSwitchFGUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger + .logUserJourneyBegin(11, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(11, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(10, targetUser, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 3); + + report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId, + USER_JOURNEY_USER_START, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 4); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 2); + } + + + @Test + public void testSwitchUIUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger + .logUserJourneyBegin(11, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + UserInfo targetUser = new UserInfo(11, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(10, targetUser, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 3); + + report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId, + USER_JOURNEY_USER_START, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_FINISH, + ERROR_CODE_UNSPECIFIED, 4); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 2); + } + + + @Test + public void testSwitchWithStopUIUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + + // BEGIN USER SWITCH + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + // BEGIN USER STOP + final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2); + + // BEGIN USER START + UserJourneyLogger.UserJourneySession session3 = mUserJourneyLogger + .logUserJourneyBegin(11, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session3.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 3); + + + // FINISH USER STOP + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(-1, targetUser, + USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 4); + + report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId, + USER_JOURNEY_USER_STOP, -1, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + FULL_USER_ADMIN_FLAG, ERROR_CODE_UNSPECIFIED, 1); + + // FINISH USER START + final UserInfo targetUser2 = new UserInfo(11, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(10, targetUser2, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session3.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 5); + + report2.captureAndAssert(mUserJourneyLogger, session3.mSessionId, + USER_JOURNEY_USER_START, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + FULL_USER_ADMIN_FLAG, ERROR_CODE_UNSPECIFIED, 2); + + + // FINISH USER SWITCH + mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser2); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 6); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 3); + } + + static class UserLifecycleJourneyReportedCaptor { + ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor<Integer> mJourney = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mOriginalUserId = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mTargetUserId = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mUserType = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mUserFlags = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class); + + public void captureAndAssert(UserJourneyLogger mUserJourneyLogger, + long sessionId, int journey, int originalUserId, + int targetUserId, int userType, int userFlags, int errorCode, int times) { + verify(mUserJourneyLogger, times(times)) + .writeUserLifecycleJourneyReported(mSessionId.capture(), + mJourney.capture(), + mOriginalUserId.capture(), + mTargetUserId.capture(), + mUserType.capture(), + mUserFlags.capture(), + mErrorCode.capture()); + + assertThat(mSessionId.getValue()).isEqualTo(sessionId); + assertThat(mJourney.getValue()).isEqualTo(journey); + assertThat(mOriginalUserId.getValue()).isEqualTo(originalUserId); + assertThat(mTargetUserId.getValue()).isEqualTo(targetUserId); + assertThat(mUserType.getValue()).isEqualTo(userType); + assertThat(mUserFlags.getValue()).isEqualTo(userFlags); + assertThat(mErrorCode.getValue()).isEqualTo(errorCode); + } + + + public void captureLogAndAssert(UserJourneyLogger mUserJourneyLogger, + UserJourneyLogger.UserJourneySession session, int journey, int originalUserId, + int targetUserId, int userType, int userFlags, int errorCode) { + mUserJourneyLogger.logUserLifecycleJourneyReported(session, journey, originalUserId, + targetUserId, userType, userFlags, errorCode); + + captureAndAssert(mUserJourneyLogger, session.mSessionId, journey, originalUserId, + targetUserId, userType, userFlags, errorCode, 1); + } + } + + + static class UserLifecycleEventOccurredCaptor { + ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor<Integer> mTargetUserId = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mEvent = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mStste = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class); + + + public void captureAndAssert(UserJourneyLogger mUserJourneyLogger, + long sessionId, int targetUserId, int event, int state, int errorCode, int times) { + verify(mUserJourneyLogger, times(times)) + .writeUserLifecycleEventOccurred(mSessionId.capture(), + mTargetUserId.capture(), + mEvent.capture(), + mStste.capture(), + mErrorCode.capture()); + + assertThat(mSessionId.getValue()).isEqualTo(sessionId); + assertThat(mTargetUserId.getValue()).isEqualTo(targetUserId); + assertThat(mEvent.getValue()).isEqualTo(event); + assertThat(mStste.getValue()).isEqualTo(state); + assertThat(mErrorCode.getValue()).isEqualTo(errorCode); + } + + + public void captureLogAndAssert(UserJourneyLogger mUserJourneyLogger, + UserJourneyLogger.UserJourneySession session, int targetUserId, int event, + int state, int errorCode) { + mUserJourneyLogger.logUserLifecycleEventOccurred(session, targetUserId, event, + state, errorCode); + + captureAndAssert(mUserJourneyLogger, session.mSessionId, targetUserId, event, + state, errorCode, 1); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 52bf244e99fb..ae3ceb1203f8 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; +import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; @@ -41,6 +42,7 @@ import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -112,6 +114,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; @@ -2468,4 +2471,18 @@ public class PowerManagerServiceTest { verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(), anyInt(), any(), any(), same(callback2)); } + + @Test + public void testUserActivity_futureEventsAreIgnored() { + createService(); + startSystem(); + // Starting the system triggers a user activity event, so clear that before calling + // userActivity() directly. + clearInvocations(mNotifierMock); + final long eventTime = mClock.now() + Duration.ofHours(10).toMillis(); + mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, eventTime, + USER_ACTIVITY_EVENT_BUTTON, /* flags= */ 0); + verify(mNotifierMock, never()).onUserActivity(anyInt(), anyInt(), anyInt()); + } + } diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/DeviceStateHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/DeviceStateHandlerTest.java new file mode 100644 index 000000000000..089bd454bfb8 --- /dev/null +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/DeviceStateHandlerTest.java @@ -0,0 +1,261 @@ +/* + * 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.soundtrigger; + +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY; + +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState.*; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.SystemClock; + +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.utils.EventLogger; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public final class DeviceStateHandlerTest { + private final long CONFIRM_NO_EVENT_WAIT_MS = 1000; + // A wait substantially less than the duration we delay phone notifications by + private final long PHONE_DELAY_BRIEF_WAIT_MS = + DeviceStateHandler.CALL_INACTIVE_MSG_DELAY_MS / 4; + + private DeviceStateHandler mHandler; + private DeviceStateHandler.DeviceStateListener mDeviceStateCallback; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private SoundTriggerDeviceState mState; + + @GuardedBy("mLock") + private CountDownLatch mLatch; + + private EventLogger mEventLogger; + + @Before + public void setup() { + // Reset the state prior to each test + mEventLogger = new EventLogger(256, "test logger"); + synchronized (mLock) { + mLatch = new CountDownLatch(1); + } + mDeviceStateCallback = + (SoundTriggerDeviceState state) -> { + synchronized (mLock) { + mState = state; + mLatch.countDown(); + } + }; + mHandler = new DeviceStateHandler(Runnable::run, mEventLogger); + mHandler.onPhoneCallStateChanged(false); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + mHandler.registerListener(mDeviceStateCallback); + try { + waitAndAssertState(ENABLE); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void waitAndAssertState(SoundTriggerDeviceState state) throws InterruptedException { + CountDownLatch latch; + synchronized (mLock) { + latch = mLatch; + } + latch.await(); + synchronized (mLock) { + assertThat(mState).isEqualTo(state); + mLatch = new CountDownLatch(1); + } + } + + private void waitToConfirmNoEventReceived() throws InterruptedException { + CountDownLatch latch; + synchronized (mLock) { + latch = mLatch; + } + // Check that we time out + assertThat(latch.await(CONFIRM_NO_EVENT_WAIT_MS, TimeUnit.MILLISECONDS)).isFalse(); + } + + @Test + public void onPowerModeChangedCritical_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitAndAssertState(ENABLE); + } + + @Test + public void onPowerModeChangedDisabled_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitAndAssertState(ENABLE); + } + + @Test + public void onPowerModeChangedMultiple_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + } + + @Test + public void onPowerModeSameState_noStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitAndAssertState(ENABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitToConfirmNoEventReceived(); + } + + @Test + public void onPhoneCall_receiveStateChange() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(ENABLE); + } + + @Test + public void onPhoneCall_receiveStateChangeIsDelayed() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + long beforeTime = SystemClock.uptimeMillis(); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(ENABLE); + long afterTime = SystemClock.uptimeMillis(); + assertThat(afterTime - beforeTime).isAtLeast(DeviceStateHandler.CALL_INACTIVE_MSG_DELAY_MS); + } + + @Test + public void onPhoneCallEnterExitEnter_receiveNoStateChange() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + SystemClock.sleep(PHONE_DELAY_BRIEF_WAIT_MS); + mHandler.onPhoneCallStateChanged(true); + waitToConfirmNoEventReceived(); + } + + @Test + public void onBatteryCallbackDuringPhoneWait_receiveStateChangeDelayed() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + SystemClock.sleep(PHONE_DELAY_BRIEF_WAIT_MS); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + // Ensure we don't get an ENABLE event after + waitToConfirmNoEventReceived(); + } + + @Test + public void onBatteryChangeWhenInPhoneCall_receiveNoStateChange() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitToConfirmNoEventReceived(); + } + + @Test + public void whenBatteryCriticalChangeDuringCallAfterPhoneCall_receiveCriticalStateChange() + throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitToConfirmNoEventReceived(); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(CRITICAL); + } + + @Test + public void whenBatteryDisableDuringCallAfterPhoneCallBatteryEnable_receiveStateChange() + throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitToConfirmNoEventReceived(); + mHandler.onPhoneCallStateChanged(false); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + } + + @Test + public void whenPhoneCallDuringBatteryDisable_receiveNoStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(true); + waitToConfirmNoEventReceived(); + mHandler.onPhoneCallStateChanged(false); + waitToConfirmNoEventReceived(); + } + + @Test + public void whenPhoneCallDuringBatteryCritical_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(CRITICAL); + } + + // This test could be flaky, but we want to verify that we only delay notification if + // we are exiting a call, NOT if we are entering a call. + @FlakyTest + @Test + public void whenPhoneCallReceived_receiveStateChangeFast() throws Exception { + mHandler.onPhoneCallStateChanged(true); + CountDownLatch latch; + synchronized (mLock) { + latch = mLatch; + } + assertThat(latch.await(PHONE_DELAY_BRIEF_WAIT_MS, TimeUnit.MILLISECONDS)).isTrue(); + synchronized (mLock) { + assertThat(mState).isEqualTo(DISABLE); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index ad606cb90841..2d8ddfa47300 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -1443,10 +1443,8 @@ public class WindowContainerTests extends WindowTestsBase { final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect2); - final int sourceId1 = InsetsSource.createId( - provider1.getOwner(), provider1.getIndex(), provider1.getType()); - final int sourceId2 = InsetsSource.createId( - provider2.getOwner(), provider2.getIndex(), provider2.getType()); + final int sourceId1 = provider1.getId(); + final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); container.addLocalInsetsFrameProvider(provider2); @@ -1504,10 +1502,8 @@ public class WindowContainerTests extends WindowTestsBase { final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect2); - final int sourceId1 = InsetsSource.createId( - provider1.getOwner(), provider1.getIndex(), provider1.getType()); - final int sourceId2 = InsetsSource.createId( - provider2.getOwner(), provider2.getIndex(), provider2.getType()); + final int sourceId1 = provider1.getId(); + final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); activity0.forAllWindows(window -> { @@ -1566,10 +1562,8 @@ public class WindowContainerTests extends WindowTestsBase { final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(navigationBarInsetsRect2); - final int sourceId1 = InsetsSource.createId( - provider1.getOwner(), provider1.getIndex(), provider1.getType()); - final int sourceId2 = InsetsSource.createId( - provider2.getOwner(), provider2.getIndex(), provider2.getType()); + final int sourceId1 = provider1.getId(); + final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); container.addLocalInsetsFrameProvider(provider2); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index a63807d23a14..a4cad5e24dc1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -359,9 +359,12 @@ class WindowTestsBase extends SystemServiceTestsBase { new InsetsFrameProvider(owner, 0, WindowInsets.Type.tappableElement()), new InsetsFrameProvider(owner, 0, WindowInsets.Type.mandatorySystemGestures()) }; - for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { - mNavBarWindow.mAttrs.paramsForRotation[rot] = - getNavBarLayoutParamsForRotation(rot, owner); + // If the navigation bar cannot move then it is always at the bottom. + if (mDisplayContent.getDisplayPolicy().navigationBarCanMove()) { + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + mNavBarWindow.mAttrs.paramsForRotation[rot] = + getNavBarLayoutParamsForRotation(rot, owner); + } } } if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) { diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/DeviceStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/DeviceStateHandler.java new file mode 100644 index 000000000000..66054494c277 --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/DeviceStateHandler.java @@ -0,0 +1,279 @@ +/** + * 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.soundtrigger; + +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.utils.EventLogger; + +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Manages device state events which require pausing SoundTrigger recognition + * + * @hide + */ +public class DeviceStateHandler implements PhoneCallStateHandler.Callback { + + public static final long CALL_INACTIVE_MSG_DELAY_MS = 1000; + + public interface DeviceStateListener { + void onSoundTriggerDeviceStateUpdate(SoundTriggerDeviceState state); + } + + public enum SoundTriggerDeviceState { + DISABLE, // The device state requires all SoundTrigger sessions are disabled + CRITICAL, // The device state requires all non-critical SoundTrigger sessions are disabled + ENABLE // The device state permits all SoundTrigger sessions + } + + private final Object mLock = new Object(); + + private final EventLogger mEventLogger; + + @GuardedBy("mLock") + SoundTriggerDeviceState mSoundTriggerDeviceState = SoundTriggerDeviceState.ENABLE; + + // Individual components of the SoundTriggerDeviceState + @GuardedBy("mLock") + private int mSoundTriggerPowerSaveMode = SOUND_TRIGGER_MODE_ALL_ENABLED; + + @GuardedBy("mLock") + private boolean mIsPhoneCallOngoing = false; + + // There can only be one pending notify at any given time. + // If any phone state change comes in between, we will cancel the previous pending + // task. + @GuardedBy("mLock") + private NotificationTask mPhoneStateChangePendingNotify = null; + + private Set<DeviceStateListener> mCallbackSet = ConcurrentHashMap.newKeySet(4); + + private final Executor mDelayedNotificationExecutor = Executors.newSingleThreadExecutor(); + + private final Executor mCallbackExecutor; + + public void onPowerModeChanged(int soundTriggerPowerSaveMode) { + mEventLogger.enqueue(new SoundTriggerPowerEvent(soundTriggerPowerSaveMode)); + synchronized (mLock) { + if (soundTriggerPowerSaveMode == mSoundTriggerPowerSaveMode) { + // No state change, nothing to do + return; + } + mSoundTriggerPowerSaveMode = soundTriggerPowerSaveMode; + evaluateStateChange(); + } + } + + @Override + public void onPhoneCallStateChanged(boolean isInPhoneCall) { + mEventLogger.enqueue(new PhoneCallEvent(isInPhoneCall)); + synchronized (mLock) { + if (mIsPhoneCallOngoing == isInPhoneCall) { + // no change, nothing to do + return; + } + // Clear any pending notification + if (mPhoneStateChangePendingNotify != null) { + mPhoneStateChangePendingNotify.cancel(); + mPhoneStateChangePendingNotify = null; + } + mIsPhoneCallOngoing = isInPhoneCall; + if (!mIsPhoneCallOngoing) { + // State has changed from call to no call, delay notification + mPhoneStateChangePendingNotify = new NotificationTask( + new Runnable() { + @Override + public void run() { + synchronized (mLock) { + if (mPhoneStateChangePendingNotify != null && + mPhoneStateChangePendingNotify.runnableEquals(this)) { + + mPhoneStateChangePendingNotify = null; + evaluateStateChange(); + } + } + } + }, + CALL_INACTIVE_MSG_DELAY_MS); + mDelayedNotificationExecutor.execute(mPhoneStateChangePendingNotify); + } else { + evaluateStateChange(); + } + } + } + + /** Note, we expect initial callbacks immediately following construction */ + public DeviceStateHandler(Executor callbackExecutor, EventLogger eventLogger) { + mCallbackExecutor = Objects.requireNonNull(callbackExecutor); + mEventLogger = Objects.requireNonNull(eventLogger); + } + + public SoundTriggerDeviceState getDeviceState() { + synchronized (mLock) { + return mSoundTriggerDeviceState; + } + } + + public void registerListener(DeviceStateListener callback) { + final var state = getDeviceState(); + mCallbackExecutor.execute( + () -> callback.onSoundTriggerDeviceStateUpdate(state)); + mCallbackSet.add(callback); + } + + public void unregisterListener(DeviceStateListener callback) { + mCallbackSet.remove(callback); + } + + void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println("DeviceState: " + mSoundTriggerDeviceState.name()); + pw.println("PhoneState: " + mIsPhoneCallOngoing); + pw.println("PowerSaveMode: " + mSoundTriggerPowerSaveMode); + } + } + + @GuardedBy("mLock") + private void evaluateStateChange() { + // We should wait until any pending delays are complete to update. + // We will eventually get called by the notification task, or something which + // cancels it. + // Additionally, if there isn't a state change, there is nothing to update. + SoundTriggerDeviceState newState = computeState(); + if (mPhoneStateChangePendingNotify != null || mSoundTriggerDeviceState == newState) { + return; + } + + mSoundTriggerDeviceState = newState; + mEventLogger.enqueue(new DeviceStateEvent(mSoundTriggerDeviceState)); + final var state = mSoundTriggerDeviceState; + for (var callback : mCallbackSet) { + mCallbackExecutor.execute( + () -> callback.onSoundTriggerDeviceStateUpdate(state)); + } + } + + @GuardedBy("mLock") + private SoundTriggerDeviceState computeState() { + if (mIsPhoneCallOngoing) { + return SoundTriggerDeviceState.DISABLE; + } + return switch (mSoundTriggerPowerSaveMode) { + case SOUND_TRIGGER_MODE_ALL_ENABLED -> SoundTriggerDeviceState.ENABLE; + case SOUND_TRIGGER_MODE_CRITICAL_ONLY -> SoundTriggerDeviceState.CRITICAL; + case SOUND_TRIGGER_MODE_ALL_DISABLED -> SoundTriggerDeviceState.DISABLE; + default -> throw new IllegalStateException( + "Received unexpected power state code" + mSoundTriggerPowerSaveMode); + }; + } + + /** + * One-shot, cancellable task which runs after a delay. Run must only be called once, from a + * single thread. Cancel can be called from any other thread. + */ + private static class NotificationTask implements Runnable { + private final Runnable mRunnable; + private final long mWaitInMillis; + + private final CountDownLatch mCancelLatch = new CountDownLatch(1); + + NotificationTask(Runnable r, long waitInMillis) { + mRunnable = r; + mWaitInMillis = waitInMillis; + } + + void cancel() { + mCancelLatch.countDown(); + } + + // Used for determining task equality. + boolean runnableEquals(Runnable runnable) { + return mRunnable == runnable; + } + + public void run() { + try { + if (!mCancelLatch.await(mWaitInMillis, TimeUnit.MILLISECONDS)) { + mRunnable.run(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError("Unexpected InterruptedException", e); + } + } + } + + private static class PhoneCallEvent extends EventLogger.Event { + final boolean mIsInPhoneCall; + + PhoneCallEvent(boolean isInPhoneCall) { + mIsInPhoneCall = isInPhoneCall; + } + + @Override + public String eventToString() { + return "PhoneCallChange - inPhoneCall: " + mIsInPhoneCall; + } + } + + private static class SoundTriggerPowerEvent extends EventLogger.Event { + final int mSoundTriggerPowerState; + + SoundTriggerPowerEvent(int soundTriggerPowerState) { + mSoundTriggerPowerState = soundTriggerPowerState; + } + + @Override + public String eventToString() { + return "SoundTriggerPowerChange: " + stateToString(); + } + + private String stateToString() { + return switch (mSoundTriggerPowerState) { + case SOUND_TRIGGER_MODE_ALL_ENABLED -> "All enabled"; + case SOUND_TRIGGER_MODE_CRITICAL_ONLY -> "Critical only"; + case SOUND_TRIGGER_MODE_ALL_DISABLED -> "All disabled"; + default -> "Unknown power state: " + mSoundTriggerPowerState; + }; + } + } + + private static class DeviceStateEvent extends EventLogger.Event { + final SoundTriggerDeviceState mSoundTriggerDeviceState; + + DeviceStateEvent(SoundTriggerDeviceState soundTriggerDeviceState) { + mSoundTriggerDeviceState = soundTriggerDeviceState; + } + + @Override + public String eventToString() { + return "DeviceStateChange: " + mSoundTriggerDeviceState.name(); + } + } +} diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java new file mode 100644 index 000000000000..8773cabeeb92 --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java @@ -0,0 +1,158 @@ +/** + * 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.soundtrigger; + +import android.telephony.Annotation; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Handles monitoring telephony call state across active subscriptions. + * + * @hide + */ +public class PhoneCallStateHandler { + + public interface Callback { + void onPhoneCallStateChanged(boolean isInPhoneCall); + } + + private final Object mLock = new Object(); + + // Actually never contended due to executor. + @GuardedBy("mLock") + private final List<MyCallStateListener> mListenerList = new ArrayList<>(); + + private final AtomicBoolean mIsPhoneCallOngoing = new AtomicBoolean(false); + + private final SubscriptionManager mSubscriptionManager; + private final TelephonyManager mTelephonyManager; + private final Callback mCallback; + + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + + public PhoneCallStateHandler( + SubscriptionManager subscriptionManager, + TelephonyManager telephonyManager, + Callback callback) { + mSubscriptionManager = Objects.requireNonNull(subscriptionManager); + mTelephonyManager = Objects.requireNonNull(telephonyManager); + mCallback = Objects.requireNonNull(callback); + mSubscriptionManager.addOnSubscriptionsChangedListener( + mExecutor, + new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + updateTelephonyListeners(); + } + + @Override + public void onAddListenerFailed() { + Slog.wtf( + "SoundTriggerPhoneCallStateHandler", + "Failed to add a telephony listener"); + } + }); + } + + private final class MyCallStateListener extends TelephonyCallback + implements TelephonyCallback.CallStateListener { + + final TelephonyManager mTelephonyManagerForSubId; + + // Manager corresponding to the sub-id + MyCallStateListener(TelephonyManager telephonyManager) { + mTelephonyManagerForSubId = telephonyManager; + } + + void cleanup() { + mExecutor.execute(() -> mTelephonyManagerForSubId.unregisterTelephonyCallback(this)); + } + + @Override + public void onCallStateChanged(int unused) { + updateCallStatus(); + } + } + + /** Compute the current call status, and dispatch callback if it has changed. */ + private void updateCallStatus() { + boolean callStatus = checkCallStatus(); + if (mIsPhoneCallOngoing.compareAndSet(!callStatus, callStatus)) { + mCallback.onPhoneCallStateChanged(callStatus); + } + } + + /** + * Synchronously query the current telephony call state across all subscriptions + * + * @return - {@code true} if in call, {@code false} if not in call. + */ + private boolean checkCallStatus() { + List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (infoList == null) return false; + return infoList.stream() + .filter(s -> (s.getSubscriptionId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) + .anyMatch(s -> isCallOngoingFromState( + mTelephonyManager + .createForSubscriptionId(s.getSubscriptionId()) + .getCallStateForSubscription())); + } + + private void updateTelephonyListeners() { + synchronized (mLock) { + for (var listener : mListenerList) { + listener.cleanup(); + } + mListenerList.clear(); + List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (infoList == null) return; + infoList.stream() + .filter(s -> s.getSubscriptionId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID) + .map(s -> mTelephonyManager.createForSubscriptionId(s.getSubscriptionId())) + .forEach(manager -> { + synchronized (mLock) { + var listener = new MyCallStateListener(manager); + mListenerList.add(listener); + manager.registerTelephonyCallback(mExecutor, listener); + } + }); + } + } + + private static boolean isCallOngoingFromState(@Annotation.CallState int callState) { + return switch (callState) { + case TelephonyManager.CALL_STATE_IDLE, TelephonyManager.CALL_STATE_RINGING -> false; + case TelephonyManager.CALL_STATE_OFFHOOK -> true; + default -> throw new IllegalStateException( + "Received unexpected call state from Telephony Manager: " + callState); + }; + } +} diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 255db1e4db83..b4066ab1ff39 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -16,15 +16,12 @@ package com.android.server.soundtrigger; +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; import static com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent.Type; import static com.android.server.utils.EventLogger.Event.ALOGW; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.ModelParams; import android.hardware.soundtrigger.SoundTrigger; @@ -45,11 +42,7 @@ import android.os.DeadObjectException; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.SoundTriggerPowerSaveMode; import android.os.RemoteException; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -99,37 +92,20 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private SoundTriggerModule mModule; private final Object mLock = new Object(); private final Context mContext; - private final TelephonyManager mTelephonyManager; - private final PhoneStateListener mPhoneStateListener; - private final PowerManager mPowerManager; // The SoundTriggerManager layer handles multiple recognition models of type generic and // keyphrase. We store the ModelData here in a hashmap. - private final HashMap<UUID, ModelData> mModelDataMap; + private final HashMap<UUID, ModelData> mModelDataMap = new HashMap<>(); // An index of keyphrase sound models so that we can reach them easily. We support indexing // keyphrase sound models with a keyphrase ID. Sound model with the same keyphrase ID will // replace an existing model, thus there is a 1:1 mapping from keyphrase ID to a voice // sound model. - private HashMap<Integer, UUID> mKeyphraseUuidMap; - - private boolean mCallActive = false; - private @SoundTriggerPowerSaveMode int mSoundTriggerPowerSaveMode = - PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; + private final HashMap<Integer, UUID> mKeyphraseUuidMap = new HashMap<>(); // Whether ANY recognition (keyphrase or generic) has been requested. private boolean mRecognitionRequested = false; - private PowerSaveModeListener mPowerSaveModeListener; - - - // Handler to process call state changes will delay to allow time for the audio - // and sound trigger HALs to process the end of call notifications - // before we re enable pending recognition requests. - private final Handler mHandler; - private static final int MSG_CALL_STATE_CHANGED = 0; - private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000; - // TODO(b/269366605) Temporary solution to query correct moduleProperties private final int mModuleId; private final Function<SoundTrigger.StatusListener, SoundTriggerModule> mModuleProvider; @@ -139,16 +115,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { @GuardedBy("mLock") private boolean mIsDetached = false; + @GuardedBy("mLock") + private SoundTriggerDeviceState mDeviceState = SoundTriggerDeviceState.DISABLE; + SoundTriggerHelper(Context context, EventLogger eventLogger, @NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider, int moduleId, @NonNull Supplier<List<ModuleProperties>> modulePropertiesProvider) { mModuleId = moduleId; mContext = context; - mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mModelDataMap = new HashMap<UUID, ModelData>(); - mKeyphraseUuidMap = new HashMap<Integer, UUID>(); mModuleProvider = moduleProvider; mEventLogger = eventLogger; mModulePropertiesProvider = modulePropertiesProvider; @@ -157,31 +132,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } else { mModule = mModuleProvider.apply(this); } - Looper looper = Looper.myLooper(); - if (looper == null) { - looper = Looper.getMainLooper(); - } - mPhoneStateListener = new MyCallStateListener(looper); - if (looper != null) { - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CALL_STATE_CHANGED: - synchronized (mLock) { - onCallStateChangedLocked( - TelephonyManager.CALL_STATE_OFFHOOK == msg.arg1); - } - break; - default: - Slog.e(TAG, "unknown message in handler:" + msg.what); - break; - } - } - }; - } else { - mHandler = null; - } } /** @@ -373,7 +323,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setSoundModel(soundModel); if (!isRecognitionAllowedByDeviceState(modelData)) { - initializeDeviceStateListeners(); return STATUS_OK; } @@ -497,11 +446,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setLoaded(); modelData.clearCallback(); modelData.setRecognitionConfig(null); - - if (!computeRecognitionRequestedLocked()) { - internalClearGlobalStateLocked(); - } - return status; } } @@ -638,6 +582,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } + public void onDeviceStateChanged(SoundTriggerDeviceState state) { + synchronized (mLock) { + if (mIsDetached || mDeviceState == state) { + // Nothing to update + return; + } + mDeviceState = state; + updateAllRecognitionsLocked(); + } + } + public int getGenericModelState(UUID modelId) { synchronized (mLock) { MetricsLogger.count(mContext, "sth_get_generic_model_state", 1); @@ -880,25 +835,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - private void onCallStateChangedLocked(boolean callActive) { - if (mCallActive == callActive) { - // We consider multiple call states as being active - // so we check if something really changed or not here. - return; - } - mCallActive = callActive; - updateAllRecognitionsLocked(); - } - - private void onPowerSaveModeChangedLocked( - @SoundTriggerPowerSaveMode int soundTriggerPowerSaveMode) { - if (mSoundTriggerPowerSaveMode == soundTriggerPowerSaveMode) { - return; - } - mSoundTriggerPowerSaveMode = soundTriggerPowerSaveMode; - updateAllRecognitionsLocked(); - } - private void onModelUnloadedLocked(int modelHandle) { ModelData modelData = getModelDataForLocked(modelHandle); if (modelData != null) { @@ -1011,10 +947,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return status; } status = startRecognitionLocked(model, notifyClientOnError); - // Initialize power save, call active state monitoring logic. - if (status == STATUS_OK) { - initializeDeviceStateListeners(); - } return status; } else { return stopRecognitionLocked(model, notifyClientOnError); @@ -1040,7 +972,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } finally { internalClearModelStateLocked(); - internalClearGlobalStateLocked(); if (mModule != null) { mModule.detach(); try { @@ -1054,24 +985,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - // internalClearGlobalStateLocked() cleans up the telephony and power save listeners. - private void internalClearGlobalStateLocked() { - // Unregister from call state changes. - final long token = Binder.clearCallingIdentity(); - try { - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); - } finally { - Binder.restoreCallingIdentity(token); - } - - // Unregister from power save mode changes. - if (mPowerSaveModeListener != null) { - mContext.unregisterReceiver(mPowerSaveModeListener); - mPowerSaveModeListener = null; - } - mRecognitionRequested = false; - } - // Clears state for all models (generic and keyphrase). private void internalClearModelStateLocked() { for (ModelData modelData : mModelDataMap.values()) { @@ -1079,67 +992,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - class MyCallStateListener extends PhoneStateListener { - MyCallStateListener(@NonNull Looper looper) { - super(Objects.requireNonNull(looper)); - } - - @Override - public void onCallStateChanged(int state, String arg1) { - - if (mHandler != null) { - synchronized (mLock) { - mHandler.removeMessages(MSG_CALL_STATE_CHANGED); - Message msg = mHandler.obtainMessage(MSG_CALL_STATE_CHANGED, state, 0); - mHandler.sendMessageDelayed( - msg, (TelephonyManager.CALL_STATE_OFFHOOK == state) ? 0 - : CALL_INACTIVE_MSG_DELAY_MS); - } - } - } - } - - class PowerSaveModeListener extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { - return; - } - @SoundTriggerPowerSaveMode int soundTriggerPowerSaveMode = - mPowerManager.getSoundTriggerPowerSaveMode(); - synchronized (mLock) { - onPowerSaveModeChangedLocked(soundTriggerPowerSaveMode); - } - } - } - - private void initializeDeviceStateListeners() { - if (mRecognitionRequested) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - // Get the current call state synchronously for the first recognition. - mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK; - - // Register for call state changes when the first call to start recognition occurs. - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - - // Register for power saver mode changes when the first call to start recognition - // occurs. - if (mPowerSaveModeListener == null) { - mPowerSaveModeListener = new PowerSaveModeListener(); - mContext.registerReceiver(mPowerSaveModeListener, - new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); - } - mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode(); - - mRecognitionRequested = true; - } finally { - Binder.restoreCallingIdentity(token); - } - } - /** * Stops and unloads all models. This is intended as a clean-up call with the expectation that * this instance is not used after. @@ -1153,7 +1005,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { forceStopAndUnloadModelLocked(model, null); } mModelDataMap.clear(); - internalClearGlobalStateLocked(); if (mModule != null) { mModule.detach(); mModule = null; @@ -1305,28 +1156,14 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { * @param modelData Model data to be used for recognition * @return True if recognition is allowed to run at this time. False if not. */ + @GuardedBy("mLock") private boolean isRecognitionAllowedByDeviceState(ModelData modelData) { - // if mRecognitionRequested is false, call and power state listeners are not registered so - // we read current state directly from services - if (!mRecognitionRequested) { - mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK; - mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode(); - } - - return !mCallActive && isRecognitionAllowedByPowerState( - modelData); - } - - /** - * Helper function to validate if a recognition should run based on the current power state - * - * @param modelData Model data to be used for recognition - * @return True if device state allows recognition to run, false if not. - */ - private boolean isRecognitionAllowedByPowerState(ModelData modelData) { - return mSoundTriggerPowerSaveMode == PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED - || (mSoundTriggerPowerSaveMode == PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY - && modelData.shouldRunInBatterySaverMode()); + return switch (mDeviceState) { + case DISABLE -> false; + case CRITICAL -> modelData.shouldRunInBatterySaverMode(); + case ENABLE -> true; + default -> throw new AssertionError("Enum changed between compile and runtime"); + }; } // A single routine that implements the start recognition logic for both generic and keyphrase diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 913535e06a21..3151781ff7ba 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -35,14 +35,18 @@ import static com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent.Typ import static com.android.server.utils.EventLogger.Event.ALOGW; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import static com.android.server.soundtrigger.DeviceStateHandler.DeviceStateListener; +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.ServiceConnection; import android.content.pm.PackageManager; @@ -85,6 +89,8 @@ import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -112,6 +118,8 @@ import java.util.Objects; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -131,8 +139,8 @@ public class SoundTriggerService extends SystemService { private static final boolean DEBUG = true; private static final int SESSION_MAX_EVENT_SIZE = 128; - final Context mContext; - private Object mLock; + private final Context mContext; + private final Object mLock = new Object(); private final SoundTriggerServiceStub mServiceStub; private final LocalSoundTriggerService mLocalSoundTriggerService; @@ -140,6 +148,7 @@ public class SoundTriggerService extends SystemService { private SoundTriggerDbHelper mDbHelper; private final EventLogger mServiceEventLogger = new EventLogger(256, "Service"); + private final EventLogger mDeviceEventLogger = new EventLogger(256, "Device Event"); private final Set<EventLogger> mSessionEventLoggers = ConcurrentHashMap.newKeySet(4); private final Deque<EventLogger> mDetachedSessionEventLoggers = new LinkedBlockingDeque<>(4); @@ -223,13 +232,18 @@ public class SoundTriggerService extends SystemService { @GuardedBy("mLock") private final ArrayMap<String, NumOps> mNumOpsPerPackage = new ArrayMap<>(); + private final DeviceStateHandler mDeviceStateHandler; + private final Executor mDeviceStateHandlerExecutor = Executors.newSingleThreadExecutor(); + private PhoneCallStateHandler mPhoneCallStateHandler; + public SoundTriggerService(Context context) { super(context); mContext = context; mServiceStub = new SoundTriggerServiceStub(); mLocalSoundTriggerService = new LocalSoundTriggerService(context); - mLock = new Object(); mSoundModelStatTracker = new SoundModelStatTracker(); + mDeviceStateHandler = new DeviceStateHandler(mDeviceStateHandlerExecutor, + mDeviceEventLogger); } @Override @@ -243,6 +257,29 @@ public class SoundTriggerService extends SystemService { Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode()); if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) { mDbHelper = new SoundTriggerDbHelper(mContext); + final PowerManager powerManager = mContext.getSystemService(PowerManager.class); + // Hook up power state listener + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED + .equals(intent.getAction())) { + return; + } + mDeviceStateHandler.onPowerModeChanged( + powerManager.getSoundTriggerPowerSaveMode()); + } + }, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); + // Initialize the initial power state + // Do so after registering the listener so we ensure that we don't drop any events + mDeviceStateHandler.onPowerModeChanged(powerManager.getSoundTriggerPowerSaveMode()); + + // PhoneCallStateHandler initializes the original call state + mPhoneCallStateHandler = new PhoneCallStateHandler( + mContext.getSystemService(SubscriptionManager.class), + mContext.getSystemService(TelephonyManager.class), + mDeviceStateHandler); } mMiddlewareService = ISoundTriggerMiddlewareService.Stub.asInterface( ServiceManager.waitForService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE)); @@ -380,6 +417,9 @@ public class SoundTriggerService extends SystemService { // Event loggers pw.println("##Service-Wide logs:"); mServiceEventLogger.dump(pw, /* indent = */ " "); + pw.println("\n##Device state logs:"); + mDeviceStateHandler.dump(pw); + mDeviceEventLogger.dump(pw, /* indent = */ " "); pw.println("\n##Active Session dumps:\n"); for (var sessionLogger : mSessionEventLoggers) { @@ -403,6 +443,7 @@ public class SoundTriggerService extends SystemService { class SoundTriggerSessionStub extends ISoundTriggerSession.Stub { private final SoundTriggerHelper mSoundTriggerHelper; + private final DeviceStateListener mListener; // Used to detect client death. private final IBinder mClient; private final Identity mOriginatorIdentity; @@ -424,6 +465,9 @@ public class SoundTriggerService extends SystemService { } catch (RemoteException e) { clientDied(); } + mListener = (SoundTriggerDeviceState state) + -> mSoundTriggerHelper.onDeviceStateChanged(state); + mDeviceStateHandler.registerListener(mListener); } @Override @@ -874,6 +918,7 @@ public class SoundTriggerService extends SystemService { } private void detach() { + mDeviceStateHandler.unregisterListener(mListener); mSoundTriggerHelper.detach(); detachSessionLogger(mEventLogger); } @@ -890,7 +935,8 @@ public class SoundTriggerService extends SystemService { private void enforceDetectionPermissions(ComponentName detectionService) { PackageManager packageManager = mContext.getPackageManager(); String packageName = detectionService.getPackageName(); - if (packageManager.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName) + if (packageManager.checkPermission( + Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(detectionService.getPackageName() + " does not have" + " permission " + Manifest.permission.CAPTURE_AUDIO_HOTWORD); @@ -1576,6 +1622,7 @@ public class SoundTriggerService extends SystemService { private final @NonNull IBinder mClient; private final EventLogger mEventLogger; private final Identity mOriginatorIdentity; + private final @NonNull DeviceStateListener mListener; private final SparseArray<UUID> mModelUuid = new SparseArray<>(1); @@ -1594,6 +1641,9 @@ public class SoundTriggerService extends SystemService { } catch (RemoteException e) { clientDied(); } + mListener = (SoundTriggerDeviceState state) + -> mSoundTriggerHelper.onDeviceStateChanged(state); + mDeviceStateHandler.registerListener(mListener); } @Override @@ -1662,6 +1712,7 @@ public class SoundTriggerService extends SystemService { private void detachInternal() { mEventLogger.enqueue(new SessionEvent(Type.DETACH, null)); detachSessionLogger(mEventLogger); + mDeviceStateHandler.unregisterListener(mListener); mSoundTriggerHelper.detach(); } } diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index 7272abba897d..32ff243921ec 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -21,6 +21,9 @@ <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" /> <!-- Ensure output directory is empty at the start --> <option name="run-command" value="rm -rf /sdcard/flicker" /> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480" /> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> |