diff options
217 files changed, 3967 insertions, 3579 deletions
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 70c3d7ae3f82..f33d2991329a 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1346,8 +1346,26 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } // Set the child animators to the right end: if (mShouldResetValuesAtStart) { - initChildren(); - skipToEndValue(!mReversing); + if (isInitialized()) { + skipToEndValue(!mReversing); + } else if (mReversing) { + // Reversing but haven't initialized all the children yet. + initChildren(); + skipToEndValue(!mReversing); + } else { + // If not all children are initialized and play direction is forward + for (int i = mEvents.size() - 1; i >= 0; i--) { + if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { + Animator anim = mEvents.get(i).mNode.mAnimation; + // Only reset the animations that have been initialized to start value, + // so that if they are defined without a start value, they will get the + // values set at the right time (i.e. the next animation run) + if (anim.isInitialized()) { + anim.skipToEndValue(true); + } + } + } + } } if (mReversing || mStartDelay == 0 || mSeekState.isActive()) { diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index 3d6ab6fd9478..9a818e49ae4a 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -20,6 +20,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; /** * @hide @@ -49,4 +50,6 @@ interface IGameManagerService { void addGameModeListener(IGameModeListener gameModeListener); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void removeGameModeListener(IGameModeListener gameModeListener); + void addGameStateListener(IGameStateListener gameStateListener); + void removeGameStateListener(IGameStateListener gameStateListener); } diff --git a/core/java/android/app/IGameStateListener.aidl b/core/java/android/app/IGameStateListener.aidl new file mode 100644 index 000000000000..34cff489d077 --- /dev/null +++ b/core/java/android/app/IGameStateListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.app.GameState; + +/** @hide */ +interface IGameStateListener { + /** + * Called when the state of the game has changed. + */ + oneway void onGameStateChanged(String packageName, in GameState state, int userId); +} diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index f7d2afba428e..e1c45d98e678 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -63,10 +63,11 @@ per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS # Multiuser per-file *User* = file:/MULTIUSER_OWNERS -# Notification, DND, Status bar +# Notification, DND, Status bar, UiModeManager per-file *Notification* = file:/packages/SystemUI/OWNERS per-file *Zen* = file:/packages/SystemUI/OWNERS per-file *StatusBar* = file:/packages/SystemUI/OWNERS +per-file *UiModeManager* = file:/packages/SystemUI/OWNERS # PackageManager per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a9c4818393a8..e472a40617ee 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -103,11 +103,10 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Xml; import android.util.proto.ProtoOutputStream; -import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver; -import android.view.Choreographer; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -1052,17 +1051,22 @@ public class InputMethodService extends AbstractInputMethodService { stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent); // create receiver for channel - mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver( - channel, - Looper.getMainLooper(), Choreographer.getInstance(), - event -> { + mHandwritingEventReceiver = new InputEventReceiver(channel, Looper.getMainLooper()) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + try { if (!(event instanceof MotionEvent)) { - return false; + return; } onStylusHandwritingMotionEvent((MotionEvent) event); scheduleHandwritingSessionTimeout(); - return true; - }); + handled = true; + } finally { + finishInputEvent(event, handled); + } + } + }; scheduleHandwritingSessionTimeout(); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 3da696ad0bc7..7fbaf1027af6 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -882,10 +882,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } static Uri readFrom(Parcel parcel) { + final StringUri stringUri = new StringUri(parcel.readString8()); return new OpaqueUri( - parcel.readString8(), - Part.readFrom(parcel), - Part.readFrom(parcel) + stringUri.parseScheme(), + stringUri.getSsp(), + stringUri.getFragmentPart() ); } @@ -895,9 +896,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(TYPE_ID); - parcel.writeString8(scheme); - ssp.writeTo(parcel); - fragment.writeTo(parcel); + parcel.writeString8(toString()); } public boolean isHierarchical() { @@ -1196,22 +1195,25 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { Part query, Part fragment) { this.scheme = scheme; this.authority = Part.nonNull(authority); - this.path = path == null ? PathPart.NULL : path; + this.path = generatePath(path); this.query = Part.nonNull(query); this.fragment = Part.nonNull(fragment); } - static Uri readFrom(Parcel parcel) { - final String scheme = parcel.readString8(); - final Part authority = Part.readFrom(parcel); + private PathPart generatePath(PathPart originalPath) { // In RFC3986 the path should be determined based on whether there is a scheme or // authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3). final boolean hasSchemeOrAuthority = (scheme != null && scheme.length() > 0) || !authority.isEmpty(); - final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel); - final Part query = Part.readFrom(parcel); - final Part fragment = Part.readFrom(parcel); - return new HierarchicalUri(scheme, authority, path, query, fragment); + final PathPart newPath = hasSchemeOrAuthority ? PathPart.makeAbsolute(originalPath) + : originalPath; + return newPath == null ? PathPart.NULL : newPath; + } + + static Uri readFrom(Parcel parcel) { + final StringUri stringUri = new StringUri(parcel.readString8()); + return new HierarchicalUri(stringUri.getScheme(), stringUri.getAuthorityPart(), + stringUri.getPathPart(), stringUri.getQueryPart(), stringUri.getFragmentPart()); } public int describeContents() { @@ -1220,11 +1222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(TYPE_ID); - parcel.writeString8(scheme); - authority.writeTo(parcel); - path.writeTo(parcel); - query.writeTo(parcel); - fragment.writeTo(parcel); + parcel.writeString8(toString()); } public boolean isHierarchical() { diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index ac3344e91c06..4909b0830eeb 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -594,9 +594,6 @@ public final class CardEmulation { if (activity == null || service == null) { throw new NullPointerException("activity or service or category is null"); } - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } try { return sService.setPreferredService(service); } catch (RemoteException e) { @@ -629,9 +626,6 @@ public final class CardEmulation { if (activity == null) { throw new NullPointerException("activity is null"); } - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } try { return sService.unsetPreferredService(); } catch (RemoteException e) { diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index ff8e3a03b77b..e6bdfe1b95c4 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -116,12 +116,6 @@ public class GraphicsEnvironment { private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle"; private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native"; - private enum AngleDriverChoice { - DEFAULT, - ANGLE, - NATIVE, - } - private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported"; private ClassLoader mClassLoader; @@ -201,16 +195,15 @@ public class GraphicsEnvironment { } /** - * Query to determine the ANGLE driver choice. + * Query to determine if ANGLE should be used */ - private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings, - String packageName) { + private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) { if (TextUtils.isEmpty(packageName)) { Log.v(TAG, "No package name specified; use the system driver"); - return AngleDriverChoice.DEFAULT; + return false; } - return queryAngleChoiceInternal(context, coreSettings, packageName); + return shouldUseAngleInternal(context, coreSettings, packageName); } private int getVulkanVersion(PackageManager pm) { @@ -431,11 +424,10 @@ public class GraphicsEnvironment { * forces a choice; * 3) Use ANGLE if isAngleEnabledByGameMode() returns true; */ - private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle, - String packageName) { + private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) { // Make sure we have a good package name if (TextUtils.isEmpty(packageName)) { - return AngleDriverChoice.DEFAULT; + return false; } // Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE @@ -450,7 +442,7 @@ public class GraphicsEnvironment { } if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) { Log.v(TAG, "Turn on ANGLE for all applications."); - return AngleDriverChoice.ANGLE; + return true; } // Get the per-application settings lists @@ -473,7 +465,7 @@ public class GraphicsEnvironment { + optInPackages.size() + ", " + "number of values: " + optInValues.size()); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } // See if this application is listed in the per-application settings list @@ -481,7 +473,7 @@ public class GraphicsEnvironment { if (pkgIndex < 0) { Log.v(TAG, packageName + " is not listed in per-application setting"); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } mAngleOptInIndex = pkgIndex; @@ -492,13 +484,13 @@ public class GraphicsEnvironment { "ANGLE Developer option for '" + packageName + "' " + "set to: '" + optInValue + "'"); if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) { - return AngleDriverChoice.ANGLE; + return true; } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { - return AngleDriverChoice.NATIVE; + return false; } else { // The user either chose default or an invalid value; go with the default driver or what // the game mode indicates - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } } @@ -566,13 +558,7 @@ public class GraphicsEnvironment { private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager, String packageName) { - final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName); - if (angleDriverChoice == AngleDriverChoice.DEFAULT) { - return false; - } - - if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) { - nativeSetAngleInfo("", true, packageName, null); + if (!shouldUseAngle(context, bundle, packageName)) { return false; } @@ -641,10 +627,10 @@ public class GraphicsEnvironment { Log.d(TAG, "ANGLE package libs: " + paths); } - // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the - // application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo(paths, false, packageName, features); + setAngleInfo(paths, false, packageName, features); return true; } @@ -666,10 +652,10 @@ public class GraphicsEnvironment { return false; } - // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with - // the application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo("system", false, packageName, features); + setAngleInfo("", true, packageName, features); return true; } @@ -950,8 +936,8 @@ public class GraphicsEnvironment { private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); - private static native void nativeSetAngleInfo(String path, boolean useNativeDriver, - String packageName, String[] features); + private static native void setAngleInfo(String path, boolean useSystemAngle, String packageName, + String[] features); private static native boolean setInjectLayersPrSetDumpable(); private static native void nativeToggleAngleAsSystemDriver(boolean enabled); diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index afc567e5de5c..c88048c17160 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -97,6 +97,12 @@ public interface WindowManagerPolicyConstants { */ String EXTRA_START_REASON = "android.intent.extra.EXTRA_START_REASON"; + /** + * Set to {@code true} when intent was invoked from pressing one of the brightness keys. + * @hide + */ + String EXTRA_FROM_BRIGHTNESS_KEY = "android.intent.extra.FROM_BRIGHTNESS_KEY"; + // TODO: move this to a more appropriate place. interface PointerEventListener { /** diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index c9afdc0ad074..2c7d326587c7 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -414,7 +414,7 @@ public final class ContentCaptureManager { /** @hide */ public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false; /** @hide */ - public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 1000; + public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 5000; /** @hide */ public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3f308e6fccee..b3233141f126 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -50,6 +50,7 @@ import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.app.PropertyInvalidatedCache; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -536,6 +537,13 @@ public final class InputMethodManager { @UnsupportedAppUsage Rect mCursorRect = new Rect(); + /** Cached value for {@link #isStylusHandwritingAvailable} for userId. */ + @GuardedBy("mH") + private PropertyInvalidatedCache<Integer, Boolean> mStylusHandwritingAvailableCache; + + private static final String CACHE_KEY_STYLUS_HANDWRITING_PROPERTY = + "cache_key.system_server.stylus_handwriting"; + @GuardedBy("mH") private int mCursorSelStart; @GuardedBy("mH") @@ -662,6 +670,15 @@ public final class InputMethodManager { private static final int MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX = 30; private static final int MSG_ON_SHOW_REQUESTED = 31; + /** + * Calling this will invalidate Local stylus handwriting availability Cache which + * forces the next query in any process to recompute the cache. + * @hide + */ + public static void invalidateLocalStylusHandwritingAvailabilityCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STYLUS_HANDWRITING_PROPERTY); + } + private static boolean isAutofillUIShowing(View servedView) { AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class); return afm != null && afm.isAutofillUiShowing(); @@ -1577,8 +1594,21 @@ public final class InputMethodManager { if (fallbackContext == null) { return false; } - - return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser(userId); + boolean isAvailable; + synchronized (mH) { + if (mStylusHandwritingAvailableCache == null) { + mStylusHandwritingAvailableCache = new PropertyInvalidatedCache<>( + 4 /* maxEntries */, CACHE_KEY_STYLUS_HANDWRITING_PROPERTY) { + @Override + public Boolean recompute(Integer userId) { + return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser( + userId); + } + }; + } + isAvailable = mStylusHandwritingAvailableCache.query(userId); + } + return isAvailable; } /** diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index b65c1a17e26b..cb5dbe6c5618 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1550,6 +1550,7 @@ public class ScrollView extends FrameLayout { float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size; int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR * mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f)); + mEdgeGlowTop.onRelease(); if (consumed != unconsumed) { mEdgeGlowTop.finish(); } @@ -1560,6 +1561,7 @@ public class ScrollView extends FrameLayout { float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size; int consumed = Math.round(size / FLING_DESTRETCH_FACTOR * mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f)); + mEdgeGlowBottom.onRelease(); if (consumed != unconsumed) { mEdgeGlowBottom.finish(); } diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java index 413f0ccbc275..e153bb70a7ca 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -73,6 +73,13 @@ public final class TaskFragmentOperation implements Parcelable { /** Sets the relative bounds with {@link WindowContainerTransaction#setRelativeBounds}. */ public static final int OP_TYPE_SET_RELATIVE_BOUNDS = 9; + /** + * Reorders the TaskFragment to be the front-most TaskFragment in the Task. + * Note that there could still have other WindowContainer on top of the front-most + * TaskFragment, such as a non-embedded Activity. + */ + public static final int OP_TYPE_REORDER_TO_FRONT = 10; + @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, @@ -84,7 +91,8 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT, OP_TYPE_SET_COMPANION_TASK_FRAGMENT, OP_TYPE_SET_ANIMATION_PARAMS, - OP_TYPE_SET_RELATIVE_BOUNDS + OP_TYPE_SET_RELATIVE_BOUNDS, + OP_TYPE_REORDER_TO_FRONT }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index d068a3a0f62e..e2672f5b03ba 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -21,3 +21,6 @@ per-file ImageFloatingTextView.java = file:/services/core/java/com/android/serve per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS + +# Appwidget related +per-file *RemoteViews* = file:/services/appwidget/java/com/android/server/appwidget/OWNERS diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index e1be0cd80bb6..4cf17b78f489 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -30,6 +30,8 @@ #include <media/AudioSystem.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/jni_macros.h> #include <system/audio.h> #include <system/audio_policy.h> #include <utils/Log.h> @@ -285,7 +287,7 @@ JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject we ALOGE("Can't find class %s", kEventHandlerClassPathName); return; } - mClass = (jclass)env->NewGlobalRef(clazz); + mClass = static_cast<jclass>(env->NewGlobalRef(clazz)); // We use a weak reference so the AudioPortEventHandler object can be garbage collected. // The reference is only used as a proxy for callbacks. @@ -337,15 +339,16 @@ static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env, const sp<JNIAudioPortCallback>& callback) { Mutex::Autolock l(gLock); - sp<JNIAudioPortCallback> old = - (JNIAudioPortCallback*)env->GetLongField(thiz, gEventHandlerFields.mJniCallback); + sp<JNIAudioPortCallback> old = reinterpret_cast<JNIAudioPortCallback *>( + env->GetLongField(thiz, gEventHandlerFields.mJniCallback)); if (callback.get()) { - callback->incStrong((void*)setJniCallback); + callback->incStrong(reinterpret_cast<void *>(setJniCallback)); } if (old != 0) { - old->decStrong((void*)setJniCallback); + old->decStrong(reinterpret_cast<void *>(setJniCallback)); } - env->SetLongField(thiz, gEventHandlerFields.mJniCallback, (jlong)callback.get()); + env->SetLongField(thiz, gEventHandlerFields.mJniCallback, + reinterpret_cast<jlong>(callback.get())); return old; } @@ -374,43 +377,44 @@ static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, jobjectArray deviceAddresses, AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) { if (deviceTypes == nullptr || deviceAddresses == nullptr) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jsize deviceCount = env->GetArrayLength(deviceTypes); if (deviceCount == 0 || deviceCount != env->GetArrayLength(deviceAddresses)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } // retrieve all device types std::vector<audio_devices_t> deviceTypesVector; jint *typesPtr = nullptr; typesPtr = env->GetIntArrayElements(deviceTypes, 0); if (typesPtr == nullptr) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } for (jint i = 0; i < deviceCount; i++) { - deviceTypesVector.push_back((audio_devices_t)typesPtr[i]); + deviceTypesVector.push_back(static_cast<audio_devices_t>(typesPtr[i])); } // check each address is a string and add device type/address to list jclass stringClass = FindClassOrDie(env, "java/lang/String"); for (jint i = 0; i < deviceCount; i++) { jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i); if (!env->IsInstanceOf(addrJobj, stringClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL); - AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address); + const char *address = env->GetStringUTFChars(static_cast<jstring>(addrJobj), NULL); + AudioDeviceTypeAddr dev = + AudioDeviceTypeAddr(static_cast<audio_devices_t>(typesPtr[i]), address); audioDeviceTypeAddrVector.push_back(dev); - env->ReleaseStringUTFChars((jstring)addrJobj, address); + env->ReleaseStringUTFChars(static_cast<jstring>(addrJobj), address); } env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0); - return (jint)NO_ERROR; + return NO_ERROR; } static jint android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on) { - return (jint) check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); + return check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); } static jboolean @@ -425,7 +429,7 @@ static jboolean android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, jint inPastMs) { bool state = false; - AudioSystem::isStreamActive((audio_stream_type_t) stream, &state, inPastMs); + AudioSystem::isStreamActive(static_cast<audio_stream_type_t>(stream), &state, inPastMs); return state; } @@ -434,7 +438,7 @@ android_media_AudioSystem_isStreamActiveRemotely(JNIEnv *env, jobject thiz, jint jint inPastMs) { bool state = false; - AudioSystem::isStreamActiveRemotely((audio_stream_type_t) stream, &state, inPastMs); + AudioSystem::isStreamActiveRemotely(static_cast<audio_stream_type_t>(stream), &state, inPastMs); return state; } @@ -442,7 +446,7 @@ static jboolean android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) { bool state = false; - AudioSystem::isSourceActive((audio_source_t) source, &state); + AudioSystem::isSourceActive(static_cast<audio_source_t>(source), &state); return state; } @@ -477,8 +481,7 @@ android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyVa env->GetStringLength(keyValuePairs)); env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs); } - int status = check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8)); - return (jint) status; + return check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8)); } static jstring @@ -558,15 +561,15 @@ android_media_AudioSystem_recording_callback(int event, return; } jint recParamData[REC_PARAM_SIZE]; - recParamData[0] = (jint) audioFormatFromNative(clientConfig->format); + recParamData[0] = audioFormatFromNative(clientConfig->format); // FIXME this doesn't support index-based masks - recParamData[1] = (jint) inChannelMaskFromNative(clientConfig->channel_mask); - recParamData[2] = (jint) clientConfig->sample_rate; - recParamData[3] = (jint) audioFormatFromNative(deviceConfig->format); + recParamData[1] = inChannelMaskFromNative(clientConfig->channel_mask); + recParamData[2] = clientConfig->sample_rate; + recParamData[3] = audioFormatFromNative(deviceConfig->format); // FIXME this doesn't support index-based masks - recParamData[4] = (jint) inChannelMaskFromNative(deviceConfig->channel_mask); - recParamData[5] = (jint) deviceConfig->sample_rate; - recParamData[6] = (jint) patchHandle; + recParamData[4] = inChannelMaskFromNative(deviceConfig->channel_mask); + recParamData[5] = deviceConfig->sample_rate; + recParamData[6] = patchHandle; env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData); jobjectArray jClientEffects; @@ -580,10 +583,9 @@ android_media_AudioSystem_recording_callback(int event, env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative, - event, (jint) clientInfo->riid, (jint) clientInfo->uid, - clientInfo->session, clientInfo->source, clientInfo->port_id, - clientInfo->silenced, recParamArray, jClientEffects, jEffects, - source); + event, clientInfo->riid, clientInfo->uid, clientInfo->session, + clientInfo->source, clientInfo->port_id, clientInfo->silenced, + recParamArray, jClientEffects, jEffects, source); env->DeleteLocalRef(clazz); env->DeleteLocalRef(recParamArray); env->DeleteLocalRef(jClientEffects); @@ -626,11 +628,9 @@ static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobj if (Parcel *parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) { android::media::audio::common::AudioPort port{}; if (status_t statusOfParcel = port.readFromParcel(parcel); statusOfParcel == OK) { - status = check_AudioSystem_Command( - AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>( - state), - port, - static_cast<audio_format_t>(codec))); + status = check_AudioSystem_Command( + AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>(state), + port, static_cast<audio_format_t>(codec))); } else { ALOGE("Failed to read from parcel: %s", statusToString(statusOfParcel).c_str()); status = kAudioStatusError; @@ -639,17 +639,17 @@ static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobj ALOGE("Failed to retrieve the native parcel from Java parcel"); status = kAudioStatusError; } - return (jint) status; + return status; } static jint android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address) { const char *c_address = env->GetStringUTFChars(device_address, NULL); - int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <audio_devices_t>(device), - c_address)); + int state = static_cast<int>( + AudioSystem::getDeviceConnectionState(static_cast<audio_devices_t>(device), c_address)); env->ReleaseStringUTFChars(device_address, c_address); - return (jint) state; + return state; } static jint @@ -658,38 +658,41 @@ android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, ji { const char *c_address = env->GetStringUTFChars(device_address, NULL); const char *c_name = env->GetStringUTFChars(device_name, NULL); - int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device), - c_address, c_name, static_cast <audio_format_t>(codec))); + int status = check_AudioSystem_Command( + AudioSystem::handleDeviceConfigChange(static_cast<audio_devices_t>(device), c_address, + c_name, static_cast<audio_format_t>(codec))); env->ReleaseStringUTFChars(device_address, c_address); env->ReleaseStringUTFChars(device_name, c_name); - return (jint) status; + return status; } static jint android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state, jint uid) { - return (jint)check_AudioSystem_Command( - AudioSystem::setPhoneState((audio_mode_t)state, (uid_t)uid)); + return check_AudioSystem_Command( + AudioSystem::setPhoneState(static_cast<audio_mode_t>(state), static_cast<uid_t>(uid))); } static jint android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config) { - return (jint) check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage), - static_cast <audio_policy_forced_cfg_t>(config))); + return check_AudioSystem_Command( + AudioSystem::setForceUse(static_cast<audio_policy_force_use_t>(usage), + static_cast<audio_policy_forced_cfg_t>(config))); } static jint android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage) { - return static_cast <jint>(AudioSystem::getForceUse(static_cast <audio_policy_force_use_t>(usage))); + return static_cast<jint>( + AudioSystem::getForceUse(static_cast<audio_policy_force_use_t>(usage))); } static jint android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax) { - return (jint) check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <audio_stream_type_t>(stream), - indexMin, - indexMax)); + return check_AudioSystem_Command( + AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(stream), indexMin, + indexMax)); } static jint @@ -699,10 +702,9 @@ android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jint index, jint device) { - return (jint) check_AudioSystem_Command( - AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), - index, - (audio_devices_t)device)); + return check_AudioSystem_Command( + AudioSystem::setStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), index, + static_cast<audio_devices_t>(device))); } static jint @@ -712,13 +714,11 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jint device) { int index; - if (AudioSystem::getStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), - &index, - (audio_devices_t)device) - != NO_ERROR) { + if (AudioSystem::getStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), &index, + static_cast<audio_devices_t>(device)) != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -731,11 +731,12 @@ android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } - return (jint) check_AudioSystem_Command( - AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device)); + return check_AudioSystem_Command( + AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, + static_cast<audio_devices_t>(device))); } static jint @@ -747,15 +748,16 @@ android_media_AudioSystem_getVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; - if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device) - != NO_ERROR) { + if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, + static_cast<audio_devices_t>(device)) != + NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -766,7 +768,7 @@ android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; @@ -774,7 +776,7 @@ android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -785,7 +787,7 @@ android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; @@ -793,13 +795,13 @@ android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint android_media_AudioSystem_setMasterVolume(JNIEnv *env, jobject thiz, jfloat value) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); + return check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); } static jfloat @@ -815,7 +817,7 @@ android_media_AudioSystem_getMasterVolume(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterMute(JNIEnv *env, jobject thiz, jboolean mute) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterMute(mute)); + return check_AudioSystem_Command(AudioSystem::setMasterMute(mute)); } static jboolean @@ -831,7 +833,7 @@ android_media_AudioSystem_getMasterMute(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterMono(JNIEnv *env, jobject thiz, jboolean mono) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterMono(mono)); + return check_AudioSystem_Command(AudioSystem::setMasterMono(mono)); } static jboolean @@ -847,7 +849,7 @@ android_media_AudioSystem_getMasterMono(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterBalance(JNIEnv *env, jobject thiz, jfloat balance) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterBalance(balance)); + return check_AudioSystem_Command(AudioSystem::setMasterBalance(balance)); } static jfloat @@ -865,37 +867,37 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz) { - return (jint) AudioSystem::getPrimaryOutputSamplingRate(); + return AudioSystem::getPrimaryOutputSamplingRate(); } static jint android_media_AudioSystem_getPrimaryOutputFrameCount(JNIEnv *env, jobject clazz) { - return (jint) AudioSystem::getPrimaryOutputFrameCount(); + return AudioSystem::getPrimaryOutputFrameCount(); } static jint android_media_AudioSystem_getOutputLatency(JNIEnv *env, jobject clazz, jint stream) { uint32_t afLatency; - if (AudioSystem::getOutputLatency(&afLatency, static_cast <audio_stream_type_t>(stream)) - != NO_ERROR) { + if (AudioSystem::getOutputLatency(&afLatency, static_cast<audio_stream_type_t>(stream)) != + NO_ERROR) { afLatency = -1; } - return (jint) afLatency; + return afLatency; } static jint android_media_AudioSystem_setLowRamDevice( JNIEnv *env, jobject clazz, jboolean isLowRamDevice, jlong totalMemory) { - return (jint) AudioSystem::setLowRamDevice((bool) isLowRamDevice, (int64_t) totalMemory); + return AudioSystem::setLowRamDevice(isLowRamDevice, totalMemory); } static jint android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz) { - return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); + return check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); } static void android_media_AudioSystem_setAudioFlingerBinder(JNIEnv *env, jobject clazz, @@ -909,8 +911,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, bool useInMask) { nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex); - nAudioGainConfig->mode = - (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode); + nAudioGainConfig->mode = static_cast<audio_gain_mode_t>( + env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode)); ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index); jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask); audio_channel_mask_t nMask; @@ -924,8 +926,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, nAudioGainConfig->channel_mask = nMask; nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mRampDurationMs); - jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig, - gAudioGainConfigFields.mValues); + jintArray jValues = static_cast<jintArray>( + env->GetObjectField(jAudioGainConfig, gAudioGainConfigFields.mValues)); int *nValues = env->GetIntArrayElements(jValues, NULL); size_t size = env->GetArrayLength(jValues); memcpy(nAudioGainConfig->values, nValues, size * sizeof(int)); @@ -940,8 +942,8 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle); nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId); - nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort, - gAudioPortFields.mRole); + nAudioPortConfig->role = + static_cast<audio_port_role_t>(env->GetIntField(jAudioPort, gAudioPortFields.mRole)); if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE; } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { @@ -949,7 +951,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, } else { env->DeleteLocalRef(jAudioPort); env->DeleteLocalRef(jHandle); - return (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } ALOGV("convertAudioPortConfigToNative handle %d role %d type %d", nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type); @@ -1004,7 +1006,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, } env->DeleteLocalRef(jAudioPort); env->DeleteLocalRef(jHandle); - return (jint)AUDIO_JAVA_SUCCESS; + return AUDIO_JAVA_SUCCESS; } /** @@ -1025,15 +1027,15 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, } // Supports AUDIO_PORT_TYPE_DEVICE only if (nAudioPortConfig->type != AUDIO_PORT_TYPE_DEVICE) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); - nAudioPortConfig->ext.device.type = - (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType); - jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort, - gAudioPortFields.mAddress); + nAudioPortConfig->ext.device.type = static_cast<audio_devices_t>( + env->GetIntField(jAudioDevicePort, gAudioPortFields.mType)); + jstring jDeviceAddress = + static_cast<jstring>(env->GetObjectField(jAudioDevicePort, gAudioPortFields.mAddress)); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); strncpy(nAudioPortConfig->ext.device.address, nDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN - 1); @@ -1043,45 +1045,41 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, return jStatus; } -static jint convertAudioPortConfigFromNative(JNIEnv *env, - jobject jAudioPort, - jobject *jAudioPortConfig, - const struct audio_port_config *nAudioPortConfig) -{ - jint jStatus = AUDIO_JAVA_SUCCESS; - jobject jAudioGainConfig = NULL; - jobject jAudioGain = NULL; +static jint convertAudioPortConfigFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioPort, + ScopedLocalRef<jobject> *jAudioPortConfig, + const struct audio_port_config *nAudioPortConfig) { jintArray jGainValues; bool audioportCreated = false; ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort); - if (jAudioPort == NULL) { - jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - nAudioPortConfig->id); + if (*jAudioPort == nullptr) { + ScopedLocalRef<jobject> jHandle(env, + env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPortConfig->id)); ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id, nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix"); if (jHandle == NULL) { - return (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } // create placeholder port and port config objects with just the correct handle // and configuration data. The actual AudioPortConfig objects will be // constructed by java code with correct class type (device, mix etc...) // and reference to AudioPort instance in this client - jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor, - jHandle, // handle - 0, // role - NULL, // name - NULL, // samplingRates - NULL, // channelMasks - NULL, // channelIndexMasks - NULL, // formats - NULL); // gains - env->DeleteLocalRef(jHandle); - if (jAudioPort == NULL) { - return (jint)AUDIO_JAVA_ERROR; + jAudioPort->reset(env->NewObject(gAudioPortClass, gAudioPortCstor, + jHandle.get(), // handle + 0, // role + nullptr, // name + nullptr, // samplingRates + nullptr, // channelMasks + nullptr, // channelIndexMasks + nullptr, // formats + nullptr)); // gains + + if (*jAudioPort == nullptr) { + return AUDIO_JAVA_ERROR; } ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d", nAudioPortConfig->id); @@ -1089,6 +1087,9 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, audioportCreated = true; } + ScopedLocalRef<jobject> jAudioGainConfig(env, nullptr); + ScopedLocalRef<jobject> jAudioGain(env, nullptr); + bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig); audio_channel_mask_t nMask; @@ -1102,36 +1103,28 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, gainIndex, nAudioPortConfig->gain.mode); if (audioportCreated) { ALOGV("convertAudioPortConfigFromNative creating gain"); - jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor, - gainIndex, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); + jAudioGain.reset(env->NewObject(gAudioGainClass, gAudioGainCstor, gainIndex, 0 /*mode*/, + 0 /*channelMask*/, 0 /*minValue*/, 0 /*maxValue*/, + 0 /*defaultValue*/, 0 /*stepValue*/, + 0 /*rampDurationMinMs*/, 0 /*rampDurationMaxMs*/)); if (jAudioGain == NULL) { ALOGV("convertAudioPortConfigFromNative creating gain FAILED"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } else { ALOGV("convertAudioPortConfigFromNative reading gain from port"); - jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort, - gAudioPortFields.mGains); + ScopedLocalRef<jobjectArray> + jGains(env, + static_cast<jobjectArray>(env->GetObjectField(jAudioPort->get(), + gAudioPortFields.mGains))); if (jGains == NULL) { ALOGV("convertAudioPortConfigFromNative could not get gains from port"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - jAudioGain = env->GetObjectArrayElement(jGains, gainIndex); - env->DeleteLocalRef(jGains); + jAudioGain.reset(env->GetObjectArrayElement(jGains.get(), gainIndex)); if (jAudioGain == NULL) { ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } int numValues; @@ -1143,8 +1136,7 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, jGainValues = env->NewIntArray(numValues); if (jGainValues == NULL) { ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } env->SetIntArrayRegion(jGainValues, 0, numValues, nAudioPortConfig->gain.values); @@ -1158,19 +1150,14 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); } - jAudioGainConfig = env->NewObject(gAudioGainConfigClass, - gAudioGainConfigCstor, - gainIndex, - jAudioGain, - nAudioPortConfig->gain.mode, - jMask, - jGainValues, - nAudioPortConfig->gain.ramp_duration_ms); + jAudioGainConfig.reset(env->NewObject(gAudioGainConfigClass, gAudioGainConfigCstor, + gainIndex, jAudioGain.get(), + nAudioPortConfig->gain.mode, jMask, jGainValues, + nAudioPortConfig->gain.ramp_duration_ms)); env->DeleteLocalRef(jGainValues); if (jAudioGainConfig == NULL) { ALOGV("convertAudioPortConfigFromNative could not create gain config"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } jclass clazz; @@ -1180,17 +1167,16 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, methodID = gAudioPortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a generic port config"); } else { - if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + if (env->IsInstanceOf(jAudioPort->get(), gAudioDevicePortClass)) { clazz = gAudioDevicePortConfigClass; methodID = gAudioDevicePortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a device config"); - } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + } else if (env->IsInstanceOf(jAudioPort->get(), gAudioMixPortClass)) { clazz = gAudioMixPortConfigClass; methodID = gAudioMixPortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a mix config"); } else { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } nMask = (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) @@ -1204,8 +1190,8 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); } - *jAudioPortConfig = - env->NewObject(clazz, methodID, jAudioPort, + jAudioPortConfig->reset( + env->NewObject(clazz, methodID, jAudioPort->get(), (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) ? nAudioPortConfig->sample_rate : AUDIO_CONFIG_BASE_INITIALIZER.sample_rate, @@ -1214,31 +1200,14 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) ? nAudioPortConfig->format : AUDIO_CONFIG_BASE_INITIALIZER.format), - jAudioGainConfig); + jAudioGainConfig.get())); if (*jAudioPortConfig == NULL) { ALOGV("convertAudioPortConfigFromNative could not create new port config"); - jStatus = (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } else { ALOGV("convertAudioPortConfigFromNative OK"); } - -exit: - if (audioportCreated) { - env->DeleteLocalRef(jAudioPort); - if (jAudioGain != NULL) { - env->DeleteLocalRef(jAudioGain); - } - } - if (jAudioGainConfig != NULL) { - env->DeleteLocalRef(jAudioGainConfig); - } - return jStatus; -} - -// TODO: pull out to separate file -template <typename T, size_t N> -static constexpr size_t array_size(const T (&)[N]) { - return N; + return AUDIO_JAVA_SUCCESS; } static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encapsulationInfo) { @@ -1252,7 +1221,8 @@ static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encaps } } jintArray result = env->NewIntArray(encapsulation.size()); - env->SetIntArrayRegion(result, 0, encapsulation.size(), (jint *)encapsulation.data()); + env->SetIntArrayRegion(result, 0, encapsulation.size(), + reinterpret_cast<jint *>(encapsulation.data())); return result; } @@ -1260,8 +1230,8 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP std::stringstream &ss) { ss << " num_audio_profiles " << nAudioPort->num_audio_profiles << " num_gains " << nAudioPort->num_gains; - if (nAudioPort->num_audio_profiles > array_size(nAudioPort->audio_profiles) || - nAudioPort->num_gains > array_size(nAudioPort->gains)) { + if (nAudioPort->num_audio_profiles > std::size(nAudioPort->audio_profiles) || + nAudioPort->num_gains > std::size(nAudioPort->gains)) { return true; } for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { @@ -1269,16 +1239,16 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP << " num_sample_rates " << nAudioPort->audio_profiles[i].num_sample_rates << " num_channel_masks " << nAudioPort->audio_profiles[i].num_channel_masks; if (nAudioPort->audio_profiles[i].num_sample_rates > - array_size(nAudioPort->audio_profiles[i].sample_rates) || + std::size(nAudioPort->audio_profiles[i].sample_rates) || nAudioPort->audio_profiles[i].num_channel_masks > - array_size(nAudioPort->audio_profiles[i].channel_masks)) { + std::size(nAudioPort->audio_profiles[i].channel_masks)) { return true; } } return false; } -static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, +static jint convertAudioProfileFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioProfile, const audio_profile *nAudioProfile, bool useInMask) { size_t numPositionMasks = 0; size_t numIndexMasks = 0; @@ -1309,7 +1279,8 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, if (nAudioProfile->num_sample_rates) { env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, nAudioProfile->num_sample_rates, - (jint *)nAudioProfile->sample_rates); + const_cast<jint *>(reinterpret_cast<const jint *>( + nAudioProfile->sample_rates))); } // put the masks in the output arrays @@ -1331,10 +1302,9 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type); } - *jAudioProfile = env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat, - jSamplingRates.get(), jChannelMasks.get(), - jChannelIndexMasks.get(), encapsulationType); - + jAudioProfile->reset(env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat, + jSamplingRates.get(), jChannelMasks.get(), + jChannelIndexMasks.get(), encapsulationType)); if (*jAudioProfile == nullptr) { return AUDIO_JAVA_ERROR; } @@ -1342,18 +1312,8 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, return AUDIO_JAVA_SUCCESS; } -static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, +static jint convertAudioPortFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioPort, const struct audio_port_v7 *nAudioPort) { - jint jStatus = (jint)AUDIO_JAVA_SUCCESS; - jintArray jEncapsulationModes = NULL; - jintArray jEncapsulationMetadataTypes = NULL; - jobjectArray jGains = NULL; - jobject jHandle = NULL; - jobject jAudioPortConfig = NULL; - jstring jDeviceName = NULL; - jobject jAudioProfiles = NULL; - jobject jAudioDescriptors = nullptr; - ScopedLocalRef<jobject> jPcmFloatProfileFromExtendedInteger(env, nullptr); bool hasFloat = false; bool useInMask; @@ -1377,30 +1337,30 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, } else { ALOGE("%s", s.c_str()); } - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } useInMask = audio_has_input_direction(nAudioPort->type, nAudioPort->role); - jAudioProfiles = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + ScopedLocalRef<jobject> jAudioProfiles(env, + env->NewObject(gArrayListClass, + gArrayListMethods.cstor)); if (jAudioProfiles == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } + ScopedLocalRef<jobject> jPcmFloatProfileFromExtendedInteger(env, nullptr); for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { - jobject jAudioProfile = nullptr; - jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i], - useInMask); + ScopedLocalRef<jobject> jAudioProfile(env); + jint jStatus = convertAudioProfileFromNative(env, &jAudioProfile, + &nAudioPort->audio_profiles[i], useInMask); if (jStatus == AUDIO_JAVA_BAD_VALUE) { // skipping Java layer unsupported audio formats continue; } if (jStatus != NO_ERROR) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile); + env->CallBooleanMethod(jAudioProfiles.get(), gArrayListMethods.add, jAudioProfile.get()); if (nAudioPort->audio_profiles[i].format == AUDIO_FORMAT_PCM_FLOAT) { hasFloat = true; @@ -1409,21 +1369,23 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, audio_bytes_per_sample(nAudioPort->audio_profiles[i].format) > 2) { ScopedLocalRef<jintArray> jSamplingRates(env, - (jintArray) - env->GetObjectField(jAudioProfile, - gAudioProfileFields.mSamplingRates)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile.get(), + gAudioProfileFields + .mSamplingRates))); ScopedLocalRef<jintArray> jChannelMasks(env, - (jintArray) - env->GetObjectField(jAudioProfile, - gAudioProfileFields.mChannelMasks)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile.get(), + gAudioProfileFields.mChannelMasks))); ScopedLocalRef<jintArray> jChannelIndexMasks(env, - (jintArray)env->GetObjectField(jAudioProfile, - gAudioProfileFields - .mChannelIndexMasks)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile.get(), + gAudioProfileFields + .mChannelIndexMasks))); int encapsulationType = - env->GetIntField(jAudioProfile, gAudioProfileFields.mEncapsulationType); + env->GetIntField(jAudioProfile.get(), gAudioProfileFields.mEncapsulationType); jPcmFloatProfileFromExtendedInteger.reset( env->NewObject(gAudioProfileClass, gAudioProfileCstor, @@ -1431,24 +1393,21 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, jSamplingRates.get(), jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType)); } - - if (jAudioProfile != nullptr) { - env->DeleteLocalRef(jAudioProfile); - } } if (!hasFloat && jPcmFloatProfileFromExtendedInteger.get() != nullptr) { // R and earlier compatibility - add ENCODING_PCM_FLOAT to the end // (replacing the zero pad). This ensures pre-S apps that look // for ENCODING_PCM_FLOAT continue to see that encoding if the device supports // extended precision integers. - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, + env->CallBooleanMethod(jAudioProfiles.get(), gArrayListMethods.add, jPcmFloatProfileFromExtendedInteger.get()); } - jAudioDescriptors = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + ScopedLocalRef<jobject> jAudioDescriptors(env, + env->NewObject(gArrayListClass, + gArrayListMethods.cstor)); if (jAudioDescriptors == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } for (size_t i = 0; i < nAudioPort->num_extra_audio_descriptors; ++i) { const auto &extraAudioDescriptor = nAudioPort->extra_audio_descriptors[i]; @@ -1478,15 +1437,16 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, env->NewObject(gAudioDescriptorClass, gAudioDescriptorCstor, standard, encapsulationType, jDescriptor.get())); - env->CallBooleanMethod(jAudioDescriptors, gArrayListMethods.add, jAudioDescriptor.get()); + env->CallBooleanMethod(jAudioDescriptors.get(), gArrayListMethods.add, + jAudioDescriptor.get()); } // gains - jGains = env->NewObjectArray(nAudioPort->num_gains, - gAudioGainClass, NULL); - if (jGains == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + ScopedLocalRef<jobjectArray> jGains(env, + env->NewObjectArray(nAudioPort->num_gains, gAudioGainClass, + nullptr)); + if (jGains == nullptr) { + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nAudioPort->num_gains; j++) { @@ -1511,88 +1471,71 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, nAudioPort->gains[j].min_ramp_ms, nAudioPort->gains[j].max_ramp_ms); if (jGain == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - env->SetObjectArrayElement(jGains, j, jGain); + env->SetObjectArrayElement(jGains.get(), j, jGain); env->DeleteLocalRef(jGain); } - jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - nAudioPort->id); - if (jHandle == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + ScopedLocalRef<jobject> jHandle(env, + env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPort->id)); + if (jHandle == nullptr) { + return AUDIO_JAVA_ERROR; } - jDeviceName = env->NewStringUTF(nAudioPort->name); - + ScopedLocalRef<jstring> jDeviceName(env, env->NewStringUTF(nAudioPort->name)); if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) { - ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); - jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address); - jEncapsulationModes = - convertEncapsulationInfoFromNative(env, nAudioPort->ext.device.encapsulation_modes); - jEncapsulationMetadataTypes = + ScopedLocalRef<jintArray> jEncapsulationModes( + env, + convertEncapsulationInfoFromNative(env, + nAudioPort->ext.device.encapsulation_modes)); + ScopedLocalRef<jintArray> jEncapsulationMetadataTypes( + env, convertEncapsulationInfoFromNative(env, nAudioPort->ext.device - .encapsulation_metadata_types); - *jAudioPort = - env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, jDeviceName, - jAudioProfiles, jGains, nAudioPort->ext.device.type, jAddress, - jEncapsulationModes, jEncapsulationMetadataTypes, jAudioDescriptors); - env->DeleteLocalRef(jAddress); + .encapsulation_metadata_types)); + ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); + ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(nAudioPort->ext.device.address)); + jAudioPort->reset(env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, + jHandle.get(), jDeviceName.get(), jAudioProfiles.get(), + jGains.get(), nAudioPort->ext.device.type, jAddress.get(), + jEncapsulationModes.get(), + jEncapsulationMetadataTypes.get(), + jAudioDescriptors.get())); } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { ALOGV("convertAudioPortFromNative is a mix"); - *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle, - nAudioPort->ext.mix.handle, nAudioPort->role, jDeviceName, - jAudioProfiles, jGains); + jAudioPort->reset(env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle.get(), + nAudioPort->ext.mix.handle, nAudioPort->role, + jDeviceName.get(), jAudioProfiles.get(), jGains.get())); } else { ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } if (*jAudioPort == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - jStatus = convertAudioPortConfigFromNative(env, - *jAudioPort, - &jAudioPortConfig, + ScopedLocalRef<jobject> jAudioPortConfig(env, nullptr); + + if (int jStatus = convertAudioPortConfigFromNative(env, jAudioPort, &jAudioPortConfig, &nAudioPort->active_config); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } - env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); + env->SetObjectField(jAudioPort->get(), gAudioPortFields.mActiveConfig, jAudioPortConfig.get()); + return AUDIO_JAVA_SUCCESS; +} -exit: - if (jDeviceName != NULL) { - env->DeleteLocalRef(jDeviceName); - } - if (jEncapsulationModes != NULL) { - env->DeleteLocalRef(jEncapsulationModes); - } - if (jEncapsulationMetadataTypes != NULL) { - env->DeleteLocalRef(jEncapsulationMetadataTypes); - } - if (jAudioProfiles != NULL) { - env->DeleteLocalRef(jAudioProfiles); - } - if (jGains != NULL) { - env->DeleteLocalRef(jGains); - } - if (jHandle != NULL) { - env->DeleteLocalRef(jHandle); - } - if (jAudioPortConfig != NULL) { - env->DeleteLocalRef(jAudioPortConfig); - } - if (jAudioDescriptors != nullptr) { - env->DeleteLocalRef(jAudioDescriptors); +static bool setGeneration(JNIEnv *env, jintArray jGeneration, unsigned int generation1) { + ScopedIntArrayRW nGeneration(env, jGeneration); + if (nGeneration.get() == nullptr) { + return false; + } else { + nGeneration[0] = generation1; + return true; } - - return jStatus; } static jint @@ -1603,23 +1546,22 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, if (jPorts == NULL) { ALOGE("listAudioPorts NULL AudioPort ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jPorts, gArrayListClass)) { ALOGE("listAudioPorts not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } status_t status; - unsigned int generation1; + unsigned int generation1 = 0; unsigned int generation; unsigned int numPorts; - jint *nGeneration; - struct audio_port_v7 *nPorts = nullptr; + std::vector<audio_port_v7> nPorts; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1638,43 +1580,29 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, break; } if (numPorts == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return setGeneration(env, jGeneration, generation1) ? AUDIO_JAVA_SUCCESS + : AUDIO_JAVA_ERROR; } - nPorts = (struct audio_port_v7 *)realloc(nPorts, numPorts * sizeof(struct audio_port_v7)); + nPorts.resize(numPorts); status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts, - nPorts, &generation); + &nPorts[0], &generation); ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d", numPorts, generation, generation1); } while (generation1 != generation && status == NO_ERROR); jStatus = nativeToJavaStatus(status); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - - for (size_t i = 0; i < numPorts; i++) { - jobject jAudioPort = NULL; - jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort); - if (jAudioPort != NULL) { - env->DeleteLocalRef(jAudioPort); + if (jStatus == AUDIO_JAVA_SUCCESS) { + for (size_t i = 0; i < numPorts; i++) { + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) break; + env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort.get()); } } - -exit: - nGeneration = env->GetIntArrayElements(jGeneration, NULL); - if (nGeneration == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - } else { - nGeneration[0] = generation1; - env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; } - free(nPorts); return jStatus; } @@ -1687,64 +1615,56 @@ android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, ALOGV("createAudioPatch"); if (jPatches == NULL || jSources == NULL || jSinks == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (env->GetArrayLength(jPatches) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint numSources = env->GetArrayLength(jSources); if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint numSinks = env->GetArrayLength(jSinks); if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - audio_patch_handle_t handle = (audio_patch_handle_t)0; - jobject jPatch = env->GetObjectArrayElement(jPatches, 0); - jobject jPatchHandle = NULL; - if (jPatch != NULL) { - if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + audio_patch_handle_t handle = static_cast<audio_patch_handle_t>(AUDIO_PATCH_HANDLE_NONE); + ScopedLocalRef<jobject> jPatch(env, env->GetObjectArrayElement(jPatches, 0)); + ScopedLocalRef<jobject> jPatchHandle(env, nullptr); + if (jPatch != nullptr) { + if (!env->IsInstanceOf(jPatch.get(), gAudioPatchClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); - handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + jPatchHandle.reset(env->GetObjectField(jPatch.get(), gAudioPatchFields.mHandle)); + handle = static_cast<audio_patch_handle_t>( + env->GetIntField(jPatchHandle.get(), gAudioHandleFields.mId)); } struct audio_patch nPatch = { .id = handle }; - jobject jSource = NULL; - jobject jSink = NULL; - for (jint i = 0; i < numSources; i++) { - jSource = env->GetObjectArrayElement(jSources, i); - if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jSource(env, env->GetObjectArrayElement(jSources, i)); + if (!env->IsInstanceOf(jSource.get(), gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource, false); - env->DeleteLocalRef(jSource); - jSource = NULL; + jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource.get(), false); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } nPatch.num_sources++; } for (jint i = 0; i < numSinks; i++) { - jSink = env->GetObjectArrayElement(jSinks, i); - if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jSink(env, env->GetObjectArrayElement(jSinks, i)); + if (!env->IsInstanceOf(jSink.get(), gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink, false); - env->DeleteLocalRef(jSink); - jSink = NULL; + jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink.get(), false); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } nPatch.num_sinks++; } @@ -1755,38 +1675,22 @@ android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } - if (jPatchHandle == NULL) { - jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - handle); - if (jPatchHandle == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + if (jPatchHandle == nullptr) { + jPatchHandle.reset(env->NewObject(gAudioHandleClass, gAudioHandleCstor, handle)); + if (jPatchHandle == nullptr) { + return AUDIO_JAVA_ERROR; } - jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks); - if (jPatch == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + jPatch.reset(env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle.get(), + jSources, jSinks)); + if (jPatch == nullptr) { + return AUDIO_JAVA_ERROR; } - env->SetObjectArrayElement(jPatches, 0, jPatch); + env->SetObjectArrayElement(jPatches, 0, jPatch.get()); } else { - env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle); - } - -exit: - if (jPatchHandle != NULL) { - env->DeleteLocalRef(jPatchHandle); - } - if (jPatch != NULL) { - env->DeleteLocalRef(jPatch); - } - if (jSource != NULL) { - env->DeleteLocalRef(jSource); - } - if (jSink != NULL) { - env->DeleteLocalRef(jSink); + env->SetIntField(jPatchHandle.get(), gAudioHandleFields.mId, handle); } return jStatus; } @@ -1797,16 +1701,17 @@ android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz, { ALOGV("releaseAudioPatch"); if (jPatch == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - audio_patch_handle_t handle = (audio_patch_handle_t)0; + audio_patch_handle_t handle = static_cast<audio_patch_handle_t>(AUDIO_PATCH_HANDLE_NONE); jobject jPatchHandle = NULL; if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); - handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + handle = static_cast<audio_patch_handle_t>( + env->GetIntField(jPatchHandle, gAudioHandleFields.mId)); env->DeleteLocalRef(jPatchHandle); ALOGV("AudioSystem::releaseAudioPatch"); @@ -1823,28 +1728,22 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, ALOGV("listAudioPatches"); if (jPatches == NULL) { ALOGE("listAudioPatches NULL AudioPatch ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jPatches, gArrayListClass)) { ALOGE("listAudioPatches not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } status_t status; unsigned int generation1; unsigned int generation; unsigned int numPatches; - jint *nGeneration; - struct audio_patch *nPatches = NULL; - jobjectArray jSources = NULL; - jobject jSource = NULL; - jobjectArray jSinks = NULL; - jobject jSink = NULL; - jobject jPatch = NULL; + std::vector<audio_patch> nPatches; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1865,15 +1764,13 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, break; } if (numPatches == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return setGeneration(env, jGeneration, generation1) ? AUDIO_JAVA_SUCCESS + : AUDIO_JAVA_ERROR; } - nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch)); + nPatches.resize(numPatches); - status = AudioSystem::listAudioPatches(&numPatches, - nPatches, - &generation); + status = AudioSystem::listAudioPatches(&numPatches, nPatches.data(), &generation); ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d", numPatches, generation, generation1); @@ -1881,15 +1778,21 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } for (size_t i = 0; i < numPatches; i++) { + ScopedLocalRef<jobject> jPatch(env, nullptr); + ScopedLocalRef<jobjectArray> jSources(env, nullptr); + ScopedLocalRef<jobjectArray> jSinks(env, nullptr); jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, nPatches[i].id); if (patchHandle == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } ALOGV("listAudioPatches patch %zu num_sources %d num_sinks %d", i, nPatches[i].num_sources, nPatches[i].num_sinks); @@ -1897,96 +1800,66 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id); // load sources - jSources = env->NewObjectArray(nPatches[i].num_sources, - gAudioPortConfigClass, NULL); - if (jSources == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jSources.reset(env->NewObjectArray(nPatches[i].num_sources, gAudioPortConfigClass, NULL)); + if (jSources == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nPatches[i].num_sources; j++) { - jStatus = convertAudioPortConfigFromNative(env, - NULL, - &jSource, - &nPatches[i].sources[j]); + ScopedLocalRef<jobject> jSource(env, nullptr); + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortConfigFromNative(env, &jAudioPort, &jSource, + &nPatches[i].sources[j]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } - env->SetObjectArrayElement(jSources, j, jSource); - env->DeleteLocalRef(jSource); - jSource = NULL; + env->SetObjectArrayElement(jSources.get(), j, jSource.get()); ALOGV("listAudioPatches patch %zu source %zu is a %s handle %d", i, j, nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", nPatches[i].sources[j].id); } // load sinks - jSinks = env->NewObjectArray(nPatches[i].num_sinks, - gAudioPortConfigClass, NULL); - if (jSinks == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jSinks.reset(env->NewObjectArray(nPatches[i].num_sinks, gAudioPortConfigClass, NULL)); + if (jSinks == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nPatches[i].num_sinks; j++) { - jStatus = convertAudioPortConfigFromNative(env, - NULL, - &jSink, - &nPatches[i].sinks[j]); + ScopedLocalRef<jobject> jSink(env, nullptr); + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortConfigFromNative(env, &jAudioPort, &jSink, + &nPatches[i].sinks[j]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } - env->SetObjectArrayElement(jSinks, j, jSink); - env->DeleteLocalRef(jSink); - jSink = NULL; + env->SetObjectArrayElement(jSinks.get(), j, jSink.get()); ALOGV("listAudioPatches patch %zu sink %zu is a %s handle %d", i, j, nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", nPatches[i].sinks[j].id); } - jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, - patchHandle, jSources, jSinks); - env->DeleteLocalRef(jSources); - jSources = NULL; - env->DeleteLocalRef(jSinks); - jSinks = NULL; - if (jPatch == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jPatch.reset(env->NewObject(gAudioPatchClass, gAudioPatchCstor, patchHandle, jSources.get(), + jSinks.get())); + if (jPatch == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } - env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch); - env->DeleteLocalRef(jPatch); - jPatch = NULL; + env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch.get()); } - -exit: - - nGeneration = env->GetIntArrayElements(jGeneration, NULL); - if (nGeneration == NULL) { + if (!setGeneration(env, jGeneration, generation1)) { jStatus = AUDIO_JAVA_ERROR; - } else { - nGeneration[0] = generation1; - env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); - } - - if (jSources != NULL) { - env->DeleteLocalRef(jSources); - } - if (jSource != NULL) { - env->DeleteLocalRef(jSource); } - if (jSinks != NULL) { - env->DeleteLocalRef(jSinks); - } - if (jSink != NULL) { - env->DeleteLocalRef(jSink); - } - if (jPatch != NULL) { - env->DeleteLocalRef(jPatch); - } - free(nPatches); return jStatus; } @@ -2035,7 +1908,7 @@ android_media_AudioSystem_startAudioSource(JNIEnv *env, jobject clazz, } auto paa = JNIAudioAttributeHelper::makeUnique(); jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } audio_port_handle_t handle; @@ -2052,8 +1925,7 @@ static jint android_media_AudioSystem_stopAudioSource(JNIEnv *env, jobject clazz, jint handle) { ALOGV("stopAudioSource"); - status_t status = AudioSystem::stopAudioSource( - static_cast <audio_port_handle_t>(handle)); + status_t status = AudioSystem::stopAudioSource(static_cast<audio_port_handle_t>(handle)); ALOGV("AudioSystem::stopAudioSource() returned %d", status); return nativeToJavaStatus(status); } @@ -2085,7 +1957,7 @@ android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_getAudioHwSyncForSession(JNIEnv *env, jobject thiz, jint sessionId) { - return (jint) AudioSystem::getAudioHwSyncForSession((audio_session_t) sessionId); + return AudioSystem::getAudioHwSyncForSession(static_cast<audio_session_t>(sessionId)); } static void @@ -2204,11 +2076,11 @@ static jint convertAudioMixToNative(JNIEnv *env, { nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType); nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags); - nAudioMix->mDeviceType = (audio_devices_t) - env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType); + nAudioMix->mDeviceType = + static_cast<audio_devices_t>(env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType)); - jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioMix, - gAudioMixFields.mDeviceAddress); + jstring jDeviceAddress = + static_cast<jstring>(env->GetObjectField(jAudioMix, gAudioMixFields.mDeviceAddress)); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); nAudioMix->mDeviceAddress = String8(nDeviceAddress); env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress); @@ -2227,8 +2099,8 @@ static jint convertAudioMixToNative(JNIEnv *env, nAudioMix->mVoiceCommunicationCaptureAllowed = env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed); env->DeleteLocalRef(jRule); - jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria, - gArrayListMethods.toArray); + jobjectArray jCriteria = static_cast<jobjectArray>( + env->CallObjectMethod(jRuleCriteria, gArrayListMethods.toArray)); env->DeleteLocalRef(jRuleCriteria); jint numCriteria = env->GetArrayLength(jCriteria); @@ -2264,8 +2136,8 @@ static jint convertAudioMixToNative(JNIEnv *env, auto paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { - return jStatus; + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) { nCriterion.mValue.mUsage = paa->usage; @@ -2283,7 +2155,7 @@ static jint convertAudioMixToNative(JNIEnv *env, env->DeleteLocalRef(jCriteria); - return (jint)AUDIO_JAVA_SUCCESS; + return AUDIO_JAVA_SUCCESS; } static jint @@ -2293,34 +2165,29 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, ALOGV("registerPolicyMixes"); if (jMixesList == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jMixesList, gArrayListClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - jobjectArray jMixes = (jobjectArray)env->CallObjectMethod(jMixesList, - gArrayListMethods.toArray); + jobjectArray jMixes = + static_cast<jobjectArray>(env->CallObjectMethod(jMixesList, gArrayListMethods.toArray)); jint numMixes = env->GetArrayLength(jMixes); if (numMixes > MAX_MIXES_PER_POLICY) { numMixes = MAX_MIXES_PER_POLICY; } status_t status; - jint jStatus; - jobject jAudioMix = NULL; Vector <AudioMix> mixes; for (jint i = 0; i < numMixes; i++) { - jAudioMix = env->GetObjectArrayElement(jMixes, i); - if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jAudioMix(env, env->GetObjectArrayElement(jMixes, i)); + if (!env->IsInstanceOf(jAudioMix.get(), gAudioMixClass)) { + return AUDIO_JAVA_BAD_VALUE; } AudioMix mix; - jStatus = convertAudioMixToNative(env, &mix, jAudioMix); - env->DeleteLocalRef(jAudioMix); - jAudioMix = NULL; - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (jint jStatus = convertAudioMixToNative(env, &mix, jAudioMix.get()); + jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } mixes.add(mix); } @@ -2329,16 +2196,7 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, status = AudioSystem::registerPolicyMixes(mixes, registration); ALOGV("AudioSystem::registerPolicyMixes() returned %d", status); - jStatus = nativeToJavaStatus(status); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - -exit: - if (jAudioMix != NULL) { - env->DeleteLocalRef(jAudioMix); - } - return jStatus; + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz, @@ -2348,14 +2206,14 @@ static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobjec if (results != NO_ERROR) { return results; } - status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector); - return (jint) nativeToJavaStatus(status); + status_t status = AudioSystem::setUidDeviceAffinities(uid, deviceVector); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz, jint uid) { - status_t status = AudioSystem::removeUidDeviceAffinities((uid_t) uid); - return (jint) nativeToJavaStatus(status); + status_t status = AudioSystem::removeUidDeviceAffinities(static_cast<uid_t>(uid)); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz, @@ -2366,14 +2224,14 @@ static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, job if (results != NO_ERROR) { return results; } - status_t status = AudioSystem::setUserIdDeviceAffinities((int)userId, deviceVector); - return (jint)nativeToJavaStatus(status); + status_t status = AudioSystem::setUserIdDeviceAffinities(userId, deviceVector); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_removeUserIdDeviceAffinities(JNIEnv *env, jobject clazz, jint userId) { - status_t status = AudioSystem::removeUserIdDeviceAffinities((int)userId); - return (jint)nativeToJavaStatus(status); + status_t status = AudioSystem::removeUserIdDeviceAffinities(userId); + return nativeToJavaStatus(status); } static jint @@ -2386,19 +2244,18 @@ static jfloat android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz, jint stream, jint index, jint device) { - return (jfloat)AudioSystem::getStreamVolumeDB((audio_stream_type_t)stream, - (int)index, - (audio_devices_t)device); + return AudioSystem::getStreamVolumeDB(static_cast<audio_stream_type_t>(stream), index, + static_cast<audio_devices_t>(device)); } static jint android_media_AudioSystem_getOffloadSupport(JNIEnv *env, jobject thiz, jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask, jint streamType) { audio_offload_info_t format = AUDIO_INFO_INITIALIZER; - format.format = (audio_format_t) audioFormatToNative(encoding); - format.sample_rate = (uint32_t) sampleRate; + format.format = static_cast<audio_format_t>(audioFormatToNative(encoding)); + format.sample_rate = sampleRate; format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask); - format.stream_type = (audio_stream_type_t) streamType; + format.stream_type = static_cast<audio_stream_type_t>(streamType); format.has_video = false; format.is_streaming = false; // offload duration unknown at this point: @@ -2415,11 +2272,11 @@ android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMic if (jMicrophonesInfo == NULL) { ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) { ALOGE("getMicrophones not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint jStatus; @@ -2431,7 +2288,7 @@ android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMic return jStatus; } if (microphones.size() == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; + jStatus = AUDIO_JAVA_SUCCESS; return jStatus; } for (size_t i = 0; i < microphones.size(); i++) { @@ -2453,7 +2310,7 @@ static jint android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMe jint jStatus = AUDIO_JAVA_SUCCESS; if (!env->IsInstanceOf(jEncodingFormatList, gArrayListClass)) { ALOGE("%s: jEncodingFormatList not an ArrayList", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } std::vector<audio_format_t> encodingFormats; status_t status = @@ -2572,12 +2429,10 @@ static jint android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz, jint audioFormat, jboolean enabled) { - status_t status = AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat), - (bool)enabled); - if (status != NO_ERROR) { - ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status); - } - return (jint)nativeToJavaStatus(status); + status_t status = + AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat), enabled); + ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_getMaxChannelCount(JNIEnv *env, jobject thiz) { @@ -2618,7 +2473,7 @@ static jint android_media_AudioSystem_setAssistantServicesUids(JNIEnv *env, jobj status_t status = AudioSystem::setAssistantServicesUids(nativeUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setActiveAssistantServicesUids(JNIEnv *env, jobject thiz, @@ -2627,7 +2482,7 @@ static jint android_media_AudioSystem_setActiveAssistantServicesUids(JNIEnv *env status_t status = AudioSystem::setActiveAssistantServicesUids(nativeActiveUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint @@ -2635,12 +2490,12 @@ android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArr std::vector<uid_t> nativeUidsVector = convertJIntArrayToUidVector(env, uids); status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setCurrentImeUid(JNIEnv *env, jobject thiz, jint uid) { status_t status = AudioSystem::setCurrentImeUid(uid); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jboolean @@ -2658,7 +2513,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj std::vector<audio_usage_t> nativeSystemUsagesVector; if (systemUsages == nullptr) { - return (jint) AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } int *nativeSystemUsages = nullptr; @@ -2675,7 +2530,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj } status_t status = AudioSystem::setSupportedSystemUsages(nativeSystemUsagesVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint @@ -2686,16 +2541,16 @@ android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jin static jint android_media_AudioSystem_setRttEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return (jint) check_AudioSystem_Command(AudioSystem::setRttEnabled(enabled)); + return check_AudioSystem_Command(AudioSystem::setRttEnabled(enabled)); } static jint android_media_AudioSystem_setAudioHalPids(JNIEnv *env, jobject clazz, jintArray jPids) { if (jPids == NULL) { - return (jint) AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - pid_t *nPidsArray = (pid_t *) env->GetIntArrayElements(jPids, NULL); + pid_t *nPidsArray = reinterpret_cast<pid_t *>(env->GetIntArrayElements(jPids, nullptr)); std::vector<pid_t> nPids(nPidsArray, nPidsArray + env->GetArrayLength(jPids)); status_t status = AudioSystem::setAudioHalPids(nPids); env->ReleaseIntArrayElements(jPids, nPidsArray, 0); @@ -2719,9 +2574,9 @@ static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, job return results; } int status = check_AudioSystem_Command( - AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); - return (jint) status; + AudioSystem::setDevicesRoleForStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); + return status; } static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz, @@ -2734,8 +2589,8 @@ static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, return results; } int status = check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); + AudioSystem::removeDevicesRoleForStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); return (jint)status; } @@ -2753,10 +2608,10 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject jDevices) { AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); + AudioSystem::getDevicesForRoleAndStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); if (status != NO_ERROR) { - return (jint) status; + return status; } for (const auto &device : nDevices) { jobject jAudioDeviceAttributes = NULL; @@ -2779,9 +2634,10 @@ static jint android_media_AudioSystem_setDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::setDevicesRoleForCapturePreset(static_cast<audio_source_t>(capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( @@ -2793,9 +2649,10 @@ static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::addDevicesRoleForCapturePreset(static_cast<audio_source_t>(capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( @@ -2807,17 +2664,20 @@ static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::removeDevicesRoleForCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz, jint capturePreset, jint role) { - return (jint)check_AudioSystem_Command( - AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role)); + return static_cast<jint>(check_AudioSystem_Command( + AudioSystem::clearDevicesRoleForCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role)))); } static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz, @@ -2826,10 +2686,12 @@ static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv * jobject jDevices) { AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); + AudioSystem::getDevicesForRoleAndCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role), + nDevices)); if (status != NO_ERROR) { - return (jint)status; + return status; } for (const auto &device : nDevices) { jobject jAudioDeviceAttributes = NULL; @@ -2854,12 +2716,12 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje // components call this method often if (jDeviceArray == nullptr || maxResultSize == 0) { ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint) AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } @@ -2888,7 +2750,7 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz, jobject jVibrators) { if (!env->IsInstanceOf(jVibrators, gListClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } const jint size = env->CallIntMethod(jVibrators, gListMethods.size); std::vector<media::AudioVibratorInfo> vibratorInfos; @@ -2896,7 +2758,7 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz ScopedLocalRef<jobject> jVibrator(env, env->CallObjectMethod(jVibrators, gListMethods.get, i)); if (!env->IsInstanceOf(jVibrator.get(), gVibratorClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } media::AudioVibratorInfo vibratorInfo; vibratorInfo.id = env->CallIntMethod(jVibrator.get(), gVibratorMethods.getId); @@ -2907,7 +2769,7 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getMaxAmplitude); vibratorInfos.push_back(vibratorInfo); } - return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos)); + return check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos)); } static jobject android_media_AudioSystem_getSpatializer(JNIEnv *env, jobject thiz, @@ -2929,8 +2791,8 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject jobjectArray jDeviceArray) { JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { - return false; + if (jStatus != AUDIO_JAVA_SUCCESS) { + return false; } AudioDeviceTypeAddrVector nDevices; @@ -2943,7 +2805,7 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject return false; } jStatus = createAudioDeviceTypeAddrFromJava(env, &device, jDevice); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return false; } nDevices.push_back(device); @@ -3000,7 +2862,7 @@ static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobj jobject jFormat, jobject jaa) { JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return DIRECT_NOT_SUPPORTED; } @@ -3023,20 +2885,20 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env if (jAudioAttributes == nullptr) { ALOGE("jAudioAttributes is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jAudioProfilesList == nullptr) { ALOGE("jAudioProfilesList is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jAudioProfilesList, gArrayListClass)) { ALOGE("jAudioProfilesList not an ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } @@ -3049,7 +2911,7 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env } for (const auto &audioProfile : audioProfiles) { - jobject jAudioProfile; + ScopedLocalRef<jobject> jAudioProfile(env); jint jConvertProfileStatus = convertAudioProfileFromNative( env, &jAudioProfile, &audioProfile, false); if (jConvertProfileStatus == AUDIO_JAVA_BAD_VALUE) { @@ -3059,8 +2921,7 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env if (jConvertProfileStatus != AUDIO_JAVA_SUCCESS) { return jConvertProfileStatus; } - env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile); - env->DeleteLocalRef(jAudioProfile); + env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile.get()); } return jStatus; } @@ -3212,8 +3073,7 @@ static jboolean android_media_AudioSystem_supportsBluetoothVariableLatency(JNIEn static int android_media_AudioSystem_setBluetoothVariableLatencyEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return (jint)check_AudioSystem_Command( - AudioSystem::setBluetoothVariableLatencyEnabled(enabled)); + return check_AudioSystem_Command(AudioSystem::setBluetoothVariableLatencyEnabled(enabled)); } static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIEnv *env, @@ -3227,191 +3087,182 @@ static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIE // ---------------------------------------------------------------------------- -static const JNINativeMethod gMethods[] = - {{"setParameters", "(Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setParameters}, - {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", - (void *)android_media_AudioSystem_getParameters}, - {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, - {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, - {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, - {"isStreamActiveRemotely", "(II)Z", - (void *)android_media_AudioSystem_isStreamActiveRemotely}, - {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, - {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, - {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId}, - {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId}, - {"setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", - (void *)android_media_AudioSystem_setDeviceConnectionState}, - {"getDeviceConnectionState", "(ILjava/lang/String;)I", - (void *)android_media_AudioSystem_getDeviceConnectionState}, - {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I", - (void *)android_media_AudioSystem_handleDeviceConfigChange}, - {"setPhoneState", "(II)I", (void *)android_media_AudioSystem_setPhoneState}, - {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse}, - {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse}, - {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume}, - {"setStreamVolumeIndex", "(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex}, - {"getStreamVolumeIndex", "(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex}, - {"setVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;II)I", - (void *)android_media_AudioSystem_setVolumeIndexForAttributes}, - {"getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I", - (void *)android_media_AudioSystem_getVolumeIndexForAttributes}, - {"getMinVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes}, - {"getMaxVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes}, - {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume}, - {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume}, - {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute}, - {"getMasterMute", "()Z", (void *)android_media_AudioSystem_getMasterMute}, - {"setMasterMono", "(Z)I", (void *)android_media_AudioSystem_setMasterMono}, - {"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono}, - {"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance}, - {"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance}, - {"getPrimaryOutputSamplingRate", "()I", - (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate}, - {"getPrimaryOutputFrameCount", "()I", - (void *)android_media_AudioSystem_getPrimaryOutputFrameCount}, - {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency}, - {"setLowRamDevice", "(ZJ)I", (void *)android_media_AudioSystem_setLowRamDevice}, - {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger}, - {"setAudioFlingerBinder", "(Landroid/os/IBinder;)V", - (void *)android_media_AudioSystem_setAudioFlingerBinder}, - {"listAudioPorts", "(Ljava/util/ArrayList;[I)I", - (void *)android_media_AudioSystem_listAudioPorts}, - {"createAudioPatch", - "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/" - "AudioPortConfig;)I", - (void *)android_media_AudioSystem_createAudioPatch}, - {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I", - (void *)android_media_AudioSystem_releaseAudioPatch}, - {"listAudioPatches", "(Ljava/util/ArrayList;[I)I", - (void *)android_media_AudioSystem_listAudioPatches}, - {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", - (void *)android_media_AudioSystem_setAudioPortConfig}, - {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_startAudioSource}, - {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource}, - {"getAudioHwSyncForSession", "(I)I", - (void *)android_media_AudioSystem_getAudioHwSyncForSession}, - {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", - (void *)android_media_AudioSystem_registerPolicyMixes}, - {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setUidDeviceAffinities}, - {"removeUidDeviceAffinities", "(I)I", - (void *)android_media_AudioSystem_removeUidDeviceAffinities}, - {"native_register_dynamic_policy_callback", "()V", - (void *)android_media_AudioSystem_registerDynPolicyCallback}, - {"native_register_recording_callback", "()V", - (void *)android_media_AudioSystem_registerRecordingCallback}, - {"native_register_routing_callback", "()V", - (void *)android_media_AudioSystem_registerRoutingCallback}, - {"native_register_vol_range_init_req_callback", "()V", - (void *)android_media_AudioSystem_registerVolRangeInitReqCallback}, - {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, - {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, - {"native_get_offload_support", "(IIIII)I", - (void *)android_media_AudioSystem_getOffloadSupport}, - {"getMicrophones", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getMicrophones}, - {"getSurroundFormats", "(Ljava/util/Map;)I", - (void *)android_media_AudioSystem_getSurroundFormats}, - {"getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getReportedSurroundFormats}, - {"setSurroundFormatEnabled", "(IZ)I", - (void *)android_media_AudioSystem_setSurroundFormatEnabled}, - {"setAssistantServicesUids", "([I)I", - (void *)android_media_AudioSystem_setAssistantServicesUids}, - {"setActiveAssistantServicesUids", "([I)I", - (void *)android_media_AudioSystem_setActiveAssistantServicesUids}, - {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, - {"isHapticPlaybackSupported", "()Z", - (void *)android_media_AudioSystem_isHapticPlaybackSupported}, - {"isUltrasoundSupported", "()Z", (void *)android_media_AudioSystem_isUltrasoundSupported}, - {"getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia}, - {"setSupportedSystemUsages", "([I)I", - (void *)android_media_AudioSystem_setSupportedSystemUsages}, - {"setAllowedCapturePolicy", "(II)I", - (void *)android_media_AudioSystem_setAllowedCapturePolicy}, - {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled}, - {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids}, - {"isCallScreeningModeSupported", "()Z", - (void *)android_media_AudioSystem_isCallScreeningModeSupported}, - {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setDevicesRoleForStrategy}, - {"removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_removeDevicesRoleForStrategy}, - {"clearDevicesRoleForStrategy", "(II)I", - (void *)android_media_AudioSystem_clearDevicesRoleForStrategy}, - {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", - (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy}, - {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset}, - {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset}, - {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset}, - {"clearDevicesRoleForCapturePreset", "(II)I", - (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset}, - {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", - (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset}, - {"getDevicesForAttributes", - "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;Z)I", - (void *)android_media_AudioSystem_getDevicesForAttributes}, - {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setUserIdDeviceAffinities}, - {"removeUserIdDeviceAffinities", "(I)I", - (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}, - {"setCurrentImeUid", "(I)I", (void *)android_media_AudioSystem_setCurrentImeUid}, - {"setVibratorInfos", "(Ljava/util/List;)I", - (void *)android_media_AudioSystem_setVibratorInfos}, - {"nativeGetSpatializer", - "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", - (void *)android_media_AudioSystem_getSpatializer}, - {"canBeSpatialized", - "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" - "[Landroid/media/AudioDeviceAttributes;)Z", - (void *)android_media_AudioSystem_canBeSpatialized}, - {"nativeGetSoundDose", "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", - (void *)android_media_AudioSystem_nativeGetSoundDose}, - {"getDirectPlaybackSupport", - "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getDirectPlaybackSupport}, - {"getDirectProfilesForAttributes", - "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getDirectProfilesForAttributes}, - {"getSupportedMixerAttributes", "(ILjava/util/List;)I", - (void *)android_media_AudioSystem_getSupportedMixerAttributes}, - {"setPreferredMixerAttributes", - "(Landroid/media/AudioAttributes;IILandroid/media/AudioMixerAttributes;)I", - (void *)android_media_AudioSystem_setPreferredMixerAttributes}, - {"getPreferredMixerAttributes", "(Landroid/media/AudioAttributes;ILjava/util/List;)I", - (void *)android_media_AudioSystem_getPreferredMixerAttributes}, - {"clearPreferredMixerAttributes", "(Landroid/media/AudioAttributes;II)I", - (void *)android_media_AudioSystem_clearPreferredMixerAttributes}, - {"supportsBluetoothVariableLatency", "()Z", - (void *)android_media_AudioSystem_supportsBluetoothVariableLatency}, - {"setBluetoothVariableLatencyEnabled", "(Z)I", - (void *)android_media_AudioSystem_setBluetoothVariableLatencyEnabled}, - {"isBluetoothVariableLatencyEnabled", "()Z", - (void *)android_media_AudioSystem_isBluetoothVariableLatencyEnabled}}; - -static const JNINativeMethod gEventHandlerMethods[] = { - {"native_setup", - "(Ljava/lang/Object;)V", - (void *)android_media_AudioSystem_eventHandlerSetup}, - {"native_finalize", - "()V", - (void *)android_media_AudioSystem_eventHandlerFinalize}, -}; +#define MAKE_AUDIO_SYSTEM_METHOD(x) \ + MAKE_JNI_NATIVE_METHOD_AUTOSIG(#x, android_media_AudioSystem_##x) -static const JNINativeMethod gFrameworkCapabilities[] = { - {"native_getMaxChannelCount", "()I", (void *)android_media_AudioSystem_getMaxChannelCount}, - {"native_getMaxSampleRate", "()I", (void *)android_media_AudioSystem_getMaxSampleRate}, - {"native_getMinSampleRate", "()I", (void *)android_media_AudioSystem_getMinSampleRate}, -}; +static const JNINativeMethod gMethods[] = + {MAKE_AUDIO_SYSTEM_METHOD(setParameters), + MAKE_AUDIO_SYSTEM_METHOD(getParameters), + MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone), + MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActive), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely), + MAKE_AUDIO_SYSTEM_METHOD(isSourceActive), + MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId), + MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", + android_media_AudioSystem_setDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange), + MAKE_AUDIO_SYSTEM_METHOD(setPhoneState), + MAKE_AUDIO_SYSTEM_METHOD(setForceUse), + MAKE_AUDIO_SYSTEM_METHOD(getForceUse), + MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume), + MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex), + MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_setVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;I)I", + android_media_AudioSystem_getVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMinVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMaxVolumeIndexForAttributes), + MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount), + MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency), + MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice), + MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger), + MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V", + android_media_AudioSystem_setAudioFlingerBinder), + MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPorts), + MAKE_JNI_NATIVE_METHOD("createAudioPatch", + "([Landroid/media/AudioPatch;[Landroid/media/" + "AudioPortConfig;[Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_createAudioPatch), + MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I", + android_media_AudioSystem_releaseAudioPatch), + MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPatches), + MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_setAudioPortConfig), + MAKE_JNI_NATIVE_METHOD("startAudioSource", + "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_startAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession), + MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", + android_media_AudioSystem_registerPolicyMixes), + MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUidDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback", + android_media_AudioSystem_registerDynPolicyCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback", + android_media_AudioSystem_registerRecordingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback", + android_media_AudioSystem_registerRoutingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback", + android_media_AudioSystem_registerVolRangeInitReqCallback), + MAKE_AUDIO_SYSTEM_METHOD(systemReady), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support", + android_media_AudioSystem_getOffloadSupport), + MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getMicrophones), + MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I", + android_media_AudioSystem_getSurroundFormats), + MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getReportedSurroundFormats), + MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported), + MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported), + MAKE_JNI_NATIVE_METHOD( + "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", + android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia), + MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages), + MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy), + MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids), + MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForStrategy), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndStrategy), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_addDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForCapturePreset), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes", + "(Landroid/media/AudioAttributes;[Landroid/media/" + "AudioDeviceAttributes;Z)I", + android_media_AudioSystem_getDevicesForAttributes), + MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid), + MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I", + android_media_AudioSystem_setVibratorInfos), + MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer", + "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_getSpatializer), + MAKE_JNI_NATIVE_METHOD("canBeSpatialized", + "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" + "[Landroid/media/AudioDeviceAttributes;)Z", + android_media_AudioSystem_canBeSpatialized), + MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose", + "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_nativeGetSoundDose), + MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport", + "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getDirectPlaybackSupport), + MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes", + "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", + android_media_AudioSystem_getDirectProfilesForAttributes), + MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I", + android_media_AudioSystem_getSupportedMixerAttributes), + MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;IILandroid/media/" + "AudioMixerAttributes;)I", + android_media_AudioSystem_setPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;ILjava/util/List;)I", + android_media_AudioSystem_getPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_clearPreferredMixerAttributes), + MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency), + MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled), + MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled)}; + +static const JNINativeMethod gEventHandlerMethods[] = + {MAKE_JNI_NATIVE_METHOD("native_setup", "(Ljava/lang/Object;)V", + android_media_AudioSystem_eventHandlerSetup), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_finalize", + android_media_AudioSystem_eventHandlerFinalize)}; + +static const JNINativeMethod gFrameworkCapabilities[] = + {MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMaxChannelCount", + android_media_AudioSystem_getMaxChannelCount), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMaxSampleRate", + android_media_AudioSystem_getMaxSampleRate), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMinSampleRate", + android_media_AudioSystem_getMinSampleRate)}; int register_android_media_AudioSystem(JNIEnv *env) { @@ -3589,7 +3440,7 @@ int register_android_media_AudioSystem(JNIEnv *env) gClsAudioTrackRoutingProxy = android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy"); // make sure this reference doesn't get deleted - gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy); + gClsAudioTrackRoutingProxy = static_cast<jclass>(env->NewGlobalRef(gClsAudioTrackRoutingProxy)); gMidAudioTrackRoutingProxy_ctor = android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V"); @@ -3600,7 +3451,8 @@ int register_android_media_AudioSystem(JNIEnv *env) gClsAudioRecordRoutingProxy = android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy"); // make sure this reference doesn't get deleted - gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy); + gClsAudioRecordRoutingProxy = + static_cast<jclass>(env->NewGlobalRef(gClsAudioRecordRoutingProxy)); gMidAudioRecordRoutingProxy_ctor = android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V"); diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 8fc30d1c248d..afc3cbd15f88 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -50,7 +50,7 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, appPackageNameChars.c_str(), vulkanVersion); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver, +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useSystemAngle, jstring packageName, jobjectArray featuresObj) { ScopedUtfChars pathChars(env, path); ScopedUtfChars packageNameChars(env, packageName); @@ -73,7 +73,7 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useN } } - android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver, + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useSystemAngle, packageNameChars.c_str(), features); } @@ -118,7 +118,7 @@ const JNINativeMethod g_methods[] = { reinterpret_cast<void*>(setGpuStats_native)}, {"setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)}, - {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", + {"setAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", reinterpret_cast<void*>(setAngleInfo_native)}, {"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native)}, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 8bc52b874ae0..c19879713f2f 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -1305,103 +1305,6 @@ static jboolean android_os_BinderProxy_isBinderAlive(JNIEnv* env, jobject obj) return alive ? JNI_TRUE : JNI_FALSE; } -static int getprocname(pid_t pid, char *buf, size_t len) { - char filename[32]; - FILE *f; - - snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid); - f = fopen(filename, "re"); - if (!f) { - *buf = '\0'; - return 1; - } - if (!fgets(buf, len, f)) { - *buf = '\0'; - fclose(f); - return 2; - } - fclose(f); - return 0; -} - -static bool push_eventlog_string(char** pos, const char* end, const char* str) { - jint len = strlen(str); - int space_needed = 1 + sizeof(len) + len; - if (end - *pos < space_needed) { - ALOGW("not enough space for string. remain=%" PRIdPTR "; needed=%d", - end - *pos, space_needed); - return false; - } - **pos = EVENT_TYPE_STRING; - (*pos)++; - memcpy(*pos, &len, sizeof(len)); - *pos += sizeof(len); - memcpy(*pos, str, len); - *pos += len; - return true; -} - -static bool push_eventlog_int(char** pos, const char* end, jint val) { - int space_needed = 1 + sizeof(val); - if (end - *pos < space_needed) { - ALOGW("not enough space for int. remain=%" PRIdPTR "; needed=%d", - end - *pos, space_needed); - return false; - } - **pos = EVENT_TYPE_INT; - (*pos)++; - memcpy(*pos, &val, sizeof(val)); - *pos += sizeof(val); - return true; -} - -// From frameworks/base/core/java/android/content/EventLogTags.logtags: - -static const bool kEnableBinderSample = false; - -#define LOGTAG_BINDER_OPERATION 52004 - -static void conditionally_log_binder_call(int64_t start_millis, - IBinder* target, jint code) { - int duration_ms = static_cast<int>(uptimeMillis() - start_millis); - - int sample_percent; - if (duration_ms >= 500) { - sample_percent = 100; - } else { - sample_percent = 100 * duration_ms / 500; - if (sample_percent == 0) { - return; - } - if (sample_percent < (random() % 100 + 1)) { - return; - } - } - - char process_name[40]; - getprocname(getpid(), process_name, sizeof(process_name)); - String8 desc(target->getInterfaceDescriptor()); - - char buf[LOGGER_ENTRY_MAX_PAYLOAD]; - buf[0] = EVENT_TYPE_LIST; - buf[1] = 5; - char* pos = &buf[2]; - char* end = &buf[LOGGER_ENTRY_MAX_PAYLOAD - 1]; // leave room for final \n - if (!push_eventlog_string(&pos, end, desc.string())) return; - if (!push_eventlog_int(&pos, end, code)) return; - if (!push_eventlog_int(&pos, end, duration_ms)) return; - if (!push_eventlog_string(&pos, end, process_name)) return; - if (!push_eventlog_int(&pos, end, sample_percent)) return; - *(pos++) = '\n'; // conventional with EVENT_TYPE_LIST apparently. - android_bWriteLog(LOGTAG_BINDER_OPERATION, buf, pos - buf); -} - -// We only measure binder call durations to potentially log them if -// we're on the main thread. -static bool should_time_binder_calls() { - return (getpid() == gettid()); -} - static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException { @@ -1428,29 +1331,10 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n", target, obj, code); - - bool time_binder_calls; - int64_t start_millis; - if (kEnableBinderSample) { - // Only log the binder call duration for things on the Java-level main thread. - // But if we don't - time_binder_calls = should_time_binder_calls(); - - if (time_binder_calls) { - start_millis = uptimeMillis(); - } - } - //printf("Transact from Java code to %p sending: ", target); data->print(); status_t err = target->transact(code, *data, reply, flags); //if (reply) printf("Transact from Java code to %p received: ", target); reply->print(); - if (kEnableBinderSample) { - if (time_binder_calls) { - conditionally_log_binder_call(start_millis, target, code); - } - } - if (err == NO_ERROR) { return JNI_TRUE; } else if (err == UNKNOWN_TRANSACTION) { diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index 0525443ecf82..0c0747060f48 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.util.Property; import android.view.View; +import androidx.annotation.NonNull; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.rule.ActivityTestRule; @@ -36,6 +37,8 @@ import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @SmallTest public class AnimatorSetActivityTest { @@ -613,6 +616,68 @@ public class AnimatorSetActivityTest { }); } + @Test + public void initAfterStartNotification() throws Throwable { + Property<int[], Integer> property = new Property<>(Integer.class, "firstValue") { + @Override + public Integer get(int[] target) { + throw new IllegalStateException("Shouldn't be called"); + } + + @Override + public void set(int[] target, Integer value) { + target[0] = value; + } + }; + int[] target = new int[1]; + ObjectAnimator animator1 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator2 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator3 = ObjectAnimator.ofInt(target, property, 0, 100); + animator1.setDuration(10); + animator2.setDuration(10); + animator3.setDuration(10); + AnimatorSet set = new AnimatorSet(); + set.playSequentially(animator1, animator2, animator3); + final int[] values = new int[4]; + animator2.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[0] = target[0]; + animator2.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[1] = target[0]; + } + }); + animator3.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[2] = target[0]; + animator3.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[3] = target[0]; + } + }); + final CountDownLatch latch = new CountDownLatch(1); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + latch.countDown(); + } + }); + mActivityRule.runOnUiThread(() -> set.start()); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertEquals(100, values[0]); + assertEquals(200, values[1]); + assertEquals(200, values[2]); + assertEquals(300, values[3]); + } + /** * Check that the animator list contains exactly the given animators and nothing else. */ diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index b73a87c8f0d9..48577416b3d0 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -884,12 +884,13 @@ public class ActivityThreadTest { mConfig.setTo(config); ++mNumOfConfigChanges; - if (mConfigLatch != null) { + final CountDownLatch configLatch = mConfigLatch; + if (configLatch != null) { if (mTestLatch != null) { mTestLatch.countDown(); } try { - mConfigLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); + configLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new IllegalStateException(e); } diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index 89632a46267e..2a4ca79d997e 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -25,8 +25,6 @@ import junit.framework.TestCase; import java.io.File; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -869,84 +867,90 @@ public class UriTest extends TestCase { return (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null); } - /** Attempting to unparcel a legacy parcel format of Uri.{,Path}Part should fail. */ - public void testUnparcelLegacyPart_fails() throws Exception { - assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$Part")); - assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$PathPart")); - } - - private static void assertUnparcelLegacyPart_fails(Class partClass) throws Exception { - Parcel parcel = Parcel.obtain(); - parcel.writeInt(0 /* BOTH */); - parcel.writeString("encoded"); - parcel.writeString("decoded"); - parcel.setDataPosition(0); - - Method readFromMethod = partClass.getDeclaredMethod("readFrom", Parcel.class); - readFromMethod.setAccessible(true); - try { - readFromMethod.invoke(null, parcel); - fail(); - } catch (InvocationTargetException expected) { - Throwable targetException = expected.getTargetException(); - // Check that the exception was thrown for the correct reason. - assertEquals("Unknown representation: 0", targetException.getMessage()); - } finally { - parcel.recycle(); - } - } - - private Uri buildUriFromRawParcel(boolean argumentsEncoded, + private Uri buildUriFromParts(boolean argumentsEncoded, String scheme, String authority, String path, String query, String fragment) { - // Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}). - final int representation = argumentsEncoded ? 1 : 2; - Parcel parcel = Parcel.obtain(); - try { - parcel.writeInt(3); // hierarchical - parcel.writeString8(scheme); - parcel.writeInt(representation); - parcel.writeString8(authority); - parcel.writeInt(representation); - parcel.writeString8(path); - parcel.writeInt(representation); - parcel.writeString8(query); - parcel.writeInt(representation); - parcel.writeString8(fragment); - parcel.setDataPosition(0); - return Uri.CREATOR.createFromParcel(parcel); - } finally { - parcel.recycle(); + final Uri.Builder builder = new Uri.Builder(); + builder.scheme(scheme); + if (argumentsEncoded) { + builder.encodedAuthority(authority); + builder.encodedPath(path); + builder.encodedQuery(query); + builder.encodedFragment(fragment); + } else { + builder.authority(authority); + builder.path(path); + builder.query(query); + builder.fragment(fragment); } + return builder.build(); } public void testUnparcelMalformedPath() { // Regression tests for b/171966843. // Test cases with arguments encoded (covering testing `scheme` * `authority` options). - Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null); + Uri uri0 = buildUriFromParts(true, "https", "google.com", "@evil.com", null, null); assertEquals("https://google.com/@evil.com", uri0.toString()); - Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x"); + Uri uri1 = buildUriFromParts(true, null, "google.com", "@evil.com", "name=spark", "x"); assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString()); - Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null); + Uri uri2 = buildUriFromParts(true, "http:", null, "@evil.com", null, null); assertEquals("http::/@evil.com", uri2.toString()); - Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null); + Uri uri3 = buildUriFromParts(true, null, null, "@evil.com", null, null); assertEquals("@evil.com", uri3.toString()); // Test cases with arguments not encoded (covering testing `scheme` * `authority` options). - Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null); + Uri uriA = buildUriFromParts(false, "https", "google.com", "@evil.com", null, null); assertEquals("https://google.com/%40evil.com", uriA.toString()); - Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null); + Uri uriB = buildUriFromParts(false, null, "google.com", "@evil.com", null, null); assertEquals("//google.com/%40evil.com", uriB.toString()); - Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null); + Uri uriC = buildUriFromParts(false, "http:", null, "@evil.com", null, null); assertEquals("http::/%40evil.com", uriC.toString()); - Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y"); + Uri uriD = buildUriFromParts(false, null, null, "@evil.com", "name=spark", "y"); assertEquals("%40evil.com?name%3Dspark#y", uriD.toString()); } + public void testParsedUriFromStringEquality() { + Uri uri = buildUriFromParts( + true, "https", "google.com", "@evil.com", null, null); + assertEquals(uri, Uri.parse(uri.toString())); + Uri uri2 = buildUriFromParts( + true, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null); + assertEquals(uri2, Uri.parse(uri2.toString())); + Uri uri3 = buildUriFromParts( + false, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null); + assertEquals(uri3, Uri.parse(uri3.toString())); + } + + public void testParceledUrisAreEqual() { + Uri opaqueUri = Uri.fromParts("fake://uri#", "ssp", "fragment"); + Parcel parcel = Parcel.obtain(); + try { + opaqueUri.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel); + Uri parsedUri = Uri.parse(postParcelUri.toString()); + assertEquals(parsedUri.getScheme(), postParcelUri.getScheme()); + } finally { + parcel.recycle(); + } + + Uri hierarchicalUri = new Uri.Builder().scheme("fake://uri#").authority("auth").build(); + parcel = Parcel.obtain(); + try { + hierarchicalUri.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel); + Uri parsedUri = Uri.parse(postParcelUri.toString()); + assertEquals(parsedUri.getScheme(), postParcelUri.getScheme()); + } finally { + parcel.recycle(); + } + } + public void testToSafeString() { checkToSafeString("tel:xxxxxx", "tel:Google"); checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890"); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 76e0e1eb7a95..55eabb039c01 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 3; + return 4; } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 18497ad249ee..381e9d472f0f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -32,7 +32,7 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull - private final TaskFragmentContainer mPrimaryContainer; + private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @NonNull @@ -46,17 +46,35 @@ class SplitContainer { @NonNull private final IBinder mToken; + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + private final boolean mIsPrimaryContainerMutable; + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes) { + this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes, + false /* isPrimaryContainerMutable */); + } + + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull Activity primaryActivity, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitRule splitRule, + @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; mToken = new Binder("SplitContainer"); + mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -74,6 +92,13 @@ class SplitContainer { } } + void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { + if (!mIsPrimaryContainerMutable) { + throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); + } + mPrimaryContainer = primaryContainer; + } + @NonNull TaskFragmentContainer getPrimaryContainer() { return mPrimaryContainer; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 4cedd41e2d9a..a2f75e099465 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -213,6 +213,56 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @Override + public boolean pinTopActivityStack(int taskId, @NonNull SplitPinRule splitPinRule) { + synchronized (mLock) { + final TaskContainer task = getTaskContainer(taskId); + if (task == null) { + Log.e(TAG, "Cannot find the task for id: " + taskId); + return false; + } + + final TaskFragmentContainer topContainer = + task.getTopNonFinishingTaskFragmentContainer(); + // Cannot pin the TaskFragment if no other TaskFragment behind it. + if (topContainer == null || task.indexOf(topContainer) <= 0) { + Log.w(TAG, "Cannot find an ActivityStack to pin or split"); + return false; + } + // Abort if the top container is already pinned. + if (task.getSplitPinContainer() != null) { + Log.w(TAG, "There is already a pinned ActivityStack."); + return false; + } + + // Find a valid adjacent TaskFragmentContainer + final TaskFragmentContainer primaryContainer = + task.getNonFinishingTaskFragmentContainerBelow(topContainer); + if (primaryContainer == null) { + Log.w(TAG, "Cannot find another ActivityStack to split"); + return false; + } + + // Registers a Split + final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer, + topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes()); + task.addSplitContainer(splitPinContainer); + + // Updates the Split + final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + mPresenter.updateSplitContainer(splitPinContainer, wct); + transactionRecord.apply(false /* shouldApplyIndependently */); + updateCallbackIfNecessary(); + return true; + } + } + + @Override + public void unpinTopActivityStack(int taskId){ + // TODO + } + + @Override public void setSplitAttributesCalculator( @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) { synchronized (mLock) { @@ -672,7 +722,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (targetContainer == null) { // When there is no embedding rule matched, try to place it in the top container // like a normal launch. - targetContainer = taskContainer.getTopTaskFragmentContainer(); + targetContainer = taskContainer.getTopNonFinishingTaskFragmentContainer(); } if (targetContainer == null) { return; @@ -791,7 +841,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container != null - && container.getTaskContainer().getTopTaskFragmentContainer() != container) { + && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer() + != container) { // Do not resolve if the launched activity is not the top-most container in the Task. return true; } @@ -888,7 +939,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer == null) { return; } - final TaskFragmentContainer targetContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer targetContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); if (targetContainer == null) { return; } @@ -1213,11 +1265,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // 3. Whether the top activity (if any) should be split with the new activity intent. final TaskContainer taskContainer = getTaskContainer(taskId); - if (taskContainer == null || taskContainer.getTopTaskFragmentContainer() == null) { + if (taskContainer == null + || taskContainer.getTopNonFinishingTaskFragmentContainer() == null) { // There is no other activity in the Task to check split with. return null; } - final TaskFragmentContainer topContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer topContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); final Activity topActivity = topContainer.getTopNonFinishingActivity(); if (topActivity != null && topActivity != launchingActivity) { final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct, @@ -1567,6 +1621,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // background. return; } + final SplitContainer splitContainer = getActiveSplitForContainer(container); + if (splitContainer instanceof SplitPinContainer + && updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */)) { + // A SplitPinContainer exists and is updated. + return; + } if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. @@ -1579,7 +1639,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // If the info is not available yet the task fragment will be expanded when it's ready return; } - SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer == null) { return; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java new file mode 100644 index 000000000000..03c77a089012 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java @@ -0,0 +1,47 @@ +/* + * 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 androidx.window.extensions.embedding; + +import androidx.annotation.NonNull; + +/** + * Client-side descriptor of a split that holds two containers while the secondary + * container is pinned on top of the Task and the primary container is the container that is + * currently below the secondary container. The primary container could be updated to + * another container whenever the existing primary container is removed or no longer + * be the container that's right behind the secondary container. + */ +class SplitPinContainer extends SplitContainer { + + SplitPinContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitPinRule splitPinRule, + @NonNull SplitAttributes splitAttributes) { + super(primaryContainer, primaryContainer.getTopNonFinishingActivity(), secondaryContainer, + splitPinRule, splitAttributes, true /* isPrimaryContainerMutable */); + } + + @Override + public String toString() { + return "SplitPinContainer{" + + " primaryContainer=" + getPrimaryContainer() + + " secondaryContainer=" + getSecondaryContainer() + + " splitPinRule=" + getSplitRule() + + " splitAttributes" + getCurrentSplitAttributes() + + "}"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 53d39d9fa28e..4dafbd17f379 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -17,6 +17,7 @@ package androidx.window.extensions.embedding; import static android.content.pm.PackageManager.MATCH_ALL; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import android.app.Activity; import android.app.ActivityThread; @@ -39,6 +40,7 @@ import android.view.WindowInsets; import android.view.WindowMetrics; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; +import android.window.TaskFragmentOperation; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; @@ -336,10 +338,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // value. final SplitRule rule = splitContainer.getSplitRule(); final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer(); - final Activity activity = primaryContainer.getTopNonFinishingActivity(); - if (activity == null) { - return; - } final TaskContainer taskContainer = splitContainer.getTaskContainer(); final TaskProperties taskProperties = taskContainer.getTaskProperties(); final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes(); @@ -424,6 +422,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds()); container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode()); super.createTaskFragment(wct, fragmentOptions); + + // Reorders the pinned TaskFragment to front to ensure it is the front-most TaskFragment. + final SplitPinContainer pinnedContainer = + container.getTaskContainer().getSplitPinContainer(); + if (pinnedContainer != null) { + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + wct.addTaskFragmentOperation( + pinnedContainer.getSecondaryContainer().getTaskFragmentToken(), operation); + } } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 4580c9836168..969e3ed5b9b6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -57,6 +57,10 @@ class TaskContainer { @NonNull private final List<SplitContainer> mSplitContainers = new ArrayList<>(); + /** Active pin split pair in this Task. */ + @Nullable + private SplitPinContainer mSplitPinContainer; + @NonNull private final Configuration mConfiguration; @@ -174,11 +178,28 @@ class TaskContainer { } @Nullable - TaskFragmentContainer getTopTaskFragmentContainer() { - if (mContainers.isEmpty()) { - return null; + TaskFragmentContainer getTopNonFinishingTaskFragmentContainer() { + for (int i = mContainers.size() - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } + } + return null; + } + + /** Gets a non-finishing container below the given one. */ + @Nullable + TaskFragmentContainer getNonFinishingTaskFragmentContainerBelow( + @NonNull TaskFragmentContainer current) { + final int index = mContainers.indexOf(current); + for (int i = index - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } } - return mContainers.get(mContainers.size() - 1); + return null; } @Nullable @@ -217,31 +238,57 @@ class TaskContainer { } void addSplitContainer(@NonNull SplitContainer splitContainer) { + if (splitContainer instanceof SplitPinContainer) { + mSplitPinContainer = (SplitPinContainer) splitContainer; + mSplitContainers.add(splitContainer); + return; + } + + // Keeps the SplitPinContainer on the top of the list. + mSplitContainers.remove(mSplitPinContainer); mSplitContainers.add(splitContainer); + if (mSplitPinContainer != null) { + mSplitContainers.add(mSplitPinContainer); + } } void removeSplitContainers(@NonNull List<SplitContainer> containers) { mSplitContainers.removeAll(containers); } + void removeSplitPinContainer() { + mSplitContainers.remove(mSplitPinContainer); + mSplitPinContainer = null; + } + + @Nullable + SplitPinContainer getSplitPinContainer() { + return mSplitPinContainer; + } + void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(index, taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.remove(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) { mContainers.removeAll(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void clearTaskFragmentContainer() { mContainers.clear(); + onTaskFragmentContainerUpdated(); } /** @@ -254,6 +301,34 @@ class TaskContainer { return mContainers; } + private void onTaskFragmentContainerUpdated() { + if (mSplitPinContainer == null) { + return; + } + + final TaskFragmentContainer pinnedContainer = mSplitPinContainer.getSecondaryContainer(); + final int pinnedContainerIndex = mContainers.indexOf(pinnedContainer); + if (pinnedContainerIndex <= 0) { + removeSplitPinContainer(); + return; + } + + // Ensure the pinned container is top-most. + if (pinnedContainerIndex != mContainers.size() - 1) { + mContainers.remove(pinnedContainer); + mContainers.add(pinnedContainer); + } + + // Update the primary container adjacent to the pinned container if needed. + final TaskFragmentContainer adjacentContainer = + getNonFinishingTaskFragmentContainerBelow(pinnedContainer); + if (adjacentContainer == null) { + removeSplitPinContainer(); + } else if (mSplitPinContainer.getPrimaryContainer() != adjacentContainer) { + mSplitPinContainer.setPrimaryContainer(adjacentContainer); + } + } + /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */ void getSplitStates(@NonNull List<SplitInfo> outSplitStates) { for (SplitContainer container : mSplitContainers) { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 9e264726a65a..9af1fe916279 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -1462,6 +1462,51 @@ public class SplitControllerTest { verify(testRecord).apply(eq(false)); } + @Test + public void testPinTopActivityStack() { + // Create two activities. + final Activity primaryActivity = createMockActivity(); + final Activity secondaryActivity = createMockActivity(); + + // Unable to pin if not being embedded. + SplitPinRule splitPinRule = new SplitPinRule.Builder(new SplitAttributes.Builder().build(), + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Split the two activities. + addSplitTaskFragments(primaryActivity, secondaryActivity); + final TaskFragmentContainer primaryContainer = + mSplitController.getContainerWithActivity(primaryActivity); + spyOn(primaryContainer); + + // Unable to pin if no valid TaskFragment. + doReturn(true).when(primaryContainer).isFinished(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Otherwise, should pin successfully. + doReturn(false).when(primaryContainer).isFinished(); + assertTrue(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin if there is already a pinned TaskFragment + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin on an unknown Task. + assertFalse(mSplitController.pinTopActivityStack(TASK_ID + 1, splitPinRule)); + + // Gets the current size of all the SplitContainers. + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + final int splitContainerCount = taskContainer.getSplitContainers().size(); + + // Create another activity and split with primary activity. + final Activity thirdActivity = createMockActivity(); + addSplitTaskFragments(primaryActivity, thirdActivity); + + // Ensure another SplitContainer is added and the pinned TaskFragment still on top + assertTrue(taskContainer.getSplitContainers().size() == splitContainerCount + +1); + assertTrue(mSplitController.getTopActiveContainer(TASK_ID).getTopNonFinishingActivity() + == secondaryActivity); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 11af1d1f20e1..000c65a75c81 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -135,15 +135,15 @@ public class TaskContainerTest { @Test public void testGetTopTaskFragmentContainer() { final TaskContainer taskContainer = createTestTaskContainer(); - assertNull(taskContainer.getTopTaskFragmentContainer()); + assertNull(taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf0, taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf1, taskContainer.getTopNonFinishingTaskFragmentContainer()); } @Test diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 3eec51318f56..e69825f21960 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1079,9 +1079,9 @@ public class BubbleController implements ConfigurationChangeListener, * <p>This is used by external callers (launcher). */ @VisibleForTesting - public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarXCoordinate, - int bubbleBarYCoordinate) { - mBubblePositioner.setBubbleBarPosition(bubbleBarXCoordinate, bubbleBarYCoordinate); + public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarOffsetX, + int bubbleBarOffsetY) { + mBubblePositioner.setBubbleBarPosition(bubbleBarOffsetX, bubbleBarOffsetY); if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); @@ -2092,10 +2092,10 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void showBubble(String key, int bubbleBarXCoordinate, int bubbleBarYCoordinate) { + public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) { mMainExecutor.execute( () -> mController.expandStackAndSelectBubbleFromLauncher( - key, bubbleBarXCoordinate, bubbleBarYCoordinate)); + key, bubbleBarOffsetX, bubbleBarOffsetY)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 6a5e3104fed5..ee6996d3d23d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -718,9 +718,16 @@ public class BubblePositioner { mShowingInBubbleBar = showingInBubbleBar; } - /** Sets the position of the bubble bar in screen coordinates. */ - public void setBubbleBarPosition(int x, int y) { - mBubbleBarPosition.set(x, y); + /** + * Sets the position of the bubble bar in screen coordinates. + * + * @param offsetX the offset of the bubble bar from the edge of the screen on the X axis + * @param offsetY the offset of the bubble bar from the edge of the screen on the Y axis + */ + public void setBubbleBarPosition(int offsetX, int offsetY) { + mBubbleBarPosition.set( + getAvailableRect().width() - offsetX, + getAvailableRect().height() + mInsets.top + mInsets.bottom - offsetY); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 59332f4be627..351319f5fb5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -29,8 +29,7 @@ interface IBubbles { oneway void unregisterBubbleListener(in IBubblesListener listener) = 2; - oneway void showBubble(in String key, in int bubbleBarXCoordinate, - in int bubbleBarYCoordinate) = 3; + oneway void showBubble(in String key, in int bubbleBarOffsetX, in int bubbleBarOffsetY) = 3; oneway void removeBubble(in String key, in int reason) = 4; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java index 21355a3efa2e..24608d651d06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java @@ -129,6 +129,11 @@ public class BubbleInfo implements Parcelable { return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION) != 0; } + /** Sets the flags for this bubble. */ + public void setFlags(int flags) { + mFlags = flags; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b14c3c10846b..08da4857a0b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1927,6 +1927,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); + mPipTransitionController.dump(pw, innerPrefix); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 73eb62ae47e9..e3d53fc415db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -72,6 +72,7 @@ import com.android.wm.shell.transition.CounterRotatorHelper; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; +import java.io.PrintWriter; import java.util.Optional; /** @@ -451,6 +452,9 @@ public class PipTransition extends PipTransitionController { @Override public void forceFinishTransition() { + // mFinishCallback might be null with an outdated mCurrentPipTaskToken + // for example, when app crashes while in PiP and exit transition has not started + mCurrentPipTaskToken = null; if (mFinishCallback == null) return; mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; @@ -1137,4 +1141,12 @@ public class PipTransition extends PipTransitionController { PipMenuController.ALPHA_NO_CHANGE); mPipMenuController.updateMenuBounds(destinationBounds); } + + @Override + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mCurrentPipTaskToken=" + mCurrentPipTaskToken); + pw.println(innerPrefix + "mFinishCallback=" + mFinishCallback); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index e1bcd70c256b..63627938ec87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -42,6 +42,7 @@ import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -283,4 +284,9 @@ public abstract class PipTransitionController implements Transitions.TransitionH */ void onPipTransitionCanceled(int direction); } + + /** + * Dumps internal states. + */ + public void dump(PrintWriter pw, String prefix) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 283be9c09355..7d62f58014f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -2564,6 +2564,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // so don't handle it. Log.e(TAG, "Somehow removed the last task in a stage outside of a proper " + "transition."); + // This new transition would be merged to current one so we need to clear + // tile manually here. + clearSplitPairedInRecents(EXIT_REASON_APP_FINISHED); final WindowContainerTransaction wct = new WindowContainerTransaction(); final int dismissTop = (dismissStages.size() == 1 && getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN) @@ -2747,6 +2750,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "launched 2 tasks in split, but didn't receive " + "2 tasks in transition. Possibly one of them failed to launch")); + if (mRecentTasks.isPresent() && mainChild != null) { + mRecentTasks.get().removeSplitPair(mainChild.getTaskInfo().taskId); + } + if (mRecentTasks.isPresent() && sideChild != null) { + mRecentTasks.get().removeSplitPair(sideChild.getTaskInfo().taskId); + } mSplitUnsupportedToast.show(); return true; } 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 5f54f58557d1..56c0d0e67cab 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 @@ -38,8 +38,6 @@ public class ShellSharedConstants { public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks"; // See IBackAnimation.aidl public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; - // See IFloatingTasks.aidl - 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 diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt index 96bfb78eff0d..d1dd8df81a3e 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt @@ -23,6 +23,7 @@ import android.content.ComponentName import android.util.Log import com.android.dream.lowlight.dagger.LowLightDreamModule import com.android.dream.lowlight.dagger.qualifiers.Application +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -103,6 +104,11 @@ class LowLightDreamManager @Inject constructor( ) } catch (ex: TimeoutCancellationException) { Log.e(TAG, "timed out while waiting for low light animation", ex) + } catch (ex: CancellationException) { + Log.w(TAG, "low light transition animation cancelled") + // Catch the cancellation so that we still set the system dream component if the + // animation is cancelled, such as by a user tapping to wake as the transition to + // low light happens. } dreamManager.setSystemDreamComponent( if (shouldEnterLowLight) lowLightDreamComponent else null diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt index 473603002b21..de1aee598667 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt @@ -110,15 +110,5 @@ class LowLightTransitionCoordinator @Inject constructor() { } } animator.addListener(listener) - continuation.invokeOnCancellation { - try { - animator.removeListener(listener) - animator.cancel() - } catch (exception: IndexOutOfBoundsException) { - // TODO(b/285666217): remove this try/catch once a proper fix is implemented. - // Cancelling the animator can cause an exception since we may be removing a - // listener during the cancellation. See b/285666217 for more details. - } - } } } diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt new file mode 100644 index 000000000000..f69c84dafbb2 --- /dev/null +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt @@ -0,0 +1,54 @@ +/* + * 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.dream.lowlight.util + +import android.view.animation.Interpolator + +/** + * Interpolator wrapper that shortens another interpolator from its original duration to a portion + * of that duration. + * + * For example, an `originalDuration` of 1000 and a `newDuration` of 200 results in an animation + * that when played for 200ms is the exact same as the first 200ms of a 1000ms animation if using + * the original interpolator. + * + * This is useful for the transition between the user dream and the low light clock as some + * animations are defined in the spec to be longer than the total duration of the animation. For + * example, the low light clock exit translation animation is defined to last >1s while the actual + * fade out of the low light clock is only 250ms, meaning the clock isn't visible anymore after + * 250ms. + * + * Since the dream framework currently only allows one dream to be visible and running, we use this + * interpolator to play just the first 250ms of the translation animation. Simply reducing the + * duration of the animation would result in the text exiting much faster than intended, so a custom + * interpolator is needed. + */ +class TruncatedInterpolator( + private val baseInterpolator: Interpolator, + originalDuration: Float, + newDuration: Float +) : Interpolator { + private val scaleFactor: Float + + init { + scaleFactor = newDuration / originalDuration + } + + override fun getInterpolation(input: Float): Float { + return baseInterpolator.getInterpolation(input * scaleFactor) + } +} diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp index 2d79090cd7d4..64b53cbb5c5a 100644 --- a/libs/dream/lowlight/tests/Android.bp +++ b/libs/dream/lowlight/tests/Android.bp @@ -27,6 +27,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "androidx.test.ext.junit", + "animationlib", "frameworks-base-testutils", "junit", "kotlinx_coroutines_test", diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt index 2a886bc31788..de84adb2e5c2 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt @@ -152,6 +152,21 @@ class LowLightDreamManagerTest { verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) } + @Test + fun setAmbientLightMode_animationCancelled_SetsSystemDream() = testScope.runTest { + mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) + runCurrent() + cancelEnterAnimations() + runCurrent() + // Animation never finishes, but we should still set the system dream + verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) + } + + private fun cancelEnterAnimations() { + val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } + listener.onAnimationCancel(mEnterAnimator) + } + private fun completeEnterAnimations() { val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } listener.onAnimationEnd(mEnterAnimator) diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt index 4c526a6ac69d..9ae304f9763a 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt @@ -158,26 +158,6 @@ class LowLightTransitionCoordinatorTest { assertThat(job.isCancelled).isTrue() } - @Test - fun shouldCancelAnimatorWhenJobCancelled() = testScope.runTest { - whenever(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator) - val coordinator = LowLightTransitionCoordinator() - coordinator.setLowLightEnterListener(mEnterListener) - val job = launch { - coordinator.waitForLowLightTransitionAnimation(timeout = TIMEOUT, entering = true) - } - runCurrent() - // Animator listener is added and the runnable is not run yet. - verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()) - verify(mAnimator, never()).cancel() - assertThat(job.isCompleted).isFalse() - - job.cancel() - // We should have removed the listener and cancelled the animator - verify(mAnimator).removeListener(mAnimatorListenerCaptor.value) - verify(mAnimator).cancel() - } - companion object { private val TIMEOUT = 1.toDuration(DurationUnit.SECONDS) } diff --git a/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt new file mode 100644 index 000000000000..190f02e97136 --- /dev/null +++ b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt @@ -0,0 +1,53 @@ +/* + * 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.dream.lowlight.util + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TruncatedInterpolatorTest { + @Test + fun truncatedInterpolator_matchesRegularInterpolator() { + val originalInterpolator = Interpolators.EMPHASIZED + val truncatedInterpolator = + TruncatedInterpolator(originalInterpolator, ORIGINAL_DURATION_MS, NEW_DURATION_MS) + + // Both interpolators should start at the same value. + var animationPercent = 0f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent)) + + animationPercent = 1f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + + animationPercent = 0.25f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + } + + companion object { + private const val ORIGINAL_DURATION_MS: Float = 1000f + private const val NEW_DURATION_MS: Float = 200f + private const val DURATION_RATIO: Float = NEW_DURATION_MS / ORIGINAL_DURATION_MS + } +} diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index e1eb36ac276c..25ac3c9d9074 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -419,12 +419,20 @@ public class DynamicSystemInstallationService extends Service mDynSystem.remove(); } + private boolean isDsuSlotLocked() { + // Slot names ending with ".lock" are a customized installation. + // We expect the client app to provide custom UI to enter/exit DSU mode. + // We will ignore the ACTION_REBOOT_TO_NORMAL command and will not show + // notifications in this case. + return mDynSystem.getActiveDsuSlot().endsWith(".lock"); + } + private void executeRebootToNormalCommand() { if (!isInDynamicSystem()) { Log.e(TAG, "It's already running in normal system."); return; } - if (mDynSystem.getActiveDsuSlot().endsWith(".lock")) { + if (isDsuSlotLocked()) { Log.e(TAG, "Ignore the reboot intent for a locked DSU slot"); return; } @@ -449,13 +457,13 @@ public class DynamicSystemInstallationService extends Service private void executeNotifyIfInUseCommand() { switch (getStatus()) { case STATUS_IN_USE: - if (!mHideNotification) { + if (!mHideNotification && !isDsuSlotLocked()) { startForeground(NOTIFICATION_ID, buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED)); } break; case STATUS_READY: - if (!mHideNotification) { + if (!mHideNotification && !isDsuSlotLocked()) { startForeground(NOTIFICATION_ID, buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED)); } diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 4633a659fcb7..7be60431b91b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -123,6 +123,7 @@ android_library { ], static_libs: [ "SystemUISharedLib", + "SystemUICustomizationLib", "SettingsLib", "androidx.leanback_leanback", "androidx.slice_slice-core", diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index 01e6bf00a664..bb8002a18c11 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -53,6 +53,20 @@ ] }, { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + }, + { // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+) "name": "SystemUIGoogleBiometricsScreenshotTests", "options": [ @@ -131,5 +145,21 @@ } ] } + ], + "postsubmit": [ + { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "include-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + } ] } diff --git a/packages/SystemUI/customization/res/values-h800dp/dimens.xml b/packages/SystemUI/customization/res/values-h800dp/dimens.xml index 60afc8a97a71..cb4994516a3a 100644 --- a/packages/SystemUI/customization/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/customization/res/values-h800dp/dimens.xml @@ -17,4 +17,7 @@ <resources> <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> <dimen name="large_clock_text_size">200dp</dimen> + + <!-- With the large clock, move up slightly from the center --> + <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> </resources> diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index ba8f2843cdfc..8eb8132b07b9 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -24,4 +24,12 @@ <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item> <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock --> <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item> + + <!-- With the large clock, move up slightly from the center --> + <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> + + <!-- additional offset for clock switch area items --> + <dimen name="small_clock_height">114dp</dimen> + <dimen name="small_clock_padding_top">28dp</dimen> + <dimen name="clock_padding_start">28dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 39dd90e5ed60..8c817330953c 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -95,9 +95,6 @@ <dimen name="num_pad_key_margin_end">12dp</dimen> <!-- additional offset for clock switch area items --> - <dimen name="small_clock_height">114dp</dimen> - <dimen name="small_clock_padding_top">28dp</dimen> - <dimen name="clock_padding_start">28dp</dimen> <dimen name="below_clock_padding_start">32dp</dimen> <dimen name="below_clock_padding_end">16dp</dimen> <dimen name="below_clock_padding_start_icons">28dp</dimen> diff --git a/packages/SystemUI/res/color/brightness_slider_overlay_color.xml b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml new file mode 100644 index 000000000000..a8abd793bd00 --- /dev/null +++ b/packages/SystemUI/res/color/brightness_slider_overlay_color.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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" /> + <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" /> + <item android:color="@color/transparent" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 2ea90c717863..a9e7adf668a9 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -26,6 +26,13 @@ <corners android:radius="@dimen/rounded_slider_corner_radius"/> </shape> </item> + <item> + <shape> + <corners android:radius="@dimen/rounded_slider_corner_radius" /> + <size android:height="@dimen/rounded_slider_height" /> + <solid android:color="@color/brightness_slider_overlay_color" /> + </shape> + </item> <item android:id="@+id/slider_icon" android:gravity="center_vertical|right" diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 87b5a4c2fc5b..32dc4b335f7e 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -20,7 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface"/> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh"/> <size android:width="@dimen/keyguard_affordance_fixed_width" android:height="@dimen/keyguard_affordance_fixed_height"/> diff --git a/packages/SystemUI/res/layout/activity_rear_display_education.xml b/packages/SystemUI/res/layout/activity_rear_display_education.xml index c295cfe7a2e0..1b6247f29922 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_education.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_education.xml @@ -28,7 +28,7 @@ app:cardCornerRadius="28dp" app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color"> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.reardisplay.RearDisplayEducationLottieViewWrapper android:id="@+id/rear_display_folded_animation" android:importantForAccessibility="no" android:layout_width="@dimen/rear_display_animation_width" diff --git a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml index 0e6b2812a8a9..bded0127ec84 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml @@ -29,7 +29,7 @@ app:cardCornerRadius="28dp" app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color"> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.reardisplay.RearDisplayEducationLottieViewWrapper android:id="@+id/rear_display_folded_animation" android:importantForAccessibility="no" android:layout_width="@dimen/rear_display_animation_width_opened" diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml index efc661a6e974..50b3bec93731 100644 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml @@ -57,7 +57,7 @@ <include layout="@layout/auth_biometric_icon"/> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon_overlay" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml index 05ff1b1c2e6f..ecb0bfa35e9f 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml @@ -39,6 +39,7 @@ android:singleLine="true" android:marqueeRepeatLimit="1" android:ellipsize="marquee" + android:importantForAccessibility="no" style="@style/TextAppearance.AuthCredential.Subtitle"/> <TextView @@ -59,7 +60,7 @@ android:layout_height="wrap_content" android:layout_gravity="center"> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -67,7 +68,7 @@ android:contentDescription="@null" android:scaleType="fitXY" /> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon_overlay" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index e95c6a79733c..91550b3dcac0 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -34,5 +34,6 @@ android:paddingEnd="0dp" android:progressDrawable="@drawable/brightness_progress_drawable" android:splitTrack="false" + android:clickable="true" /> </com.android.systemui.settings.brightness.BrightnessSliderView> diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml index 73050c253d72..4d952209da6f 100644 --- a/packages/SystemUI/res/layout/sidefps_view.xml +++ b/packages/SystemUI/res/layout/sidefps_view.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.airbnb.lottie.LottieAnimationView +<com.android.systemui.biometrics.SideFpsLottieViewWrapper xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/sidefps_animation" diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml deleted file mode 100644 index 6cb6993bb762..000000000000 --- a/packages/SystemUI/res/layout/status_bar_wifi_group.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2018, 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. -*/ ---> -<com.android.systemui.statusbar.StatusBarWifiView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/wifi_combo" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical" > - - <include layout="@layout/status_bar_wifi_group_inner" /> - -</com.android.systemui.statusbar.StatusBarWifiView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/udfps_keyguard_preview.xml b/packages/SystemUI/res/layout/udfps_keyguard_preview.xml index c068b7bc46a9..0964a21aeb36 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_preview.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_preview.xml @@ -24,7 +24,7 @@ android:background="@drawable/fingerprint_bg"> <!-- LockScreen fingerprint icon from 0 stroke width to full width --> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.keyguard.ui.view.UdfpsLottieViewWrapper android:layout_width="0dp" android:layout_height="0dp" android:scaleType="centerCrop" diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml index 191158e4e8c2..1d6147cd2169 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml @@ -32,7 +32,7 @@ <!-- Fingerprint --> <!-- AOD dashed fingerprint icon with moving dashes --> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.keyguard.ui.view.UdfpsLottieViewWrapper android:id="@+id/udfps_aod_fp" android:layout_width="match_parent" android:layout_height="match_parent" @@ -43,7 +43,7 @@ app:lottie_rawRes="@raw/udfps_aod_fp"/> <!-- LockScreen fingerprint icon from 0 stroke width to full width --> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.keyguard.ui.view.UdfpsLottieViewWrapper android:id="@+id/udfps_lockscreen_fp" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index 3a71994e07e2..829ef98956c5 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -15,9 +15,6 @@ --> <resources> - <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> - <!-- Margin above the ambient indication container --> <dimen name="ambient_indication_container_margin_top">20dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9aef9edbe7b4..47cd1e707557 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -747,8 +747,6 @@ <dimen name="keyguard_clock_switch_y_shift">14dp</dimen> <!-- When large clock is showing, offset the smartspace by this amount --> <dimen name="keyguard_smartspace_top_offset">12dp</dimen> - <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> <dimen name="notification_scrim_corner_radius">32dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 81012a754e35..f8c13b008fd9 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1114,6 +1114,16 @@ <!-- System sharing media projection permission button to continue. [CHAR LIMIT=60] --> <string name="media_projection_entry_generic_permission_dialog_continue">Start</string> + <!-- Task switcher notification --> + <!-- Task switcher notification text. [CHAR LIMIT=100] --> + <string name="media_projection_task_switcher_text">Sharing pauses when you switch apps</string> + <!-- The action for switching to the foreground task. [CHAR LIMIT=40] --> + <string name="media_projection_task_switcher_action_switch">Share this app instead</string> + <!-- The action for switching back to the projected task. [CHAR LIMIT=40] --> + <string name="media_projection_task_switcher_action_back">Switch back</string> + <!-- Task switcher notification channel name. [CHAR LIMIT=40] --> + <string name="media_projection_task_switcher_notification_channel">App switch</string> + <!-- Title for the dialog that is shown when screen capturing is disabled by enterprise policy. [CHAR LIMIT=100] --> <string name="screen_capturing_disabled_by_policy_dialog_title">Blocked by your IT admin</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt index 0fbeb1a054a7..f3296f0632bb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt @@ -32,7 +32,7 @@ open class RegionSampler @JvmOverloads constructor( val sampledView: View, - mainExecutor: Executor?, + val mainExecutor: Executor?, val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, val isLockscreen: Boolean = false, @@ -166,7 +166,7 @@ constructor( if (isLockscreen) WallpaperManager.FLAG_LOCK else WallpaperManager.FLAG_SYSTEM ) - onColorsChanged(sampledRegionWithOffset, initialSampling) + mainExecutor?.execute { onColorsChanged(sampledRegionWithOffset, initialSampling) } } ) } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index ca064efd4f76..4b14d3cff718 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -42,8 +42,6 @@ public class QuickStepContract { "com.google.android.apps.nexuslauncher.NexusLauncherActivity"; public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; - public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; - public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation"; // See ISysuiUnlockAnimationController.aidl public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java deleted file mode 100644 index 74c325dea15c..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.system; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; -import static android.view.WindowManager.TRANSIT_SLEEP; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.IApplicationThread; -import android.graphics.Rect; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.view.IRecentsAnimationController; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; -import android.window.PictureInPictureSurfaceTransaction; -import android.window.RemoteTransition; -import android.window.TaskSnapshot; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.util.TransitionUtil; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Helper class to build {@link RemoteTransition} objects - */ -public class RemoteTransitionCompat { - private static final String TAG = "RemoteTransitionCompat"; - - /** Constructor specifically for recents animation */ - public static RemoteTransition newRemoteTransition(RecentsAnimationListener recents, - IApplicationThread appThread) { - IRemoteTransition remote = new IRemoteTransition.Stub() { - final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap(); - IBinder mToken = null; - - @Override - public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - // TODO(b/177438007): Move this set-up logic into launcher's animation impl. - mToken = transition; - mRecentsSession.start(recents, mToken, info, t, finishedCallback); - } - - @Override - public void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t)) { - try { - finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); - } catch (RemoteException e) { - Log.e(TAG, "Error merging transition.", e); - } - // commit taskAppeared after merge transition finished. - mRecentsSession.commitTasksAppearedIfNeeded(); - } else { - t.close(); - info.releaseAllSurfaces(); - } - } - }; - return new RemoteTransition(remote, appThread, "Recents"); - } - - /** - * Wrapper to hook up parts of recents animation to shell transition. - * TODO(b/177438007): Remove this once Launcher handles shell transitions directly. - */ - @VisibleForTesting - static class RecentsControllerWrap extends IRecentsAnimationController.Default { - private RecentsAnimationListener mListener = null; - private IRemoteTransitionFinishedCallback mFinishCB = null; - - /** - * List of tasks that we are switching away from via this transition. Upon finish, these - * pausing tasks will become invisible. - * These need to be ordered since the order must be restored if there is no task-switch. - */ - private ArrayList<TaskState> mPausingTasks = null; - - /** - * List of tasks that we are switching to. Upon finish, these will remain visible and - * on top. - */ - private ArrayList<TaskState> mOpeningTasks = null; - - private WindowContainerToken mPipTask = null; - private WindowContainerToken mRecentsTask = null; - private int mRecentsTaskId = 0; - private TransitionInfo mInfo = null; - private boolean mOpeningSeparateHome = false; - private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; - private PictureInPictureSurfaceTransaction mPipTransaction = null; - private IBinder mTransition = null; - private boolean mKeyguardLocked = false; - private RemoteAnimationTarget[] mAppearedTargets; - private boolean mWillFinishToHome = false; - - /** The animation is idle, waiting for the user to choose a task to switch to. */ - private static final int STATE_NORMAL = 0; - - /** The user chose a new task to switch to and the animation is animating to it. */ - private static final int STATE_NEW_TASK = 1; - - /** The latest state that the recents animation is operating in. */ - private int mState = STATE_NORMAL; - - void start(RecentsAnimationListener listener, - IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mInfo != null) { - throw new IllegalStateException("Trying to run a new recents animation while" - + " recents is already active."); - } - mListener = listener; - mInfo = info; - mFinishCB = finishedCallback; - mPausingTasks = new ArrayList<>(); - mOpeningTasks = new ArrayList<>(); - mPipTask = null; - mRecentsTask = null; - mRecentsTaskId = -1; - mLeashMap = new ArrayMap<>(); - mTransition = transition; - mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0; - mState = STATE_NORMAL; - - final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>(); - final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>(); - TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter(); - // About layering: we divide up the "layer space" into 3 regions (each the size of - // the change count). This lets us categorize things into above/below/between - // while maintaining their relative ordering. - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (TransitionUtil.isWallpaper(change)) { - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - // wallpapers go into the "below" layer space - info.getChanges().size() - i, info, t, mLeashMap); - wallpapers.add(target); - // Make all the wallpapers opaque since we want them visible from the start - t.setAlpha(target.leash, 1); - } else if (leafTaskFilter.test(change)) { - // start by putting everything into the "below" layer space. - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - info.getChanges().size() - i, info, t, mLeashMap); - apps.add(target); - if (TransitionUtil.isClosingType(change.getMode())) { - // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mPausingTasks.add(new TaskState(change, target.leash)); - if (taskInfo.pictureInPictureParams != null - && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { - mPipTask = taskInfo.token; - } - } else if (taskInfo != null - && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { - // There's a 3p launcher, so make sure recents goes above that. - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (TransitionUtil.isOpeningType(change.getMode())) { - mOpeningTasks.add(new TaskState(change, target.leash)); - } - } - } - t.apply(); - mListener.onAnimationStart(new RecentsAnimationControllerCompat(this), - apps.toArray(new RemoteAnimationTarget[apps.size()]), - wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), - new Rect(0, 0, 0, 0), new Rect()); - } - - @SuppressLint("NewApi") - boolean merge(TransitionInfo info, SurfaceControl.Transaction t) { - if (info.getType() == TRANSIT_SLEEP) { - // A sleep event means we need to stop animations immediately, so cancel here. - mListener.onAnimationCanceled(new HashMap<>()); - finish(mWillFinishToHome, false /* userLeaveHint */); - return false; - } - ArrayList<TransitionInfo.Change> openingTasks = null; - ArrayList<TransitionInfo.Change> closingTasks = null; - mAppearedTargets = null; - mOpeningSeparateHome = false; - TransitionInfo.Change recentsOpening = null; - boolean foundRecentsClosing = false; - boolean hasChangingApp = false; - final TransitionUtil.LeafTaskFilter leafTaskFilter = - new TransitionUtil.LeafTaskFilter(); - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - final boolean isLeafTask = leafTaskFilter.test(change); - if (TransitionUtil.isOpeningType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - recentsOpening = change; - } else if (isLeafTask) { - if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // This is usually a 3p launcher - mOpeningSeparateHome = true; - } - if (openingTasks == null) { - openingTasks = new ArrayList<>(); - } - openingTasks.add(change); - } - } else if (TransitionUtil.isClosingType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - foundRecentsClosing = true; - } else if (isLeafTask) { - if (closingTasks == null) { - closingTasks = new ArrayList<>(); - } - closingTasks.add(change); - } - } else if (change.getMode() == TRANSIT_CHANGE) { - // Finish recents animation if the display is changed, so the default - // transition handler can play the animation such as rotation effect. - if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) { - mListener.onSwitchToScreenshot(() -> finish(false /* toHome */, - false /* userLeaveHint */)); - return false; - } - hasChangingApp = true; - } - } - if (hasChangingApp && foundRecentsClosing) { - // This happens when a visible app is expanding (usually PiP). In this case, - // that transition probably has a special-purpose animation, so finish recents - // now and let it do its animation (since recents is going to be occluded). - if (!mListener.onSwitchToScreenshot( - () -> finish(true /* toHome */, false /* userLeaveHint */))) { - Log.w(TAG, "Recents callback doesn't support support switching to screenshot" - + ", there might be a flicker."); - finish(true /* toHome */, false /* userLeaveHint */); - } - return false; - } - if (recentsOpening != null) { - // the recents task re-appeared. This happens if the user gestures before the - // task-switch (NEW_TASK) animation finishes. - if (mState == STATE_NORMAL) { - Log.e(TAG, "Returning to recents while recents is already idle."); - } - if (closingTasks == null || closingTasks.size() == 0) { - Log.e(TAG, "Returning to recents without closing any opening tasks."); - } - // Setup may hide it initially since it doesn't know that overview was still active. - t.show(recentsOpening.getLeash()); - t.setAlpha(recentsOpening.getLeash(), 1.f); - mState = STATE_NORMAL; - } - boolean didMergeThings = false; - if (closingTasks != null) { - // Cancelling a task-switch. Move the tasks back to mPausing from mOpening - for (int i = 0; i < closingTasks.size(); ++i) { - final TransitionInfo.Change change = closingTasks.get(i); - int openingIdx = TaskState.indexOf(mOpeningTasks, change); - if (openingIdx < 0) { - Log.e(TAG, "Back to existing recents animation from an unrecognized " - + "task: " + change.getTaskInfo().taskId); - continue; - } - mPausingTasks.add(mOpeningTasks.remove(openingIdx)); - didMergeThings = true; - } - } - if (openingTasks != null && openingTasks.size() > 0) { - // Switching to some new tasks, add to mOpening and remove from mPausing. Also, - // enter NEW_TASK state since this will start the switch-to animation. - final int layer = mInfo.getChanges().size() * 3; - final RemoteAnimationTarget[] targets = - new RemoteAnimationTarget[openingTasks.size()]; - for (int i = 0; i < openingTasks.size(); ++i) { - final TransitionInfo.Change change = openingTasks.get(i); - int pausingIdx = TaskState.indexOf(mPausingTasks, change); - if (pausingIdx >= 0) { - // Something is showing/opening a previously-pausing app. - targets[i] = TransitionUtil.newTarget(change, layer, - mPausingTasks.get(pausingIdx).mLeash); - mOpeningTasks.add(mPausingTasks.remove(pausingIdx)); - // Setup hides opening tasks initially, so make it visible again (since we - // are already showing it). - t.show(change.getLeash()); - t.setAlpha(change.getLeash(), 1.f); - } else { - // We are receiving new opening tasks, so convert to onTasksAppeared. - targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap); - // reparent into the original `mInfo` since that's where we are animating. - final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo); - t.reparent(targets[i].leash, mInfo.getRoot(rootIdx).getLeash()); - t.setLayer(targets[i].leash, layer); - mOpeningTasks.add(new TaskState(change, targets[i].leash)); - } - } - didMergeThings = true; - mState = STATE_NEW_TASK; - mAppearedTargets = targets; - } - if (!didMergeThings) { - // Didn't recognize anything in incoming transition so don't merge it. - Log.w(TAG, "Don't know how to merge this transition."); - return false; - } - t.apply(); - // not using the incoming anim-only surfaces - info.releaseAnimSurfaces(); - return true; - } - - private void commitTasksAppearedIfNeeded() { - if (mAppearedTargets != null) { - mListener.onTasksAppeared(mAppearedTargets); - mAppearedTargets = null; - } - } - - @Override public TaskSnapshot screenshotTask(int taskId) { - try { - return ActivityTaskManager.getService().takeTaskSnapshot(taskId, - true /* updateCache */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to screenshot task", e); - } - return null; - } - - @Override public void setInputConsumerEnabled(boolean enabled) { - if (!enabled) return; - // transient launches don't receive focus automatically. Since we are taking over - // the gesture now, take focus explicitly. - // This also moves recents back to top if the user gestured before a switch - // animation finished. - try { - ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set focused task", e); - } - } - - @Override public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { - } - - @Override public void setFinishTaskTransaction(int taskId, - PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) { - mPipTransaction = finishTransaction; - } - - @Override - @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - if (mFinishCB == null) { - Log.e(TAG, "Duplicate call to finish", new RuntimeException()); - return; - } - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - if (mKeyguardLocked && mRecentsTask != null) { - if (toHome) wct.reorder(mRecentsTask, true /* toTop */); - else wct.restoreTransientOrder(mRecentsTask); - } - if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { - // The gesture is returning to the pausing-task(s) rather than continuing with - // recents, so end the transition by moving the app back to the top (and also - // re-showing it's task). - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - // reverse order so that index 0 ends up on top - wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */); - t.show(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) { - // Special situation where 3p launcher was changed during recents (this happens - // during tapltests...). Here we get both "return to home" AND "home opening". - // This is basically going home, but we have to restore the recents and home order. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - final TaskState state = mOpeningTasks.get(i); - if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // Make sure it is on top. - wct.reorder(state.mToken, true /* onTop */); - } - t.show(state.mTaskSurface); - } - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else { - // The general case: committing to recents, going home, or switching tasks. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - t.show(mOpeningTasks.get(i).mTaskSurface); - } - for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint) { - // This means recents is not *actually* finishing, so of course we gotta - // do special stuff in WMCore to accommodate. - wct.setDoNotPip(mPausingTasks.get(i).mToken); - } - // Since we will reparent out of the leashes, pre-emptively hide the child - // surface to match the leash. Otherwise, there will be a flicker before the - // visibility gets committed in Core when using split-screen (in splitscreen, - // the leaf-tasks are not "independent" so aren't hidden by normal setup). - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) { - t.show(mInfo.getChange(mPipTask).getLeash()); - PictureInPictureSurfaceTransaction.apply(mPipTransaction, - mInfo.getChange(mPipTask).getLeash(), t); - mPipTask = null; - mPipTransaction = null; - } - } - try { - mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call animation finish callback", e); - t.apply(); - } - // Only release the non-local created surface references. The animator is responsible - // for releasing the leashes created by local. - mInfo.releaseAllSurfaces(); - // Reset all members. - mListener = null; - mFinishCB = null; - mPausingTasks = null; - mOpeningTasks = null; - mAppearedTargets = null; - mInfo = null; - mOpeningSeparateHome = false; - mLeashMap = null; - mTransition = null; - mState = STATE_NORMAL; - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { - } - - @Override public void cleanupScreenshot() { - } - - @Override public void setWillFinishToHome(boolean willFinishToHome) { - mWillFinishToHome = willFinishToHome; - } - - /** - * @see IRecentsAnimationController#removeTask - */ - @Override public boolean removeTask(int taskId) { - return false; - } - - /** - * @see IRecentsAnimationController#detachNavigationBarFromApp - */ - @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) { - try { - ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition); - } catch (RemoteException e) { - Log.e(TAG, "Failed to detach the navigation bar from app", e); - } - } - - /** - * @see IRecentsAnimationController#animateNavigationBarToApp(long) - */ - @Override public void animateNavigationBarToApp(long duration) { - } - } - - /** Utility class to track the state of a task as-seen by recents. */ - private static class TaskState { - WindowContainerToken mToken; - ActivityManager.RunningTaskInfo mTaskInfo; - - /** The surface/leash of the task provided by Core. */ - SurfaceControl mTaskSurface; - - /** The (local) animation-leash created for this task. */ - SurfaceControl mLeash; - - TaskState(TransitionInfo.Change change, SurfaceControl leash) { - mToken = change.getContainer(); - mTaskInfo = change.getTaskInfo(); - mTaskSurface = change.getLeash(); - mLeash = leash; - } - - static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) { - for (int i = list.size() - 1; i >= 0; --i) { - if (list.get(i).mToken.equals(change.getContainer())) { - return i; - } - } - return -1; - } - - public String toString() { - return "" + mToken + " : " + mLeash; - } - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java deleted file mode 100644 index 98212e1d91b6..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2019 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.tracing; - -import android.os.Trace; -import android.util.Log; -import android.view.Choreographer; - -import com.android.internal.util.TraceBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Queue; -import java.util.function.Consumer; - -/** - * A proto tracer implementation that can be updated directly (upon state change), or on the next - * scheduled frame. - * - * @param <P> The class type of the proto provider - * @param <S> The proto class type of the encapsulating proto - * @param <T> The proto class type of the individual proto entries in the buffer - * @param <R> The proto class type of the entry root proto in the buffer - */ -public class FrameProtoTracer<P, S extends P, T extends P, R> - implements Choreographer.FrameCallback { - - private static final String TAG = "FrameProtoTracer"; - private static final int BUFFER_CAPACITY = 1024 * 1024; - - private final Object mLock = new Object(); - private final TraceBuffer<P, S, T> mBuffer; - private final File mTraceFile; - private final ProtoTraceParams<P, S, T, R> mParams; - private Choreographer mChoreographer; - private final Queue<T> mPool = new ArrayDeque<>(); - private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>(); - private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>(); - - private volatile boolean mEnabled; - private boolean mFrameScheduled; - - private final TraceBuffer.ProtoProvider<P, S, T> mProvider = - new TraceBuffer.ProtoProvider<P, S, T>() { - @Override - public int getItemSize(P proto) { - return mParams.getProtoSize(proto); - } - - @Override - public byte[] getBytes(P proto) { - return mParams.getProtoBytes(proto); - } - - @Override - public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) - throws IOException { - os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer)); - } - }; - - public interface ProtoTraceParams<P, S, T, R> { - File getTraceFile(); - S getEncapsulatingTraceProto(); - T updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables); - byte[] serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer); - byte[] getProtoBytes(P proto); - int getProtoSize(P proto); - } - - public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) { - mParams = params; - mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, mProvider, new Consumer<T>() { - @Override - public void accept(T t) { - onProtoDequeued(t); - } - }); - mTraceFile = params.getTraceFile(); - } - - public void start() { - synchronized (mLock) { - if (mEnabled) { - return; - } - mBuffer.resetBuffer(); - mEnabled = true; - } - logState(); - } - - public void stop() { - synchronized (mLock) { - if (!mEnabled) { - return; - } - mEnabled = false; - } - writeToFile(); - } - - public boolean isEnabled() { - return mEnabled; - } - - public void add(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.add(traceable); - } - } - - public void remove(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.remove(traceable); - } - } - - public void scheduleFrameUpdate() { - if (!mEnabled || mFrameScheduled) { - return; - } - - // Schedule an update on the next frame - if (mChoreographer == null) { - mChoreographer = Choreographer.getMainThreadInstance(); - } - mChoreographer.postFrameCallback(this); - mFrameScheduled = true; - } - - public void update() { - if (!mEnabled) { - return; - } - - logState(); - } - - public float getBufferUsagePct() { - return (float) mBuffer.getBufferSize() / BUFFER_CAPACITY; - } - - @Override - public void doFrame(long frameTimeNanos) { - logState(); - } - - private void onProtoDequeued(T proto) { - mPool.add(proto); - } - - private void logState() { - synchronized (mLock) { - mTmpTraceables.addAll(mTraceables); - } - - mBuffer.add(mParams.updateBufferProto(mPool.poll(), mTmpTraceables)); - mTmpTraceables.clear(); - mFrameScheduled = false; - } - - private void writeToFile() { - try { - Trace.beginSection("ProtoTracer.writeToFile"); - mBuffer.writeTraceToFile(mTraceFile, mParams.getEncapsulatingTraceProto()); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } finally { - Trace.endSection(); - } - } -} - - diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java deleted file mode 100644 index e05b0b074449..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2017 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.tracing; - -/** - * @see FrameProtoTracer - */ -public interface ProtoTraceable<T> { - - /** - * NOTE: Implementations should update all fields in this proto. - */ - void writeToProto(T proto); -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 794e6941c2bb..b3e08c0bc69f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -165,15 +165,18 @@ public class KeyguardPinViewController * Responsible for identifying if PIN hinting is to be enabled or not */ private boolean isPinHinting() { - return mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser()) - == DEFAULT_PIN_LENGTH; + return mPinLength == DEFAULT_PIN_LENGTH; } /** - * Responsible for identifying if auto confirm is enabled or not in Settings + * Responsible for identifying if auto confirm is enabled or not in Settings and + * a valid PIN_LENGTH is stored on the device (though the latter check is only to make it more + * robust since we only allow enabling PIN confirmation if the user has a valid PIN length + * saved on device) */ private boolean isAutoPinConfirmEnabledInSettings() { //Checks if user has enabled the auto confirm in Settings - return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()); + return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()) + && mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE; } } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java index a7d4455b43c2..87627698597f 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java @@ -20,6 +20,7 @@ import com.android.keyguard.CarrierText; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import dagger.Module; @@ -44,6 +45,13 @@ public abstract class KeyguardStatusBarViewModule { /** */ @Provides @KeyguardStatusBarViewScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.KEYGUARD; + } + + /** */ + @Provides + @KeyguardStatusBarViewScope static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) { return view.findViewById(R.id.user_switcher_container); } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 1f1b154ef1c8..04acd0b91173 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -123,7 +123,6 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.telephony.TelephonyListenerManager; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.DeviceConfigProxy; @@ -335,7 +334,6 @@ public class Dependency { @Inject Lazy<IWallpaperManager> mWallpaperManager; @Inject Lazy<CommandQueue> mCommandQueue; @Inject Lazy<RecordingController> mRecordingController; - @Inject Lazy<ProtoTracer> mProtoTracer; @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory; @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy; @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager; @@ -528,7 +526,6 @@ public class Dependency { mProviders.put(DozeParameters.class, mDozeParameters::get); mProviders.put(IWallpaperManager.class, mWallpaperManager::get); mProviders.put(CommandQueue.class, mCommandQueue::get); - mProviders.put(ProtoTracer.class, mProtoTracer::get); mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get); mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get); diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index 43fb3630bd19..444491f53629 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -144,12 +144,7 @@ constructor( private val lockPatternUtils: LockPatternUtils, ) : AuthenticationRepository { - override val isUnlocked: StateFlow<Boolean> = - keyguardRepository.isKeyguardUnlocked.stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) + override val isUnlocked: StateFlow<Boolean> = keyguardRepository.isKeyguardUnlocked override suspend fun isLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt index b52ddc1dbc42..b34f1b45d763 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt @@ -87,6 +87,10 @@ class AccessorizedBatteryDrawable( } var displayShield: Boolean = false + set(value) { + field = value + postInvalidate() + } private fun updateSizes() { val b = bounds @@ -204,4 +208,11 @@ class AccessorizedBatteryDrawable( val shieldPathString = context.resources.getString(R.string.config_batterymeterShieldPath) shieldPath.set(PathParser.createPathFromPathData(shieldPathString)) } + + private val invalidateRunnable: () -> Unit = { invalidateSelf() } + + private fun postInvalidate() { + unscheduleSelf(invalidateRunnable) + scheduleSelf(invalidateRunnable, 0) + } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index f5f47d007164..234795fa9639 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -466,9 +466,11 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { public void dump(PrintWriter pw, String[] args) { String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + ""; + String displayShield = mDrawable == null ? null : mDrawable.getDisplayShield() + ""; CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText(); pw.println(" BatteryMeterView:"); pw.println(" mDrawable.getPowerSave: " + powerSave); + pw.println(" mDrawable.getDisplayShield: " + displayShield); pw.println(" mBatteryPercentView.getText(): " + percent); pw.println(" mTextColor: #" + Integer.toHexString(mTextColor)); pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown); diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index f6a10bd4af4a..6a5749cc5571 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -30,16 +30,18 @@ import android.view.View; import androidx.annotation.NonNull; +import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; +import java.io.PrintWriter; + import javax.inject.Inject; /** Controller for {@link BatteryMeterView}. **/ @@ -53,6 +55,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> private final String mSlotBattery; private final SettingObserver mSettingObserver; private final UserTracker mUserTracker; + private final StatusBarLocation mLocation; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -94,6 +97,13 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> public void onIsBatteryDefenderChanged(boolean isBatteryDefender) { mView.onIsBatteryDefenderChanged(isBatteryDefender); } + + @Override + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.print(super.toString()); + pw.println(" location=" + mLocation); + mView.dump(pw, args); + } }; private final UserTracker.Callback mUserChangedCallback = @@ -113,14 +123,15 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> @Inject public BatteryMeterViewController( BatteryMeterView view, + StatusBarLocation location, UserTracker userTracker, ConfigurationController configurationController, TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, - FeatureFlags featureFlags, BatteryController batteryController) { super(view); + mLocation = location; mUserTracker = userTracker; mConfigurationController = configurationController; mTunerService = tunerService; @@ -129,7 +140,8 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> mBatteryController = batteryController; mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); - mView.setDisplayShieldEnabled(featureFlags.isEnabled(Flags.BATTERY_SHIELD_ICON)); + mView.setDisplayShieldEnabled( + getContext().getResources().getBoolean(R.bool.flag_battery_shield_icon)); mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery); mSettingObserver = new SettingObserver(mMainHandler); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricPromptLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricPromptLottieViewWrapper.kt new file mode 100644 index 000000000000..e48e6e2dfdc6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricPromptLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * 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.biometrics + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class BiometricPromptLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 9d0cde18a6ef..37ce44488346 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -117,6 +117,8 @@ constructor( private var overlayView: View? = null set(value) { field?.let { oldView -> + val lottie = oldView.findViewById(R.id.sidefps_animation) as LottieAnimationView + lottie.pauseAnimation() windowManager.removeView(oldView) orientationListener.disable() } @@ -193,7 +195,9 @@ constructor( requests.add(request) mainExecutor.execute { if (overlayView == null) { - traceSection("SideFpsController#show(request=${request.name}, reason=$reason") { + traceSection( + "SideFpsController#show(request=${request.name}, reason=$reason)" + ) { createOverlayForDisplay(reason) } } else { @@ -208,7 +212,7 @@ constructor( requests.remove(request) mainExecutor.execute { if (requests.isEmpty()) { - traceSection("SideFpsController#hide(${request.name}") { overlayView = null } + traceSection("SideFpsController#hide(${request.name})") { overlayView = null } } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt new file mode 100644 index 000000000000..e98f6db12d34 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * 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.biometrics + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class SideFpsLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt index 3197c0935d0b..fb580ca54aff 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.biometrics.domain.model +package com.android.systemui.biometrics.shared.model import android.hardware.biometrics.BiometricAuthenticator diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 64df6a03001d..e5a4d1a644f1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -46,9 +46,9 @@ import com.android.systemui.biometrics.AuthIconController import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality -import com.android.systemui.biometrics.domain.model.asBiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind +import com.android.systemui.biometrics.shared.model.asBiometricModality import com.android.systemui.biometrics.ui.BiometricPromptLayout import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode import com.android.systemui.biometrics.ui.viewmodel.PromptMessage @@ -396,7 +396,6 @@ private class Spaghetti( private var lifecycleScope: CoroutineScope? = null private var modalities: BiometricModalities = BiometricModalities() - private var faceFailedAtLeastOnce = false private var legacyCallback: Callback? = null override var legacyIconController: AuthIconController? = null @@ -476,19 +475,15 @@ private class Spaghetti( viewModel.ensureFingerprintHasStarted(isDelayed = true) applicationScope.launch { - val suppress = - modalities.hasFaceAndFingerprint && - (failedModality == BiometricModality.Face) && - faceFailedAtLeastOnce - if (failedModality == BiometricModality.Face) { - faceFailedAtLeastOnce = true - } - viewModel.showTemporaryError( failureReason, messageAfterError = modalities.asDefaultHelpMessage(applicationContext), authenticateAfterError = modalities.hasFingerprint, - suppressIfErrorShowing = suppress, + suppressIf = { currentMessage -> + modalities.hasFaceAndFingerprint && + failedModality == BiometricModality.Face && + currentMessage.isError + }, failedModality = failedModality, ) } @@ -501,11 +496,10 @@ private class Spaghetti( } applicationScope.launch { - val suppress = - modalities.hasFaceAndFingerprint && (errorModality == BiometricModality.Face) viewModel.showTemporaryError( error, - suppressIfErrorShowing = suppress, + messageAfterError = modalities.asDefaultHelpMessage(applicationContext), + authenticateAfterError = modalities.hasFingerprint, ) delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong()) legacyCallback?.onAction(Callback.ACTION_ERROR) @@ -522,6 +516,7 @@ private class Spaghetti( viewModel.showTemporaryError( help, messageAfterError = modalities.asDefaultHelpMessage(applicationContext), + authenticateAfterError = modalities.hasFingerprint, hapticFeedback = false, ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt index 444082ca2742..2f9557f70a32 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt @@ -16,7 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality /** * The authenticated state with the [authenticatedModality] (when [isAuthenticated]) with an diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt index 219da716f7d9..50f491142949 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt @@ -33,9 +33,9 @@ sealed interface PromptMessage { else -> "" } - /** If this is an [Error] or [Help] message. */ - val isErrorOrHelp: Boolean - get() = this is Error || this is Help + /** If this is an [Error]. */ + val isError: Boolean + get() = this is Error /** An error message. */ data class Error(val errorMessage: String) : PromptMessage diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index d63bf57013e5..8a2e4059ee73 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -20,7 +20,7 @@ import android.util.Log import com.android.systemui.biometrics.AuthBiometricView import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.statusbar.VibratorHelper import javax.inject.Inject @@ -210,35 +210,33 @@ constructor( * Show a temporary error [message] associated with an optional [failedModality] and play * [hapticFeedback]. * - * An optional [messageAfterError] will be shown via [showAuthenticating] when - * [authenticateAfterError] is set (or via [showHelp] when not set) after the error is - * dismissed. + * The [messageAfterError] will be shown via [showAuthenticating] when [authenticateAfterError] + * is set (or via [showHelp] when not set) after the error is dismissed. * - * The error is ignored if the user has already authenticated or if [suppressIfErrorShowing] is - * set and an error message is already showing. + * The error is ignored if the user has already authenticated or if [suppressIf] is true given + * the currently showing [PromptMessage]. */ suspend fun showTemporaryError( message: String, + messageAfterError: String, + authenticateAfterError: Boolean, + suppressIf: (PromptMessage) -> Boolean = { false }, hapticFeedback: Boolean = true, - messageAfterError: String = "", - authenticateAfterError: Boolean = false, - suppressIfErrorShowing: Boolean = false, failedModality: BiometricModality = BiometricModality.None, ) = coroutineScope { if (_isAuthenticated.value.isAuthenticated) { return@coroutineScope } - if (_message.value.isErrorOrHelp && suppressIfErrorShowing) { - if (_isAuthenticated.value.isNotAuthenticated) { - _canTryAgainNow.value = supportsRetry(failedModality) - } + + _canTryAgainNow.value = supportsRetry(failedModality) + + if (suppressIf(_message.value)) { return@coroutineScope } _isAuthenticating.value = false _isAuthenticated.value = PromptAuthState(false) _forceMediumSize.value = true - _canTryAgainNow.value = supportsRetry(failedModality) _message.value = PromptMessage.Error(message) _legacyState.value = AuthBiometricView.STATE_ERROR @@ -374,7 +372,9 @@ constructor( AuthBiometricView.STATE_AUTHENTICATED } - vibrator.success(modality) + if (!needsUserConfirmation) { + vibrator.success(modality) + } messageJob?.cancel() messageJob = null @@ -420,6 +420,8 @@ constructor( _message.value = PromptMessage.Empty _legacyState.value = AuthBiometricView.STATE_AUTHENTICATED + vibrator.success(authState.authenticatedModality) + messageJob?.cancel() messageJob = null } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 2513c81c17f8..62a484d42dec 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -53,6 +52,7 @@ constructor( private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, + featureFlags: FeatureFlags, @Assisted private val containerName: String, ) { @@ -95,36 +95,14 @@ constructor( val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { - // UNLOCKING SHOWS Gone. - // - // Move to the gone scene if the device becomes unlocked while on the bouncer scene. - applicationScope.launch { - sceneInteractor - .currentScene(containerName) - .flatMapLatest { currentScene -> - if (currentScene.key == SceneKey.Bouncer) { - authenticationInteractor.isUnlocked - } else { - flowOf(false) - } - } - .distinctUntilChanged() - .collect { isUnlocked -> - if (isUnlocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + // Clear the message if moved from throttling to no-longer throttling. + applicationScope.launch { + isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> + if (wasThrottled && !currentlyThrottled) { + clearMessage() } } - } - - // Clear the message if moved from throttling to no-longer throttling. - applicationScope.launch { - isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> - if (wasThrottled && !currentlyThrottled) { - clearMessage() - } } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 5425022e11b7..a4ef5cec6525 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -23,6 +23,8 @@ import com.android.systemui.R import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -49,6 +51,7 @@ constructor( @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, interactorFactory: BouncerInteractor.Factory, + featureFlags: FeatureFlags, @Assisted containerName: String, ) { private val interactor: BouncerInteractor = interactorFactory.create(containerName) @@ -102,15 +105,48 @@ constructor( ) init { - applicationScope.launch { - _authMethod.subscriptionCount - .pairwise() - .map { (previousCount, currentCount) -> currentCount > previousCount } - .collect { subscriberAdded -> - if (subscriberAdded) { - reloadAuthMethod() + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + applicationScope.launch { + interactor.isThrottled + .map { isThrottled -> + if (isThrottled) { + when (interactor.getAuthenticationMethod()) { + is AuthenticationMethodModel.Pin -> + R.string.kg_too_many_failed_pin_attempts_dialog_message + is AuthenticationMethodModel.Password -> + R.string.kg_too_many_failed_password_attempts_dialog_message + is AuthenticationMethodModel.Pattern -> + R.string.kg_too_many_failed_pattern_attempts_dialog_message + else -> null + }?.let { stringResourceId -> + applicationContext.getString( + stringResourceId, + interactor.throttling.value.failedAttemptCount, + ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), + ) + } + } else { + null + } + } + .distinctUntilChanged() + .collect { dialogMessageOrNull -> + if (dialogMessageOrNull != null) { + _throttlingDialogMessage.value = dialogMessageOrNull + } + } + } + + applicationScope.launch { + _authMethod.subscriptionCount + .pairwise() + .map { (previousCount, currentCount) -> currentCount > previousCount } + .collect { subscriberAdded -> + if (subscriberAdded) { + reloadAuthMethod() + } } - } + } } } @@ -144,39 +180,6 @@ constructor( */ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() - init { - applicationScope.launch { - interactor.isThrottled - .map { isThrottled -> - if (isThrottled) { - when (interactor.getAuthenticationMethod()) { - is AuthenticationMethodModel.Pin -> - R.string.kg_too_many_failed_pin_attempts_dialog_message - is AuthenticationMethodModel.Password -> - R.string.kg_too_many_failed_password_attempts_dialog_message - is AuthenticationMethodModel.Pattern -> - R.string.kg_too_many_failed_pattern_attempts_dialog_message - else -> null - }?.let { stringResourceId -> - applicationContext.getString( - stringResourceId, - interactor.throttling.value.failedAttemptCount, - ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), - ) - } - } else { - null - } - } - .distinctUntilChanged() - .collect { dialogMessageOrNull -> - if (dialogMessageOrNull != null) { - _throttlingDialogMessage.value = dialogMessageOrNull - } - } - } - } - /** Notifies that the emergency services button was clicked. */ fun onEmergencyServicesButtonClicked() { // TODO(b/280877228): implement this diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index a90980fddfb0..046ccf165d07 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -18,6 +18,7 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -32,6 +33,7 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, + ShadeModule.class, ShutdownUiModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 3b897394c515..3c42a29451ca 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -82,7 +82,6 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl; import com.android.systemui.shared.condition.Monitor; @@ -199,7 +198,6 @@ import javax.inject.Named; SecurityRepositoryModule.class, ScreenRecordModule.class, SettingsUtilModule.class, - ShadeModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, StatusBarPipelineModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index ee046c27d72e..484bf3d51f36 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -26,6 +26,7 @@ import androidx.core.animation.doOnEnd import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.dream.lowlight.util.TruncatedInterpolator import com.android.systemui.R import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams @@ -204,31 +205,28 @@ constructor( translationYAnimator( from = 0f, to = -mDreamInTranslationYDistance.toFloat(), - durationMs = mDreamInTranslationYDurationMs, + durationMs = mDreamInComplicationsAnimDurationMs, delayMs = 0, - interpolator = Interpolators.EMPHASIZED + // Truncate the animation from the full duration to match the alpha + // animation so that the whole animation ends at the same time. + interpolator = + TruncatedInterpolator( + Interpolators.EMPHASIZED, + /*originalDuration=*/ mDreamInTranslationYDurationMs.toFloat(), + /*newDuration=*/ mDreamInComplicationsAnimDurationMs.toFloat() + ) ), alphaAnimator( - from = - mCurrentAlphaAtPosition.getOrDefault( - key = POSITION_BOTTOM, - defaultValue = 1f - ), - to = 0f, - durationMs = mDreamInComplicationsAnimDurationMs, - delayMs = 0, - positions = POSITION_BOTTOM - ) - .apply { - doOnEnd { - // The logical end of the animation is once the alpha and blur - // animations finish, end the animation so that any listeners are - // notified. The Y translation animation is much longer than all of - // the other animations due to how the spec is defined, but is not - // expected to run to completion. - mAnimator?.end() - } - }, + from = + mCurrentAlphaAtPosition.getOrDefault( + key = POSITION_BOTTOM, + defaultValue = 1f + ), + to = 0f, + durationMs = mDreamInComplicationsAnimDurationMs, + delayMs = 0, + positions = POSITION_BOTTOM + ), alphaAnimator( from = mCurrentAlphaAtPosition.getOrDefault( diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 45a8f13311a0..83c1c7100761 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -91,6 +91,11 @@ object Flags { val NOTIFICATION_SHELF_REFACTOR = unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true) + // TODO(b/290787599): Tracking Bug + @JvmField + val NOTIFICATION_ICON_CONTAINER_REFACTOR = + unreleasedFlag(278765923, "notification_icon_container_refactor") + // TODO(b/288326013): Tracking Bug @JvmField val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION = @@ -258,6 +263,16 @@ object Flags { @JvmField val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true) + /** + * Migrate the bottom area to the new keyguard root view. + * Because there is no such thing as a "bottom area" after this, this also breaks it up into + * many smaller, modular pieces. + */ + // TODO(b/290652751): Tracking bug. + @JvmField + val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA = + unreleasedFlag(290652751, "migrate_split_keyguard_bottom_area") + /** Whether to listen for fingerprint authentication over keyguard occluding activities. */ // TODO(b/283260512): Tracking bug. @JvmField @@ -275,7 +290,16 @@ object Flags { /** Migrate the lock icon view to the new keyguard root view. */ // TODO(b/286552209): Tracking bug. @JvmField - val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon") + val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true) + + // TODO(b/288276738): Tracking bug. + @JvmField + val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") + + /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */ + // TODO(b/288074305): Tracking bug. + @JvmField + val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") // 300 - power menu // TODO(b/254512600): Tracking Bug @@ -348,22 +372,10 @@ object Flags { // TODO(b/256614753): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS = releasedFlag(606, "new_status_bar_mobile_icons") - // TODO(b/256614210): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON = releasedFlag(607, "new_status_bar_wifi_icon") - // TODO(b/256614751): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true) - // TODO(b/256613548): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON_BACKEND = - unreleasedFlag(609, "new_status_bar_wifi_icon_backend", teamfood = true) - - // TODO(b/256623670): Tracking Bug - @JvmField - val BATTERY_SHIELD_ICON = - resourceBooleanFlag(610, R.bool.flag_battery_shield_icon, "battery_shield_icon") - // TODO(b/260881289): Tracking Bug val NEW_STATUS_BAR_ICONS_DEBUG_COLORING = unreleasedFlag(611, "new_status_bar_icons_debug_coloring") @@ -780,4 +792,11 @@ object Flags { // TODO(b/285174336): Tracking Bug @JvmField val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing") + + // 3100 - Haptic interactions + + // TODO(b/290213663): Tracking Bug + @JvmField + val ONE_WAY_HAPTICS_API_MIGRATION = + unreleasedFlag(3100, "oneway_haptics_api_migration") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index e7704d625168..d119920e1a42 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -87,7 +87,7 @@ interface KeyguardRepository { val isKeyguardShowing: Flow<Boolean> /** Is the keyguard in a unlocked state? */ - val isKeyguardUnlocked: Flow<Boolean> + val isKeyguardUnlocked: StateFlow<Boolean> /** Is an activity showing over the keyguard? */ val isKeyguardOccluded: Flow<Boolean> @@ -299,7 +299,7 @@ constructor( } .distinctUntilChanged() - override val isKeyguardUnlocked: Flow<Boolean> = + override val isKeyguardUnlocked: StateFlow<Boolean> = conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { @@ -330,7 +330,11 @@ constructor( awaitClose { keyguardStateController.removeCallback(callback) } } - .distinctUntilChanged() + .stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = keyguardStateController.isUnlocked, + ) override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt index c8f7efbeb397..1c200b086990 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt @@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.SceneModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Hosts business and application state accessing logic for the lockscreen scene. */ class LockscreenSceneInteractor @@ -42,7 +36,6 @@ constructor( @Application applicationScope: CoroutineScope, private val authenticationInteractor: AuthenticationInteractor, bouncerInteractorFactory: BouncerInteractor.Factory, - private val sceneInteractor: SceneInteractor, @Assisted private val containerName: String, ) { private val bouncerInteractor: BouncerInteractor = @@ -72,46 +65,6 @@ constructor( initialValue = false, ) - init { - // LOCKING SHOWS Lockscreen. - // - // Move to the lockscreen scene if the device becomes locked while in any scene. - applicationScope.launch { - authenticationInteractor.isUnlocked - .map { !it } - .distinctUntilChanged() - .collect { isLocked -> - if (isLocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Lockscreen), - ) - } - } - } - - // BYPASS UNLOCK. - // - // Moves to the gone scene if bypass is enabled and the device becomes unlocked while in the - // lockscreen scene. - applicationScope.launch { - combine( - authenticationInteractor.isBypassEnabled, - authenticationInteractor.isUnlocked, - sceneInteractor.currentScene(containerName), - ::Triple, - ) - .collect { (isBypassEnabled, isUnlocked, currentScene) -> - if (isBypassEnabled && isUnlocked && currentScene.key == SceneKey.Lockscreen) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) - } - } - } - } - /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ fun dismissLockscreen() { bouncerInteractor.showOrUnlockDevice(containerName = containerName) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 7d14198bdb17..db84268f7c58 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -343,9 +343,9 @@ object KeyguardBottomAreaViewBinder { Utils.getColorAttrDefaultColor( view.context, if (viewModel.isActivated) { - com.android.internal.R.attr.textColorPrimaryInverse + com.android.internal.R.attr.materialColorOnPrimaryFixed } else { - com.android.internal.R.attr.textColorPrimary + com.android.internal.R.attr.materialColorOnSurface }, ) ) @@ -355,9 +355,9 @@ object KeyguardBottomAreaViewBinder { Utils.getColorAttr( view.context, if (viewModel.isActivated) { - com.android.internal.R.attr.colorAccentPrimary + com.android.internal.R.attr.materialColorPrimaryFixed } else { - com.android.internal.R.attr.colorSurface + com.android.internal.R.attr.materialColorSurfaceContainerHigh } ) } else { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/UdfpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/UdfpsLottieViewWrapper.kt new file mode 100644 index 000000000000..3a2c3c70a452 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/UdfpsLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.keyguard.ui.view + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class UdfpsLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt index a4f407612fa8..a437139226de 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt @@ -16,15 +16,19 @@ package com.android.systemui.mediaprojection.taskswitcher.ui +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager import android.content.Context import android.util.Log -import android.widget.Toast +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.NotShowing import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.Showing import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel +import com.android.systemui.util.NotificationChannels import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -37,38 +41,69 @@ class TaskSwitcherNotificationCoordinator @Inject constructor( private val context: Context, + private val notificationManager: NotificationManager, @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, private val viewModel: TaskSwitcherNotificationViewModel, ) { - fun start() { applicationScope.launch { viewModel.uiState.flowOn(mainDispatcher).collect { uiState -> Log.d(TAG, "uiState -> $uiState") when (uiState) { - is Showing -> showNotification(uiState) + is Showing -> showNotification() is NotShowing -> hideNotification() } } } } - private fun showNotification(uiState: Showing) { - val text = - """ - Sharing pauses when you switch apps. - Share this app instead. - Switch back. - """ - .trimIndent() - // TODO(b/286201515): Create actual notification. - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + private fun showNotification() { + notificationManager.notify(TAG, NOTIFICATION_ID, createNotification()) + } + + private fun createNotification(): Notification { + // TODO(b/286201261): implement actions + val actionSwitch = + Notification.Action.Builder( + /* icon = */ null, + context.getString(R.string.media_projection_task_switcher_action_switch), + /* intent = */ null + ) + .build() + + val actionBack = + Notification.Action.Builder( + /* icon = */ null, + context.getString(R.string.media_projection_task_switcher_action_back), + /* intent = */ null + ) + .build() + + val channel = + NotificationChannel( + NotificationChannels.HINTS, + context.getString(R.string.media_projection_task_switcher_notification_channel), + NotificationManager.IMPORTANCE_HIGH + ) + notificationManager.createNotificationChannel(channel) + return Notification.Builder(context, channel.id) + .setSmallIcon(R.drawable.qs_screen_record_icon_on) + .setAutoCancel(true) + .setContentText(context.getString(R.string.media_projection_task_switcher_text)) + .addAction(actionSwitch) + .addAction(actionBack) + .setPriority(Notification.PRIORITY_HIGH) + .setDefaults(Notification.DEFAULT_VIBRATE) + .build() } - private fun hideNotification() {} + private fun hideNotification() { + notificationManager.cancel(NOTIFICATION_ID) + } companion object { private const val TAG = "TaskSwitchNotifCoord" + private const val NOTIFICATION_ID = 5566 } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index a8af67a9fc97..b21b0017208c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -88,11 +88,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.systemui.util.Assert; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.desktopmode.DesktopMode; @@ -115,8 +111,7 @@ import javax.inject.Provider; /** * Utility class to handle edge swipes for back gesture */ -public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin>, - ProtoTraceable<SystemUiTraceProto> { +public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin> { private static final String TAG = "EdgeBackGestureHandler"; private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( @@ -192,7 +187,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private Consumer<Boolean> mButtonForcedVisibleCallback; private final PluginManager mPluginManager; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -402,7 +396,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -425,7 +418,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; mPluginManager = pluginManager; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -557,7 +549,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarAttached() { mIsAttached = true; - mProtoTracer.add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); if (mIsTrackpadGestureFeaturesEnabled) { @@ -576,7 +567,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarDetached() { mIsAttached = false; - mProtoTracer.remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); mSysUiState.removeCallback(mSysUiStateCallback); mInputManager.unregisterInputDeviceListener(mInputDeviceListener); @@ -1135,8 +1125,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack dispatchToBackAnimation(ev); } } - - mProtoTracer.scheduleFrameUpdate(); } private boolean isButtonPressFromTrackpad(MotionEvent ev) { @@ -1285,14 +1273,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack return topActivity != null && mGestureBlockingActivities.contains(topActivity); } - @Override - public void writeToProto(SystemUiTraceProto proto) { - if (proto.edgeBackGestureHandler == null) { - proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto(); - } - proto.edgeBackGestureHandler.allowGesture = mAllowGesture; - } - public void setBackAnimation(BackAnimation backAnimation) { mBackAnimation = backAnimation; updateBackAnimationThresholds(); @@ -1319,7 +1299,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final Handler mHandler; private final Executor mBackgroundExecutor; private final UserTracker mUserTracker; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -1343,7 +1322,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -1365,7 +1343,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -1392,7 +1369,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler, mBackgroundExecutor, mUserTracker, - mProtoTracer, mNavigationModeController, mBackPanelControllerFactory, mViewConfiguration, diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt index c13476fbbe08..eb1ca66f6ca8 100644 --- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.power.domain.interactor import android.os.PowerManager import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.PowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -32,6 +33,7 @@ class PowerInteractor @Inject constructor( private val repository: PowerRepository, + private val keyguardRepository: KeyguardRepository, private val falsingCollector: FalsingCollector, private val screenOffAnimationController: ScreenOffAnimationController, private val statusBarStateController: StatusBarStateController, @@ -54,4 +56,21 @@ constructor( falsingCollector.onScreenOnFromTouch() } } + + /** + * Wakes up the device if the device was dozing or going to sleep in order to display a + * full-screen intent. + */ + fun wakeUpForFullScreenIntent() { + if ( + keyguardRepository.wakefulness.value.isStartingToSleep() || + statusBarStateController.isDozing + ) { + repository.wakeUp(why = FSI_WAKE_WHY, wakeReason = PowerManager.WAKE_REASON_APPLICATION) + } + } + + companion object { + private const val FSI_WAKE_WHY = "full_screen_intent" + } } diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayEducationLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayEducationLottieViewWrapper.kt new file mode 100644 index 000000000000..716a4d649665 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayEducationLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * 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.reardisplay + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class RearDisplayEducationLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index e7dde6617964..207cc1398279 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,11 +25,9 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; @@ -173,8 +171,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private boolean mInputFocusTransferStarted; private float mInputFocusTransferStartY; private long mInputFocusTransferStartMillis; - private float mWindowCornerRadius; - private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; @VisibleForTesting @@ -454,8 +450,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Bundle params = new Bundle(); params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); - params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); - params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER, mSysuiUnlockAnimationController.asBinder()); mUnfoldTransitionProgressForwarder.ifPresent( @@ -588,9 +582,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis com.android.internal.R.string.config_recentsComponentName)); mQuickStepIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); - mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext); - mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils - .supportsRoundedCornersOnWindows(mContext.getResources()); mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); mUiEventLogger = uiEventLogger; @@ -1084,8 +1075,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted); pw.print(" mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY); pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis); - pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius); - pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows); pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion); pw.print(" mNavigationBarSurface="); pw.println(mNavigationBarSurface); pw.print(" mNavBarMode="); pw.println(mNavBarMode); diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt index 91400196bbb1..285ff74f8b2e 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt @@ -17,16 +17,21 @@ package com.android.systemui.scene.domain.startable import com.android.systemui.CoreStartable +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -41,17 +46,20 @@ class SystemUiDefaultSceneContainerStartable constructor( @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, + private val authenticationInteractor: AuthenticationInteractor, + private val keyguardInteractor: KeyguardInteractor, private val featureFlags: FeatureFlags, ) : CoreStartable { override fun start() { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { - keepVisibilityUpdated() + hydrateVisibility() + automaticallySwitchScenes() } } - /** Drives visibility of the scene container. */ - private fun keepVisibilityUpdated() { + /** Updates the visibility of the scene container based on the current scene. */ + private fun hydrateVisibility() { applicationScope.launch { sceneInteractor .currentScene(CONTAINER_NAME) @@ -63,6 +71,63 @@ constructor( } } + /** Switches between scenes based on ever-changing application state. */ + private fun automaticallySwitchScenes() { + applicationScope.launch { + authenticationInteractor.isUnlocked + .map { isUnlocked -> + val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key + val isBypassEnabled = authenticationInteractor.isBypassEnabled.value + when { + isUnlocked -> + when (currentSceneKey) { + // When the device becomes unlocked in Bouncer, go to Gone. + is SceneKey.Bouncer -> SceneKey.Gone + // When the device becomes unlocked in Lockscreen, go to Gone if + // bypass is enabled. + is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled } + // We got unlocked while on a scene that's not Lockscreen or + // Bouncer, no need to change scenes. + else -> null + } + // When the device becomes locked, to Lockscreen. + !isUnlocked -> + when (currentSceneKey) { + // Already on lockscreen or bouncer, no need to change scenes. + is SceneKey.Lockscreen, + is SceneKey.Bouncer -> null + // We got locked while on a scene that's not Lockscreen or Bouncer, + // go to Lockscreen. + else -> SceneKey.Lockscreen + } + else -> null + } + } + .filterNotNull() + .collect { targetSceneKey -> switchToScene(targetSceneKey) } + } + + applicationScope.launch { + keyguardInteractor.wakefulnessModel + .map { it.state == WakefulnessState.ASLEEP } + .distinctUntilChanged() + .collect { isAsleep -> + if (isAsleep) { + // When the device goes to sleep, reset the current scene. + val isUnlocked = authenticationInteractor.isUnlocked.value + switchToScene(if (isUnlocked) SceneKey.Gone else SceneKey.Lockscreen) + } + } + } + } + + private fun switchToScene(targetSceneKey: SceneKey) { + sceneInteractor.setCurrentScene( + containerName = CONTAINER_NAME, + scene = SceneModel(targetSceneKey), + ) + } + companion object { private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index a9cecaaf1f76..6f2256eb6b31 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -117,18 +117,22 @@ public class CropView extends View { @Override protected Parcelable onSaveInstanceState() { + Log.d(TAG, "onSaveInstanceState"); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.mCrop = mCrop; + Log.d(TAG, "saving mCrop=" + mCrop); + return ss; } @Override protected void onRestoreInstanceState(Parcelable state) { + Log.d(TAG, "onRestoreInstanceState"); SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); - + Log.d(TAG, "restoring mCrop=" + ss.mCrop + " (was " + mCrop + ")"); mCrop = ss.mCrop; } @@ -242,6 +246,7 @@ public class CropView extends View { * Set the given boundary to the given value without animation. */ public void setBoundaryPosition(CropBoundary boundary, float position) { + Log.i(TAG, "setBoundaryPosition: " + boundary + ", position=" + position); position = (float) getAllowedValues(boundary).clamp(position); switch (boundary) { case TOP: @@ -260,6 +265,7 @@ public class CropView extends View { Log.w(TAG, "No boundary selected"); break; } + Log.i(TAG, "Updated mCrop: " + mCrop); invalidate(); } @@ -350,26 +356,31 @@ public class CropView extends View { mCropInteractionListener = listener; } - private Range getAllowedValues(CropBoundary boundary) { + private Range<Float> getAllowedValues(CropBoundary boundary) { + float upper = 0f; + float lower = 1f; switch (boundary) { case TOP: - return new Range<>(0f, - mCrop.bottom - pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.BOTTOM)); + lower = 0f; + upper = mCrop.bottom - pixelDistanceToFraction(mCropTouchMargin, + CropBoundary.BOTTOM); + break; case BOTTOM: - return new Range<>( - mCrop.top + pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.TOP), 1f); + lower = mCrop.top + pixelDistanceToFraction(mCropTouchMargin, CropBoundary.TOP); + upper = 1; + break; case LEFT: - return new Range<>(0f, - mCrop.right - pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.RIGHT)); + lower = 0f; + upper = mCrop.right - pixelDistanceToFraction(mCropTouchMargin, CropBoundary.RIGHT); + break; case RIGHT: - return new Range<>( - mCrop.left + pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.LEFT), 1f); + lower = mCrop.left + pixelDistanceToFraction(mCropTouchMargin, CropBoundary.LEFT); + upper = 1; + break; } - return null; + Log.i(TAG, "getAllowedValues: " + boundary + ", " + + "result=[lower=" + lower + ", upper=" + upper + "]"); + return new Range<>(lower, upper); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 4bc7ec844794..e6e1faccc3e2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -36,17 +36,18 @@ import android.text.TextUtils; import android.util.Log; import android.view.ScrollCaptureResponse; import android.view.View; -import android.view.ViewTreeObserver; import android.widget.ImageView; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.internal.app.ChooserActivity; import com.android.internal.logging.UiEventLogger; +import com.android.internal.view.OneShotPreDrawListener; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.screenshot.CropView.CropBoundary; import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot; import com.android.systemui.settings.UserTracker; @@ -215,6 +216,7 @@ public class LongScreenshotActivity extends Activity { mPreview.setImageDrawable(drawable); mMagnifierView.setDrawable(mLongScreenshot.getDrawable(), mLongScreenshot.getWidth(), mLongScreenshot.getHeight()); + Log.i(TAG, "Completed: " + longScreenshot); // Original boundaries go from the image tile set's y=0 to y=pageSize, so // we animate to that as a starting crop position. float topFraction = Math.max(0, @@ -223,31 +225,26 @@ public class LongScreenshotActivity extends Activity { 1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight()) / (float) mLongScreenshot.getHeight()); + Log.i(TAG, "topFraction: " + topFraction); + Log.i(TAG, "bottomFraction: " + bottomFraction); + mEnterTransitionView.setImageDrawable(drawable); - mEnterTransitionView.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this); - updateImageDimensions(); - mEnterTransitionView.post(() -> { - Rect dest = new Rect(); - mEnterTransitionView.getBoundsOnScreen(dest); - mLongScreenshotHolder.takeTransitionDestinationCallback() - .setTransitionDestination(dest, () -> { - mPreview.animate().alpha(1f); - mCropView.setBoundaryPosition( - CropView.CropBoundary.TOP, topFraction); - mCropView.setBoundaryPosition( - CropView.CropBoundary.BOTTOM, bottomFraction); - mCropView.animateEntrance(); - mCropView.setVisibility(View.VISIBLE); - setButtonsEnabled(true); - }); + OneShotPreDrawListener.add(mEnterTransitionView, () -> { + updateImageDimensions(); + mEnterTransitionView.post(() -> { + Rect dest = new Rect(); + mEnterTransitionView.getBoundsOnScreen(dest); + mLongScreenshotHolder.takeTransitionDestinationCallback() + .setTransitionDestination(dest, () -> { + mPreview.animate().alpha(1f); + mCropView.setBoundaryPosition(CropBoundary.TOP, topFraction); + mCropView.setBoundaryPosition(CropBoundary.BOTTOM, bottomFraction); + mCropView.animateEntrance(); + mCropView.setVisibility(View.VISIBLE); + setButtonsEnabled(true); }); - return true; - } - }); + }); + }); // Immediately export to temp image file for saved state mCacheSaveFuture = mImageExporter.exportToRawFile(mBackgroundExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index 93e5021fd6e0..e93f737308ba 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -365,6 +365,9 @@ public class ScrollCaptureClient { @Override public void onImageAvailable(ImageReader reader) { synchronized (mLock) { + if (mCapturedImage != null) { + mCapturedImage.close(); + } mCapturedImage = mReader.acquireLatestImage(); if (mCapturedArea != null) { completeCaptureRequest(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 30a0b8f2d76f..bb34ede2cf5e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -130,8 +130,14 @@ public class ScrollCaptureController { @Override public String toString() { - return "LongScreenshot{w=" + mImageTileSet.getWidth() - + ", h=" + mImageTileSet.getHeight() + "}"; + return "LongScreenshot{" + + "l=" + mImageTileSet.getLeft() + ", " + + "t=" + mImageTileSet.getTop() + ", " + + "r=" + mImageTileSet.getRight() + ", " + + "b=" + mImageTileSet.getBottom() + ", " + + "w=" + mImageTileSet.getWidth() + ", " + + "h=" + mImageTileSet.getHeight() + + "}"; } public Drawable getDrawable() { diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 5199bd43f982..182e4569b549 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -18,6 +18,7 @@ package com.android.systemui.settings.brightness; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY; import android.app.Activity; import android.graphics.Rect; @@ -29,8 +30,10 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -38,34 +41,42 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.List; -import java.util.concurrent.Executor; import javax.inject.Inject; /** A dialog that provides controls for adjusting the screen brightness. */ public class BrightnessDialog extends Activity { + @VisibleForTesting + static final int DIALOG_TIMEOUT_MILLIS = 3000; + private BrightnessController mBrightnessController; private final BrightnessSliderController.Factory mToggleSliderFactory; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; - private final Executor mMainExecutor; + private final DelayableExecutor mMainExecutor; private final Handler mBackgroundHandler; + private final AccessibilityManagerWrapper mAccessibilityMgr; + private Runnable mCancelTimeoutRunnable; @Inject public BrightnessDialog( UserTracker userTracker, DisplayTracker displayTracker, BrightnessSliderController.Factory factory, - @Main Executor mainExecutor, - @Background Handler bgHandler) { + @Main DelayableExecutor mainExecutor, + @Background Handler bgHandler, + AccessibilityManagerWrapper accessibilityMgr) { mUserTracker = userTracker; mDisplayTracker = displayTracker; mToggleSliderFactory = factory; mMainExecutor = mainExecutor; mBackgroundHandler = bgHandler; + mAccessibilityMgr = accessibilityMgr; } @@ -122,6 +133,14 @@ public class BrightnessDialog extends Activity { } @Override + protected void onResume() { + super.onResume(); + if (triggeredByBrightnessKey()) { + scheduleTimeout(); + } + } + + @Override protected void onPause() { super.onPause(); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); @@ -139,9 +158,25 @@ public class BrightnessDialog extends Activity { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } finish(); } return super.onKeyDown(keyCode, event); } + + private boolean triggeredByBrightnessKey() { + return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false); + } + + private void scheduleTimeout() { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } + final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS, + AccessibilityManager.FLAG_CONTENT_CONTROLS); + mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d97db3b27c87..ea15035a6c6f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1076,6 +1076,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mTapAgainViewController.init(); mShadeHeaderController.init(); + mShadeHeaderController.setShadeCollapseAction( + () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index ebb98883c679..02f337a8752a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -18,6 +18,7 @@ package com.android.systemui.shade; import android.view.MotionEvent; +import com.android.systemui.CoreStartable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; @@ -31,7 +32,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; * these are coordinated with {@link StatusBarKeyguardViewManager} via * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others. */ -public interface ShadeController { +public interface ShadeController extends CoreStartable { /** Make our window larger and the shade expanded */ void instantExpandShade(); @@ -164,17 +165,14 @@ public interface ShadeController { void onLaunchAnimationEnd(boolean launchIsFullScreen); /** Sets the listener for when the visibility of the shade changes. */ - default void setVisibilityListener(ShadeVisibilityListener listener) {}; + default void setVisibilityListener(ShadeVisibilityListener listener) {} /** */ - default void setNotificationPresenter(NotificationPresenter presenter) {}; + default void setNotificationPresenter(NotificationPresenter presenter) {} /** */ default void setNotificationShadeWindowViewController( - NotificationShadeWindowViewController notificationShadeWindowViewController) {}; - - /** */ - default void setShadeViewController(ShadeViewController shadeViewController) {}; + NotificationShadeWindowViewController notificationShadeWindowViewController) {} /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt index 4d0500786ca3..5f95bcae030e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt @@ -1,3 +1,19 @@ +/* + * 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.shade import android.view.MotionEvent @@ -7,6 +23,7 @@ import javax.inject.Inject /** Empty implementation of ShadeController for variants of Android without shades. */ @SysUISingleton open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController { + override fun start() {} override fun instantExpandShade() {} override fun instantCollapseShade() {} override fun animateCollapseShade( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index af74a8d7dca1..22c638177a48 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -63,6 +63,7 @@ public final class ShadeControllerImpl implements ShadeController { private final StatusBarWindowController mStatusBarWindowController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Lazy<ShadeViewController> mShadeViewControllerLazy; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<NotificationGutsManager> mGutsManager; @@ -70,8 +71,6 @@ public final class ShadeControllerImpl implements ShadeController { private boolean mExpandedVisible; - // TODO(b/237661616): Rename this variable to mShadeViewController. - private ShadeViewController mNotificationPanelViewController; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; private ShadeVisibilityListener mShadeVisibilityListener; @@ -87,11 +86,13 @@ public final class ShadeControllerImpl implements ShadeController { DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, WindowManager windowManager, + Lazy<ShadeViewController> shadeViewControllerLazy, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager ) { mCommandQueue = commandQueue; mMainExecutor = mainExecutor; + mShadeViewControllerLazy = shadeViewControllerLazy; mStatusBarStateController = statusBarStateController; mStatusBarWindowController = statusBarWindowController; mDeviceProvisionedController = deviceProvisionedController; @@ -107,7 +108,7 @@ public final class ShadeControllerImpl implements ShadeController { public void instantExpandShade() { // Make our window larger and the panel expanded. makeExpandedVisible(true /* force */); - mNotificationPanelViewController.expand(false /* animate */); + getShadeViewController().expand(false /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */); } @@ -123,13 +124,13 @@ public final class ShadeControllerImpl implements ShadeController { "animateCollapse(): mExpandedVisible=" + mExpandedVisible + "flags=" + flags); } if (getNotificationShadeWindowView() != null - && mNotificationPanelViewController.canBeCollapsed() + && getShadeViewController().canBeCollapsed() && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { // release focus immediately to kick off focus change transition mNotificationShadeWindowController.setNotificationShadeFocusable(false); mNotificationShadeWindowViewController.cancelExpandHelper(); - mNotificationPanelViewController.collapse(true, delayed, speedUpFactor); + getShadeViewController().collapse(true, delayed, speedUpFactor); } } @@ -138,7 +139,7 @@ public final class ShadeControllerImpl implements ShadeController { if (!mCommandQueue.panelsEnabled()) { return; } - mNotificationPanelViewController.expandToNotifications(); + getShadeViewController().expandToNotifications(); } @Override @@ -149,12 +150,12 @@ public final class ShadeControllerImpl implements ShadeController { // Settings are not available in setup if (!mDeviceProvisionedController.isCurrentUserSetup()) return; - mNotificationPanelViewController.expandToQs(); + getShadeViewController().expandToQs(); } @Override public boolean closeShadeIfOpen() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); notifyVisibilityChanged(false); @@ -170,12 +171,12 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean isShadeFullyOpen() { - return mNotificationPanelViewController.isShadeFullyExpanded(); + return getShadeViewController().isShadeFullyExpanded(); } @Override public boolean isExpandingOrCollapsing() { - return mNotificationPanelViewController.isExpandingOrCollapsing(); + return getShadeViewController().isExpandingOrCollapsing(); } @Override public void postAnimateCollapseShade() { @@ -194,13 +195,13 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void postOnShadeExpanded(Runnable executable) { - mNotificationPanelViewController.addOnGlobalLayoutListener( + getShadeViewController().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (getNotificationShadeWindowView().isVisibleToUser()) { - mNotificationPanelViewController.removeOnGlobalLayoutListener(this); - mNotificationPanelViewController.postToView(executable); + getShadeViewController().removeOnGlobalLayoutListener(this); + getShadeViewController().postToView(executable); } } }); @@ -224,7 +225,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean collapseShade() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { // close the shade if it was open animateCollapseShadeForcedDelayed(); notifyVisibilityChanged(false); @@ -252,10 +253,10 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void cancelExpansionAndCollapseShade() { - if (mNotificationPanelViewController.isTracking()) { + if (getShadeViewController().isTracking()) { mNotificationShadeWindowViewController.cancelCurrentTouch(); } - if (mNotificationPanelViewController.isPanelExpanded() + if (getShadeViewController().isPanelExpanded() && mStatusBarStateController.getState() == StatusBarState.SHADE) { animateCollapseShade(); } @@ -311,7 +312,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void instantCollapseShade() { - mNotificationPanelViewController.instantCollapse(); + getShadeViewController().instantCollapse(); runPostCollapseRunnables(); } @@ -342,7 +343,7 @@ public final class ShadeControllerImpl implements ShadeController { } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) - mNotificationPanelViewController.collapse(false, false, 1.0f); + getShadeViewController().collapse(false, false, 1.0f); mExpandedVisible = false; notifyVisibilityChanged(false); @@ -364,7 +365,7 @@ public final class ShadeControllerImpl implements ShadeController { notifyExpandedVisibleChanged(false); mCommandQueue.recomputeDisableFlags( mDisplayId, - mNotificationPanelViewController.shouldHideStatusBarIconsWhenExpanded()); + getShadeViewController().shouldHideStatusBarIconsWhenExpanded()); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -406,11 +407,14 @@ public final class ShadeControllerImpl implements ShadeController { return mNotificationShadeWindowViewController.getView(); } + private ShadeViewController getShadeViewController() { + return mShadeViewControllerLazy.get(); + } + @Override - public void setShadeViewController(ShadeViewController shadeViewController) { - mNotificationPanelViewController = shadeViewController; - mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables); - mNotificationPanelViewController.setOpenCloseListener( + public void start() { + getShadeViewController().setTrackingStartedListener(this::runPostCollapseRunnables); + getShadeViewController().setOpenCloseListener( new OpenCloseListener() { @Override public void onClosingFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 411f91f0cb07..8b89ff49f418 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -122,6 +122,8 @@ constructor( } } + var shadeCollapseAction: Runnable? = null + private lateinit var iconManager: StatusBarIconController.TintedIconManager private lateinit var carrierIconSlots: List<String> private lateinit var mShadeCarrierGroupController: ShadeCarrierGroupController @@ -469,9 +471,11 @@ constructor( if (largeScreenActive) { logInstantEvent("Large screen constraints set") header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID) + systemIcons.setOnClickListener { shadeCollapseAction?.run() } } else { logInstantEvent("Small screen constraints set") header.setTransition(HEADER_TRANSITION_ID) + systemIcons.setOnClickListener(null) } header.jumpToState(header.startState) updatePosition() diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 8ae9e5e1fb8c..8ec8d115de78 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.phone.KeyguardBottomAreaView +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.TapAgainView import com.android.systemui.statusbar.policy.BatteryController @@ -64,7 +65,7 @@ import javax.inject.Named import javax.inject.Provider /** Module for classes related to the notification shade. */ -@Module +@Module(includes = [StartShadeModule::class]) abstract class ShadeModule { @Binds @@ -281,17 +282,16 @@ abstract class ShadeModule { tunerService: TunerService, @Main mainHandler: Handler, contentResolver: ContentResolver, - featureFlags: FeatureFlags, batteryController: BatteryController, ): BatteryMeterViewController { return BatteryMeterViewController( batteryMeterView, + StatusBarLocation.QS, userTracker, configurationController, tunerService, mainHandler, contentResolver, - featureFlags, batteryController, ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt new file mode 100644 index 000000000000..c50693c30533 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt @@ -0,0 +1,31 @@ +/* + * 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.shade + +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +internal abstract class StartShadeModule { + @Binds + @IntoMap + @ClassKey(ShadeController::class) + abstract fun bind(shadeController: ShadeController): CoreStartable +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt index ce730baeed0d..5d06f8d083d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt @@ -21,8 +21,8 @@ import android.widget.FrameLayout /** * A temporary base class that's shared between our old status bar connectivity view implementations - * ([StatusBarWifiView], [StatusBarMobileView]) and our new status bar implementations ( - * [ModernStatusBarWifiView], [ModernStatusBarMobileView]). + * ([StatusBarMobileView]) and our new status bar implementations ([ModernStatusBarWifiView], + * [ModernStatusBarMobileView]). * * Once our refactor is over, we should be able to delete this go-between class and the old view * class. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a532195c5b9f..92df78bac17f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -73,7 +73,6 @@ import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.statusbar.policy.CallbackController; -import com.android.systemui.tracing.ProtoTracer; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -190,7 +189,6 @@ public class CommandQueue extends IStatusBar.Stub implements */ private int mLastUpdatedImeDisplayId = INVALID_DISPLAY; private final DisplayTracker mDisplayTracker; - private ProtoTracer mProtoTracer; private final @Nullable CommandRegistry mRegistry; private final @Nullable DumpHandler mDumpHandler; @@ -504,18 +502,16 @@ public class CommandQueue extends IStatusBar.Stub implements @VisibleForTesting public CommandQueue(Context context, DisplayTracker displayTracker) { - this(context, displayTracker, null, null, null); + this(context, displayTracker, null, null); } public CommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { mDisplayTracker = displayTracker; - mProtoTracer = protoTracer; mRegistry = registry; mDumpHandler = dumpHandler; mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() { @@ -1160,9 +1156,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void startTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.start(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, true).sendToTarget(); } } @@ -1170,9 +1163,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void stopTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.stop(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, false).sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index cff71d2edd23..fbbee53468c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -160,7 +160,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private ObjectAnimator mIconAppearAnimator; private ObjectAnimator mDotAnimator; private float mDotAppearAmount; - private OnVisibilityChangedListener mOnVisibilityChangedListener; private int mDrawableColor; private int mIconColor; private int mDecorColor; @@ -179,7 +178,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private int mCachedContrastBackgroundColor = NO_COLOR; private float[] mMatrix; private ColorMatrixColorFilter mMatrixColorFilter; - private boolean mIsInShelf; private Runnable mLayoutRunnable; private boolean mDismissed; private Runnable mOnDismissListener; @@ -357,19 +355,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi return mNotification != null; } - private static boolean streq(String a, String b) { - if (a == b) { - return true; - } - if (a == null && b != null) { - return false; - } - if (a != null && b == null) { - return false; - } - return a.equals(b); - } - public boolean equalIcons(Icon a, Icon b) { if (a == b) return true; if (a.getType() != b.getType()) return false; @@ -908,7 +893,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi if (targetAmount != currentAmount) { mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT, currentAmount, targetAmount); - mDotAnimator.setInterpolator(interpolator);; + mDotAnimator.setInterpolator(interpolator); mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST : duration); final boolean runRunnable = !runnableAdded; @@ -965,22 +950,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } } - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - if (mOnVisibilityChangedListener != null) { - mOnVisibilityChangedListener.onVisibilityChanged(visibility); - } - } - public float getDotAppearAmount() { return mDotAppearAmount; } - public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) { - mOnVisibilityChangedListener = listener; - } - public void setDozing(boolean dozing, boolean fade, long delay) { mDozer.setDozing(f -> { mDozeAmount = f; @@ -1014,14 +987,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi outRect.bottom += translationY; } - public void setIsInShelf(boolean isInShelf) { - mIsInShelf = isInShelf; - } - - public boolean isInShelf() { - return mIsInShelf; - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -1103,8 +1068,4 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi public boolean showsConversation() { return mShowsConversation; } - - public interface OnVisibilityChangedListener { - void onVisibilityChanged(int newVisibility); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java deleted file mode 100644 index 8d7214d6bd75..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2018 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.statusbar; - -import static com.android.systemui.plugins.DarkIconDispatcher.getTint; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.android.systemui.R; -import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; - -import java.util.ArrayList; - -/** - * Start small: StatusBarWifiView will be able to layout from a WifiIconState - */ -public class StatusBarWifiView extends BaseStatusBarFrameLayout implements DarkReceiver { - private static final String TAG = "StatusBarWifiView"; - - /// Used to show etc dots - private StatusBarIconView mDotView; - /// Contains the main icon layout - private LinearLayout mWifiGroup; - private ImageView mWifiIcon; - private ImageView mIn; - private ImageView mOut; - private View mInoutContainer; - private View mSignalSpacer; - private View mAirplaneSpacer; - private WifiIconState mState; - private String mSlot; - @StatusBarIconView.VisibleState - private int mVisibleState = STATE_HIDDEN; - - public static StatusBarWifiView fromContext(Context context, String slot) { - LayoutInflater inflater = LayoutInflater.from(context); - StatusBarWifiView v = (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); - v.setSlot(slot); - v.init(); - v.setVisibleState(STATE_ICON); - return v; - } - - public StatusBarWifiView(Context context) { - super(context); - } - - public StatusBarWifiView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public void setSlot(String slot) { - mSlot = slot; - } - - @Override - public void setStaticDrawableColor(int color) { - ColorStateList list = ColorStateList.valueOf(color); - mWifiIcon.setImageTintList(list); - mIn.setImageTintList(list); - mOut.setImageTintList(list); - mDotView.setDecorColor(color); - } - - @Override - public void setDecorColor(int color) { - mDotView.setDecorColor(color); - } - - @Override - public String getSlot() { - return mSlot; - } - - @Override - public boolean isIconVisible() { - return mState != null && mState.visible; - } - - @Override - public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) { - if (state == mVisibleState) { - return; - } - mVisibleState = state; - - switch (state) { - case STATE_ICON: - mWifiGroup.setVisibility(View.VISIBLE); - mDotView.setVisibility(View.GONE); - break; - case STATE_DOT: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.VISIBLE); - break; - case STATE_HIDDEN: - default: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.GONE); - break; - } - } - - @Override - @StatusBarIconView.VisibleState - public int getVisibleState() { - return mVisibleState; - } - - @Override - public void getDrawingRect(Rect outRect) { - super.getDrawingRect(outRect); - float translationX = getTranslationX(); - float translationY = getTranslationY(); - outRect.left += translationX; - outRect.right += translationX; - outRect.top += translationY; - outRect.bottom += translationY; - } - - private void init() { - mWifiGroup = findViewById(R.id.wifi_group); - mWifiIcon = findViewById(R.id.wifi_signal); - mIn = findViewById(R.id.wifi_in); - mOut = findViewById(R.id.wifi_out); - mSignalSpacer = findViewById(R.id.wifi_signal_spacer); - mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); - mInoutContainer = findViewById(R.id.inout_container); - - initDotView(); - } - - private void initDotView() { - mDotView = new StatusBarIconView(mContext, mSlot, null); - mDotView.setVisibleState(STATE_DOT); - - int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size_sp); - LayoutParams lp = new LayoutParams(width, width); - lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; - addView(mDotView, lp); - } - - public void applyWifiState(WifiIconState state) { - boolean requestLayout = false; - - if (state == null) { - requestLayout = getVisibility() != View.GONE; - setVisibility(View.GONE); - mState = null; - } else if (mState == null) { - requestLayout = true; - mState = state.copy(); - initViewState(); - } else if (!mState.equals(state)) { - requestLayout = updateState(state.copy()); - } - - if (requestLayout) { - requestLayout(); - } - } - - private boolean updateState(WifiIconState state) { - setContentDescription(state.contentDescription); - if (mState.resId != state.resId && state.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(state.resId)); - } - - mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE); - - boolean needsLayout = state.activityIn != mState.activityIn - ||state.activityOut != mState.activityOut; - - if (mState.visible != state.visible) { - needsLayout |= true; - setVisibility(state.visible ? View.VISIBLE : View.GONE); - } - - mState = state; - return needsLayout; - } - - private void initViewState() { - setContentDescription(mState.contentDescription); - if (mState.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(mState.resId)); - } - - mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); - setVisibility(mState.visible ? View.VISIBLE : View.GONE); - } - - @Override - public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { - int areaTint = getTint(areas, this, tint); - ColorStateList color = ColorStateList.valueOf(areaTint); - mWifiIcon.setImageTintList(color); - mIn.setImageTintList(color); - mOut.setImageTintList(color); - mDotView.setDecorColor(areaTint); - mDotView.setIconColor(areaTint, false); - } - - - @Override - public String toString() { - return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; - } -} 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 73f181b8c734..9aa28c31cfd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -18,10 +18,6 @@ package com.android.systemui.statusbar.connectivity; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.Nullable; @@ -557,10 +553,6 @@ public class NetworkControllerImpl extends BroadcastReceiver mBroadcastDispatcher.unregisterReceiver(this); } - public int getConnectedWifiLevel() { - return mWifiSignalController.getState().level; - } - @Override public AccessPointController getAccessPointController() { return mAccessPoints; @@ -654,14 +646,6 @@ public class NetworkControllerImpl extends BroadcastReceiver return mWifiSignalController.isCarrierMergedWifi(subId); } - boolean hasDefaultNetwork() { - return !mNoDefaultNetwork; - } - - boolean isNonCarrierWifiNetworkAvailable() { - return !mNoNetworksAvailable; - } - boolean isEthernetDefault() { return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); } @@ -1242,15 +1226,12 @@ public class NetworkControllerImpl extends BroadcastReceiver } private boolean mDemoInetCondition; - private WifiState mDemoWifiState; @Override public void onDemoModeStarted() { if (DEBUG) Log.d(TAG, "Entering demo mode"); unregisterListeners(); mDemoInetCondition = mInetCondition; - mDemoWifiState = mWifiSignalController.getState(); - mDemoWifiState.ssid = "DemoMode"; } @Override @@ -1300,41 +1281,6 @@ public class NetworkControllerImpl extends BroadcastReceiver controller.updateConnectivity(connected, connected); } } - String wifi = args.getString("wifi"); - if (wifi != null && !mStatusBarPipelineFlags.runNewWifiIconBackend()) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiState.level = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - mDemoWifiState.connected = mDemoWifiState.level >= 0; - } - String activity = args.getString("activity"); - if (activity != null) { - switch (activity) { - case "inout": - mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT); - break; - case "in": - mWifiSignalController.setActivity(DATA_ACTIVITY_IN); - break; - case "out": - mWifiSignalController.setActivity(DATA_ACTIVITY_OUT); - break; - default: - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - break; - } - } else { - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - } - String ssid = args.getString("ssid"); - if (ssid != null) { - mDemoWifiState.ssid = ssid; - } - mDemoWifiState.enabled = show; - mWifiSignalController.notifyListeners(); - } String sims = args.getString("sims"); if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) { int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 035fa0454bfc..e5ba3ce1fdae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -78,7 +78,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.SystemClock; @@ -195,11 +194,10 @@ public interface CentralSurfacesDependenciesModule { static CommandQueue provideCommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { - return new CommandQueue(context, displayTracker, protoTracer, registry, dumpHandler); + return new CommandQueue(context, displayTracker, registry, dumpHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 776956a20140..56390002490c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -30,9 +30,11 @@ import androidx.core.animation.Animator import androidx.core.animation.AnimatorListenerAdapter import androidx.core.animation.AnimatorSet import androidx.core.animation.ValueAnimator +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.util.animation.AnimationUtil.Companion.frames @@ -46,7 +48,7 @@ class SystemEventChipAnimationController @Inject constructor( private val context: Context, private val statusBarWindowController: StatusBarWindowController, private val contentInsetsProvider: StatusBarContentInsetsProvider, - private val featureFlags: FeatureFlags + private val featureFlags: FeatureFlags, ) : SystemStatusAnimationCallback { private lateinit var animationWindowView: FrameLayout @@ -56,7 +58,8 @@ class SystemEventChipAnimationController @Inject constructor( // Left for LTR, Right for RTL private var animationDirection = LEFT - private var chipBounds = Rect() + + @VisibleForTesting var chipBounds = Rect() private val chipWidth get() = chipBounds.width() private val chipRight get() = chipBounds.right private val chipLeft get() = chipBounds.left @@ -69,7 +72,7 @@ class SystemEventChipAnimationController @Inject constructor( private var animRect = Rect() // TODO: move to dagger - private var initialized = false + @VisibleForTesting var initialized = false /** * Give the chip controller a chance to inflate and configure the chip view before we start @@ -98,23 +101,7 @@ class SystemEventChipAnimationController @Inject constructor( View.MeasureSpec.makeMeasureSpec( (animationWindowView.parent as View).height, AT_MOST)) - // decide which direction we're animating from, and then set some screen coordinates - val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation() - val chipTop = ((animationWindowView.parent as View).height - it.view.measuredHeight) / 2 - val chipBottom = chipTop + it.view.measuredHeight - val chipRight: Int - val chipLeft: Int - when (animationDirection) { - LEFT -> { - chipRight = contentRect.right - chipLeft = contentRect.right - it.chipWidth - } - else /* RIGHT */ -> { - chipLeft = contentRect.left - chipRight = contentRect.left + it.chipWidth - } - } - chipBounds = Rect(chipLeft, chipTop, chipRight, chipBottom) + updateChipBounds(it, contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()) } } @@ -253,16 +240,67 @@ class SystemEventChipAnimationController @Inject constructor( return animSet } - private fun init() { + fun init() { initialized = true themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings) animationWindowView = LayoutInflater.from(themedContext) .inflate(R.layout.system_event_animation_window, null) as FrameLayout - val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) - lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL + // Matches status_bar.xml + val height = themedContext.resources.getDimensionPixelSize(R.dimen.status_bar_height) + val lp = FrameLayout.LayoutParams(MATCH_PARENT, height) + lp.gravity = Gravity.END or Gravity.TOP statusBarWindowController.addViewToWindow(animationWindowView, lp) animationWindowView.clipToPadding = false animationWindowView.clipChildren = false + + // Use contentInsetsProvider rather than configuration controller, since we only care + // about status bar dimens + contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener { + override fun onStatusBarContentInsetsChanged() { + val newContentArea = contentInsetsProvider + .getStatusBarContentAreaForCurrentRotation() + updateDimens(newContentArea) + + // If we are currently animating, we have to re-solve for the chip bounds. If we're + // not animating then [prepareChipAnimation] will take care of it for us + currentAnimatedView?.let { + updateChipBounds(it, newContentArea) + } + } + }) + } + + private fun updateDimens(contentArea: Rect) { + val lp = animationWindowView.layoutParams as FrameLayout.LayoutParams + lp.height = contentArea.height() + + animationWindowView.layoutParams = lp + } + + /** + * Use the current status bar content area and the current chip's measured size to update + * the animation rect and chipBounds. This method can be called at any time and will update + * the current animation values properly during e.g. a rotation. + */ + private fun updateChipBounds(chip: BackgroundAnimatableView, contentArea: Rect) { + // decide which direction we're animating from, and then set some screen coordinates + val chipTop = (contentArea.bottom - chip.view.measuredHeight) / 2 + val chipBottom = chipTop + chip.view.measuredHeight + val chipRight: Int + val chipLeft: Int + + when (animationDirection) { + LEFT -> { + chipRight = contentArea.right + chipLeft = contentArea.right - chip.chipWidth + } + else /* RIGHT */ -> { + chipLeft = contentArea.left + chipRight = contentArea.left + chip.chipWidth + } + } + chipBounds = Rect(chipLeft, chipTop, chipRight, chipBottom) + animRect.set(chipBounds) } private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 212f2c215989..1cf9c1e1f299 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -3,7 +3,10 @@ package com.android.systemui.statusbar.notification import android.util.FloatProperty import android.view.View import androidx.annotation.FloatRange +import com.android.systemui.Dependency import com.android.systemui.R +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator import kotlin.math.abs @@ -20,6 +23,8 @@ interface Roundable { /** Properties required for a Roundable */ val roundableState: RoundableState + val clipHeight: Int + /** Current top roundness */ @get:FloatRange(from = 0.0, to = 1.0) @JvmDefault @@ -40,12 +45,16 @@ interface Roundable { /** Current top corner in pixel, based on [topRoundness] and [maxRadius] */ @JvmDefault val topCornerRadius: Float - get() = topRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius + else topRoundness * maxRadius /** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */ @JvmDefault val bottomCornerRadius: Float - get() = bottomRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius + else bottomRoundness * maxRadius /** Get and update the current radii */ @JvmDefault @@ -320,14 +329,20 @@ interface Roundable { * @param roundable Target of the radius animation * @param maxRadius Max corner radius in pixels */ -class RoundableState( +class RoundableState +@JvmOverloads +constructor( internal val targetView: View, private val roundable: Roundable, maxRadius: Float, + private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java) ) { internal var maxRadius = maxRadius private set + internal val newHeadsUpAnimFlagEnabled + get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS) + /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) @@ -344,6 +359,41 @@ class RoundableState( internal var bottomRoundness = 0f private set + internal val topCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return topRadius - (overShoot * topRoundness / (topRoundness + bottomRoundness)) + } + + return topRadius + } + + internal val bottomCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return bottomRadius - + (overShoot * bottomRoundness / (topRoundness + bottomRoundness)) + } + + return bottomRadius + } + /** Last requested top roundness associated by [SourceType] */ internal val topRoundnessMap = mutableMapOf<SourceType, Float>() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index 90014c2518d1..78225dfe8d86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -87,6 +87,7 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor( private val userTrackerCallback = object : UserTracker.Callback { override fun onUserChanged(newUser: Int, userContext: Context) { + readShowSilentNotificationSetting() if (isLockedOrLocking) { // maybe public mode changed notifyStateChanged("onUserSwitched") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 27510d47b5ab..908c11a1d076 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -566,12 +566,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public float getTopCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getTopCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getTopCornerRadius(), fraction); } @Override public float getBottomCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getBottomCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getBottomCornerRadius(), fraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 9aa50e989ff1..7f23c1b89b51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -28,7 +28,10 @@ import android.util.IndentingPrintWriter; import android.view.View; import android.view.ViewOutlineProvider; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.util.DumpUtilsKt; @@ -47,12 +50,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { private float mOutlineAlpha = -1f; private boolean mAlwaysRoundBothCorners; private Path mTmpPath = new Path(); + private final FeatureFlags mFeatureFlags; /** * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. */ protected boolean mDismissUsingRowTranslationX = true; + private float[] mTmpCornerRadii = new float[8]; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @@ -81,6 +86,15 @@ public abstract class ExpandableOutlineView extends ExpandableView { return mRoundableState; } + @Override + public int getClipHeight() { + if (mCustomOutline) { + return mOutlineRect.height(); + } + + return super.getClipHeight(); + } + protected Path getClipPath(boolean ignoreTranslation) { int left; int top; @@ -112,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { return EMPTY_PATH; } float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius(); - if (topRadius + bottomRadius > height) { + if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) { float overShoot = topRadius + bottomRadius - height; float currentTopRoundness = getTopRoundness(); float currentBottomRoundness = getBottomRoundness(); @@ -153,6 +167,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { super(context, attrs); setOutlineProvider(mProvider); initDimens(); + mFeatureFlags = Dependency.get(FeatureFlags.class); } @Override @@ -360,4 +375,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { } }); } + + // TODO(b/290365128) replace with ViewRefactorFlag + protected boolean isNewHeadsUpAnimFlagEnabled() { + return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index f98624409e56..c4c116ba0613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -93,6 +93,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro return mRoundableState; } + @Override + public int getClipHeight() { + int clipHeight = Math.max(mActualHeight - mClipTopAmount - mClipBottomAmount, 0); + return Math.max(clipHeight, mMinimumHeightForClipping); + } + private void initDimens() { mContentShift = getResources().getDimensionPixelSize( R.dimen.shelf_transform_content_shift); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index ef5e86f06ef0..87205e2df9c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -120,6 +120,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } @Override + public int getClipHeight() { + return mView.getHeight(); + } + + @Override public void applyRoundnessAndInvalidate() { if (mRoundnessChangedListener != null) { // We cannot apply the rounded corner to this View, so our parents (in drawChild()) will diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt index 04308b47abc9..a8d8a8e03ed5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt @@ -30,8 +30,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView */ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) { + override var clipHeight = 0 var cornerRadius = 0f - var clipHeight = 0 var clipRect = RectF() var clipPath = Path() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index dad806418f2d..626f851d9d11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -192,6 +192,11 @@ public class NotificationChildrenContainer extends ViewGroup } @Override + public int getClipHeight() { + return Math.max(mActualHeight - mClipBottomAmount, 0); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 730ef57f1972..26b51a95acad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -37,6 +37,7 @@ import com.android.systemui.animation.DelegateLaunchAnimatorController import com.android.systemui.assist.AssistManager import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle @@ -72,6 +73,7 @@ constructor( private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, private val activityLaunchAnimator: ActivityLaunchAnimator, private val context: Context, + @DisplayId private val displayId: Int, private val lockScreenUserManager: NotificationLockscreenUserManager, private val statusBarWindowController: StatusBarWindowController, private val wakefulnessLifecycle: WakefulnessLifecycle, @@ -471,9 +473,7 @@ constructor( intent.getPackage() ) { adapter: RemoteAnimationAdapter? -> val options = - ActivityOptions( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter) - ) + ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter)) // We know that the intent of the caller is to dismiss the keyguard and // this runnable is called right after the keyguard is solved, so we tell @@ -596,7 +596,7 @@ constructor( val options = ActivityOptions( CentralSurfaces.getActivityOptions( - centralSurfaces!!.displayId, + displayId, animationAdapter ) ) @@ -762,7 +762,7 @@ constructor( TaskStackBuilder.create(context) .addNextIntent(intent) .startActivities( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter), + CentralSurfaces.getActivityOptions(displayId, adapter), userHandle ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 478baf2f58d6..2b9c3d33e9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -355,15 +355,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void updateNotificationPanelTouchState(); - int getDisplayId(); - int getRotation(); @VisibleForTesting void setBarStateForTest(int state); - void wakeUpForFullScreenIntent(); - void showTransientUnchecked(); void clearTransient(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 8902a186c43a..62e98f934888 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1613,7 +1613,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // (Right now, there's a circular dependency.) mNotificationShadeWindowController.setWindowRootView(windowRootView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); - mShadeController.setShadeViewController(mShadeSurface); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mBackActionInteractor.setup(mQsController, mShadeSurface); @@ -1773,7 +1772,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { try { EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, sbn.getKey()); - wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions opts = ActivityOptions.makeBasic(); opts.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); @@ -1786,16 +1785,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mHeadsUpManager.releaseAllImmediately(); } - @Override - public void wakeUpForFullScreenIntent() { - if (isGoingToSleep() || mDozing) { - mPowerManager.wakeUp( - SystemClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, - "com.android.systemui:full_screen_intent"); - } - } - /** * Called when another window is about to transfer it's input focus. */ @@ -2108,11 +2097,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override public void readyForKeyguardDone() { mStatusBarKeyguardViewManager.readyForKeyguardDone(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 39b5b5a4cef8..8e9f382d8c00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -35,11 +35,9 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; @@ -58,7 +56,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>(); private final int mIconSize; - private StatusBarWifiView mWifiView; private ModernStatusBarWifiView mModernWifiView; private boolean mDemoMode; private int mColor; @@ -238,36 +235,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da addView(v, 0, createLayoutParams()); } - public void addDemoWifiView(WifiIconState state) { - Log.d(TAG, "addDemoWifiView: "); - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot); - - int viewIndex = getChildCount(); - // If we have mobile views, put wifi before them - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof StatusBarMobileView - || child instanceof ModernStatusBarMobileView) { - viewIndex = i; - break; - } - } - - mWifiView = view; - mWifiView.applyWifiState(state); - mWifiView.setStaticDrawableColor(mColor); - addView(view, viewIndex, createLayoutParams()); - } - - public void updateWifiState(WifiIconState state) { - Log.d(TAG, "updateWifiState: "); - if (mWifiView == null) { - addDemoWifiView(state); - } else { - mWifiView.applyWifiState(state); - } - } - /** * Add a new mobile icon view */ @@ -352,10 +319,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onRemoveIcon(StatusIconDisplayable view) { if (view.getSlot().equals("wifi")) { - if (view instanceof StatusBarWifiView) { - removeView(mWifiView); - mWifiView = null; - } else if (view instanceof ModernStatusBarWifiView) { + if (view instanceof ModernStatusBarWifiView) { Log.d(TAG, "onRemoveIcon: removing modern wifi view"); removeView(mModernWifiView); mModernWifiView = null; @@ -409,9 +373,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { setColor(DarkIconDispatcher.getTint(areas, mStatusIcons, tint)); - if (mWifiView != null) { - mWifiView.onDarkChanged(areas, darkIntensity, tint); - } if (mModernWifiView != null) { mModernWifiView.onDarkChanged(areas, darkIntensity, tint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 0fde3ab3d19c..e18c9d86d74b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -33,14 +33,11 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider; -import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.wm.shell.bubbles.Bubbles; @@ -91,8 +88,6 @@ public class NotificationIconAreaController implements private final ArrayList<Rect> mTintAreas = new ArrayList<>(); private final Context mContext; - private final DemoModeController mDemoModeController; - private final FeatureFlags mFeatureFlags; private int mAodIconAppearTranslation; @@ -139,8 +134,7 @@ public class NotificationIconAreaController implements wakeUpCoordinator.addListener(this); mBypassController = keyguardBypassController; mBubblesOptional = bubblesOptional; - mDemoModeController = demoModeController; - mDemoModeController.addCallback(this); + demoModeController.addCallback(this); mStatusBarWindowController = statusBarWindowController; mScreenOffAnimationController = screenOffAnimationController; notificationListener.addNotificationSettingsListener(mSettingsListener); @@ -163,7 +157,6 @@ public class NotificationIconAreaController implements LayoutInflater layoutInflater = LayoutInflater.from(context); mNotificationIconArea = inflateIconArea(layoutInflater); mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons); - } /** @@ -185,16 +178,6 @@ public class NotificationIconAreaController implements updateIconLayoutParams(mContext); } - /** - * Update position of the view, with optional animation - */ - public void updatePosition(int x, AnimationProperties props, boolean animate) { - if (mAodIcons != null) { - PropertyAnimator.setProperty(mAodIcons, AnimatableProperty.TRANSLATION_X, x, props, - animate); - } - } - public void setupShelf(NotificationShelfController notificationShelfController) { NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); mShelfIcons = notificationShelfController.getShelfIcons(); @@ -303,6 +286,7 @@ public class NotificationIconAreaController implements } return true; } + /** * Updates the notifications with the given list of notifications to display. */ 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 418f9203761a..3770c1df77ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -54,19 +54,13 @@ import java.util.function.Consumer; * correctly on the screen. */ public class NotificationIconContainer extends ViewGroup { - /** - * A float value indicating how much before the overflow start the icons should transform into - * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts - * 1 icon width early. - */ - public static final float OVERFLOW_EARLY_AMOUNT = 0.2f; private static final int NO_VALUE = Integer.MIN_VALUE; private static final String TAG = "NotificationIconContainer"; private static final boolean DEBUG = false; private static final boolean DEBUG_OVERFLOW = false; private static final int CANNED_ANIMATION_DURATION = 100; private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); @Override public AnimationFilter getAnimationFilter() { @@ -75,7 +69,7 @@ public class NotificationIconContainer extends ViewGroup { }.setDuration(200); private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter() + private final AnimationFilter mAnimationFilter = new AnimationFilter() .animateX() .animateY() .animateAlpha() @@ -92,7 +86,7 @@ public class NotificationIconContainer extends ViewGroup { * Temporary AnimationProperties to avoid unnecessary allocations. */ private static final AnimationProperties sTempProperties = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter(); + private final AnimationFilter mAnimationFilter = new AnimationFilter(); @Override public AnimationFilter getAnimationFilter() { @@ -101,7 +95,7 @@ public class NotificationIconContainer extends ViewGroup { }; private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @Override public AnimationFilter getAnimationFilter() { @@ -115,7 +109,7 @@ public class NotificationIconContainer extends ViewGroup { */ private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @Override public AnimationFilter getAnimationFilter() { @@ -128,7 +122,7 @@ public class NotificationIconContainer extends ViewGroup { * This animates the translation back to the right position. */ private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); @Override public AnimationFilter getAnimationFilter() { @@ -147,7 +141,6 @@ public class NotificationIconContainer extends ViewGroup { private boolean mIsStaticLayout = true; private final HashMap<View, IconState> mIconStates = new HashMap<>(); private int mDotPadding; - private int mStaticDotRadius; private int mStaticDotDiameter; private int mActualLayoutWidth = NO_VALUE; private float mActualPaddingEnd = NO_VALUE; @@ -170,7 +163,7 @@ public class NotificationIconContainer extends ViewGroup { private boolean mIsShowingOverflowDot; private StatusBarIconView mIsolatedIcon; private Rect mIsolatedIconLocation; - private int[] mAbsolutePosition = new int[2]; + private final int[] mAbsolutePosition = new int[2]; private View mIsolatedIconForAnimation; private int mThemedTextColorPrimary; @@ -186,8 +179,8 @@ public class NotificationIconContainer extends ViewGroup { mMaxStaticIcons = getResources().getInteger(R.integer.max_notif_static_icons); mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding); - mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); - mStaticDotDiameter = 2 * mStaticDotRadius; + int staticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); + mStaticDotDiameter = 2 * staticDotRadius; final Context themedContext = new ContextThemeWrapper(getContext(), com.android.internal.R.style.Theme_DeviceDefault_DayNight); @@ -699,7 +692,6 @@ public class NotificationIconContainer extends ViewGroup { } public class IconState extends ViewState { - public static final int NO_VALUE = NotificationIconContainer.NO_VALUE; public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; public int visibleState; @@ -817,8 +809,6 @@ public class NotificationIconContainer extends ViewGroup { super.applyToView(view); } sTempProperties.setAnimationEndAction(null); - boolean inShelf = iconAppearAmount == 1.0f; - icon.setIsInShelf(inShelf); } justAdded = false; justReplaced = false; 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 59583ddf3e34..42b0a4f13e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; import android.annotation.Nullable; @@ -43,12 +42,10 @@ import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; @@ -97,16 +94,9 @@ public interface StatusBarIconController { */ void setIcon(String slot, int resourceId, CharSequence contentDescription); - /** */ - void setWifiIcon(String slot, WifiIconState state); - /** * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been * set up (inflated and added to the view hierarchy). - * - * This method completely replaces {@link #setWifiIcon} with the information from the new wifi - * data pipeline. Icons will automatically keep their state up to date, so we don't have to - * worry about funneling state objects through anymore. */ void setNewWifiIcon(); @@ -408,13 +398,7 @@ public interface StatusBarIconController { mMobileIconsViewModel = null; } - if (statusBarPipelineFlags.runNewWifiIconBackend()) { - // This starts the flow for the new pipeline, and will notify us of changes if - // {@link StatusBarPipelineFlags#useNewWifiIcon} is also true. - mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); - } else { - mWifiViewModel = null; - } + mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); } public boolean isDemoable() { @@ -462,9 +446,6 @@ public interface StatusBarIconController { case TYPE_ICON: return addIcon(index, slot, blocked, holder.getIcon()); - case TYPE_WIFI: - return addWifiIcon(index, slot, holder.getWifiState()); - case TYPE_WIFI_NEW: return addNewWifiIcon(index, slot); @@ -487,29 +468,7 @@ public interface StatusBarIconController { return view; } - @VisibleForTesting - protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon while the new " - + "icons are enabled is not supported"); - } - - final StatusBarWifiView view = onCreateStatusBarWifiView(slot); - view.applyWifiState(state); - mGroup.addView(view, index, onCreateLayoutParams()); - - if (mIsInDemoMode) { - mDemoStatusIcons.addDemoWifiView(state); - } - return view; - } - protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon using the new" - + "pipeline, but the enabled flag is false."); - } - ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); mGroup.addView(view, index, onCreateLayoutParams()); @@ -573,11 +532,6 @@ public interface StatusBarIconController { return new StatusBarIconView(mContext, slot, null, blocked); } - private StatusBarWifiView onCreateStatusBarWifiView(String slot) { - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); - return view; - } - private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) { return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel); } @@ -640,9 +594,6 @@ public interface StatusBarIconController { case TYPE_ICON: onSetIcon(viewIndex, holder.getIcon()); return; - case TYPE_WIFI: - onSetWifiIcon(viewIndex, holder.getWifiState()); - return; case TYPE_MOBILE: onSetMobileIcon(viewIndex, holder.getMobileState()); return; @@ -655,23 +606,6 @@ public interface StatusBarIconController { } } - public void onSetWifiIcon(int viewIndex, WifiIconState state) { - View view = mGroup.getChildAt(viewIndex); - if (view instanceof StatusBarWifiView) { - ((StatusBarWifiView) view).applyWifiState(state); - } else if (view instanceof ModernStatusBarWifiView) { - // ModernStatusBarWifiView will automatically apply state based on its callbacks, so - // we don't need to call applyWifiState. - } else { - throw new IllegalStateException("View at " + viewIndex + " must be of type " - + "StatusBarWifiView or ModernStatusBarWifiView"); - } - - if (mIsInDemoMode) { - mDemoStatusIcons.updateWifiState(state); - } - } - public void onSetMobileIcon(int viewIndex, MobileIconState state) { View view = mGroup.getChildAt(viewIndex); if (view instanceof StatusBarMobileView) { @@ -703,9 +637,7 @@ public interface StatusBarIconController { mIsInDemoMode = true; if (mDemoStatusIcons == null) { mDemoStatusIcons = createDemoStatusIcons(); - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - mDemoStatusIcons.addModernWifiView(mWifiViewModel); - } + mDemoStatusIcons.addModernWifiView(mWifiViewModel); } mDemoStatusIcons.onDemoModeStarted(); } 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..d1a02d6cd611 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -40,7 +40,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -202,35 +201,7 @@ public class StatusBarIconControllerImpl implements Tunable, } @Override - public void setWifiIcon(String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring old pipeline callback because the new wifi icon is enabled"); - return; - } - - if (state == null) { - removeIcon(slot, 0); - return; - } - - StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); - if (holder == null) { - holder = StatusBarIconHolder.fromWifiIconState(state); - setIcon(slot, holder); - } else { - holder.setWifiState(state); - handleSet(slot, holder); - } - } - - - @Override public void setNewWifiIcon() { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring new pipeline callback because the new wifi icon is disabled"); - return; - } - String slot = mContext.getString(com.android.internal.R.string.status_bar_wifi); StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, /* tag= */ 0); if (holder == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java index 833cb93f62e9..01fd247f54bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -25,7 +25,6 @@ import android.os.UserHandle; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel; import java.lang.annotation.Retention; @@ -36,7 +35,6 @@ import java.lang.annotation.RetentionPolicy; */ public class StatusBarIconHolder { public static final int TYPE_ICON = 0; - public static final int TYPE_WIFI = 1; public static final int TYPE_MOBILE = 2; /** * TODO (b/249790733): address this once the new pipeline is in place @@ -65,7 +63,6 @@ public class StatusBarIconHolder { @IntDef({ TYPE_ICON, - TYPE_WIFI, TYPE_MOBILE, TYPE_MOBILE_NEW, TYPE_WIFI_NEW @@ -74,7 +71,6 @@ public class StatusBarIconHolder { @interface IconType {} private StatusBarIcon mIcon; - private WifiIconState mWifiState; private MobileIconState mMobileState; private @IconType int mType = TYPE_ICON; private int mTag = 0; @@ -83,7 +79,6 @@ public class StatusBarIconHolder { public static String getTypeString(@IconType int type) { switch(type) { case TYPE_ICON: return "ICON"; - case TYPE_WIFI: return "WIFI_OLD"; case TYPE_MOBILE: return "MOBILE_OLD"; case TYPE_MOBILE_NEW: return "MOBILE_NEW"; case TYPE_WIFI_NEW: return "WIFI_NEW"; @@ -101,25 +96,6 @@ public class StatusBarIconHolder { return wrapper; } - /** */ - public static StatusBarIconHolder fromResId( - Context context, - int resId, - CharSequence contentDescription) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), - Icon.createWithResource( context, resId), 0, 0, contentDescription); - return holder; - } - - /** */ - public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mWifiState = state; - holder.mType = TYPE_WIFI; - return holder; - } - /** Creates a new holder with for the new wifi icon. */ public static StatusBarIconHolder forNewWifiIcon() { StatusBarIconHolder holder = new StatusBarIconHolder(); @@ -178,15 +154,6 @@ public class StatusBarIconHolder { } @Nullable - public WifiIconState getWifiState() { - return mWifiState; - } - - public void setWifiState(WifiIconState state) { - mWifiState = state; - } - - @Nullable public MobileIconState getMobileState() { return mMobileState; } @@ -199,8 +166,6 @@ public class StatusBarIconHolder { switch (mType) { case TYPE_ICON: return mIcon.visible; - case TYPE_WIFI: - return mWifiState.visible; case TYPE_MOBILE: return mMobileState.visible; case TYPE_MOBILE_NEW: @@ -223,10 +188,6 @@ public class StatusBarIconHolder { mIcon.visible = visible; break; - case TYPE_WIFI: - mWifiState.visible = visible; - break; - case TYPE_MOBILE: mMobileState.visible = visible; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index cb2a78d8be35..5c1dfbe42cf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -51,6 +51,7 @@ import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; +import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -306,6 +307,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Nullable private TaskbarDelegate mTaskbarDelegate; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + @NonNull TrustGrantFlags flags, + @Nullable String message + ) { + updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); + } + @Override public void onEmergencyCallAction() { // Since we won't get a setOccluded call we have to reset the view manually such that @@ -431,7 +442,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mDockManager.addListener(mDockEventListener); mIsDocked = mDockManager.isDocked(); } - mKeyguardStateController.addCallback(mKeyguardStateControllerCallback); } /** Register a callback, to be invoked by the Predictive Back system. */ @@ -1570,14 +1580,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb || mode == KeyguardSecurityModel.SecurityMode.SimPuk; } - private KeyguardStateController.Callback mKeyguardStateControllerCallback = - new KeyguardStateController.Callback() { - @Override - public void onUnlockedChanged() { - updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); - } - }; - /** * Delegate used to send show and hide events to an alternate authentication method instead of * the regular pin/pattern/password bouncer. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index f79a08173bf2..ec0c00e26c2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -54,8 +54,10 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeViewController; @@ -94,6 +96,7 @@ import javax.inject.Inject; class StatusBarNotificationActivityStarter implements NotificationActivityStarter { private final Context mContext; + private final int mDisplayId; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; @@ -120,12 +123,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; - private final CentralSurfaces mCentralSurfaces; private final NotificationPresenter mPresenter; private final ShadeViewController mShadeViewController; private final NotificationShadeWindowController mNotificationShadeWindowController; private final ActivityLaunchAnimator mActivityLaunchAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; + private final PowerInteractor mPowerInteractor; private final UserTracker mUserTracker; private final OnUserInteractionCallback mOnUserInteractionCallback; @@ -134,6 +137,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Inject StatusBarNotificationActivityStarter( Context context, + @DisplayId int displayId, Handler mainThreadHandler, Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, @@ -156,16 +160,17 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, - CentralSurfaces centralSurfaces, NotificationPresenter presenter, ShadeViewController shadeViewController, NotificationShadeWindowController notificationShadeWindowController, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, + PowerInteractor powerInteractor, FeatureFlags featureFlags, UserTracker userTracker) { mContext = context; + mDisplayId = displayId; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; mVisibilityProvider = visibilityProvider; @@ -190,12 +195,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; - // TODO: use KeyguardStateController#isOccluded to remove this dependency - mCentralSurfaces = centralSurfaces; mPresenter = presenter; mShadeViewController = shadeViewController; mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; + mPowerInteractor = powerInteractor; mUserTracker = userTracker; launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry)); @@ -280,7 +284,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mShadeController.addPostCollapseAction(runnable); mShadeController.collapseShade(true /* animate */); } else if (mKeyguardStateController.isShowing() - && mCentralSurfaces.isOccluded()) { + && mKeyguardStateController.isOccluded()) { mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapseShade(); } else { @@ -452,11 +456,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte long eventTime = row.getAndResetLastActionUpTime(); Bundle options = eventTime > 0 ? getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter, mKeyguardStateController.isShowing(), eventTime) - : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter); + : getActivityOptions(mDisplayId, adapter); int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, null, null, options); mLogger.logSendPendingIntent(entry, intent, result); @@ -491,7 +495,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte (adapter) -> TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter), new UserHandle(UserHandle.getUserId(appUid)))); }); @@ -539,7 +543,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, intent.getPackage(), (adapter) -> tsb.startActivities( - getActivityOptions(mCentralSurfaces.getDisplayId(), adapter), + getActivityOptions(mDisplayId, adapter), mUserTracker.getUserHandle())); }); return true; @@ -592,7 +596,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, entry.getKey()); - mCentralSurfaces.wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions options = ActivityOptions.makeBasic(); options.setPendingIntentBackgroundActivityStartMode( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d731f8886536..69199966dea6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -30,7 +30,6 @@ import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; -import com.android.systemui.statusbar.connectivity.WifiIndicators; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -51,7 +50,6 @@ public class StatusBarSignalPolicy implements SignalCallback, private final String mSlotAirplane; private final String mSlotMobile; - private final String mSlotWifi; private final String mSlotEthernet; private final String mSlotVpn; private final String mSlotNoCalling; @@ -67,17 +65,14 @@ public class StatusBarSignalPolicy implements SignalCallback, private boolean mHideAirplane; private boolean mHideMobile; - private boolean mHideWifi; private boolean mHideEthernet; private boolean mActivityEnabled; // Track as little state as possible, and only for padding purposes private boolean mIsAirplaneMode = false; - private boolean mIsWifiEnabled = false; private ArrayList<MobileIconState> mMobileStates = new ArrayList<>(); private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); - private WifiIconState mWifiIconState = new WifiIconState(); private boolean mInitialized; @Inject @@ -99,7 +94,6 @@ public class StatusBarSignalPolicy implements SignalCallback, mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); - mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); @@ -154,15 +148,13 @@ public class StatusBarSignalPolicy implements SignalCallback, ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); boolean hideAirplane = hideList.contains(mSlotAirplane); boolean hideMobile = hideList.contains(mSlotMobile); - boolean hideWifi = hideList.contains(mSlotWifi); boolean hideEthernet = hideList.contains(mSlotEthernet); if (hideAirplane != mHideAirplane || hideMobile != mHideMobile - || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { + || hideEthernet != mHideEthernet) { mHideAirplane = hideAirplane; mHideMobile = hideMobile; mHideEthernet = hideEthernet; - mHideWifi = hideWifi; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -170,56 +162,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } @Override - public void setWifiIndicators(@NonNull WifiIndicators indicators) { - if (DEBUG) { - Log.d(TAG, "setWifiIndicators: " + indicators); - } - boolean visible = indicators.statusIcon.visible && !mHideWifi; - boolean in = indicators.activityIn && mActivityEnabled && visible; - boolean out = indicators.activityOut && mActivityEnabled && visible; - mIsWifiEnabled = indicators.enabled; - - WifiIconState newState = mWifiIconState.copy(); - - if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable - && !mIsAirplaneMode) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable - && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_available; - } else { - newState.visible = visible; - newState.resId = indicators.statusIcon.icon; - newState.activityIn = in; - newState.activityOut = out; - newState.contentDescription = indicators.statusIcon.contentDescription; - MobileIconState first = getFirstMobileState(); - newState.signalSpacerVisible = first != null && first.typeId != 0; - } - newState.slot = mSlotWifi; - newState.airplaneSpacerVisible = mIsAirplaneMode; - updateWifiIconWithState(newState); - mWifiIconState = newState; - } - - private void updateShowWifiSignalSpacer(WifiIconState state) { - MobileIconState first = getFirstMobileState(); - state.signalSpacerVisible = first != null && first.typeId != 0; - } - - private void updateWifiIconWithState(WifiIconState state) { - if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString()); - if (state.visible && state.resId > 0) { - mIconController.setWifiIcon(mSlotWifi, state); - mIconController.setIconVisibility(mSlotWifi, true); - } else { - mIconController.setIconVisibility(mSlotWifi, false); - } - } - - @Override public void setCallIndicator(@NonNull IconState statusIcon, int subId) { if (DEBUG) { Log.d(TAG, "setCallIndicator: " @@ -257,10 +199,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return; } - // Visibility of the data type indicator changed - boolean typeChanged = indicators.statusType != state.typeId - && (indicators.statusType == 0 || state.typeId == 0); - state.visible = indicators.statusIcon.visible && !mHideMobile; state.strengthId = indicators.statusIcon.icon; state.typeId = indicators.statusType; @@ -277,15 +215,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } // Always send a copy to maintain value type semantics mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); - - if (typeChanged) { - WifiIconState wifiCopy = mWifiIconState.copy(); - updateShowWifiSignalSpacer(wifiCopy); - if (!Objects.equals(wifiCopy, mWifiIconState)) { - updateWifiIconWithState(wifiCopy); - mWifiIconState = wifiCopy; - } - } } private CallIndicatorIconState getNoCallingState(int subId) { @@ -308,15 +237,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return null; } - private MobileIconState getFirstMobileState() { - if (mMobileStates.size() > 0) { - return mMobileStates.get(0); - } - - return null; - } - - /** * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators * so we don't have to update the icon manager at this point, just remove the old ones @@ -504,60 +424,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } } - public static class WifiIconState extends SignalIconState{ - public int resId; - public boolean airplaneSpacerVisible; - public boolean signalSpacerVisible; - public boolean noDefaultNetwork; - public boolean noValidatedNetwork; - public boolean noNetworksAvailable; - - @Override - public boolean equals(Object o) { - // Skipping reference equality bc this should be more of a value type - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - WifiIconState that = (WifiIconState) o; - return resId == that.resId - && airplaneSpacerVisible == that.airplaneSpacerVisible - && signalSpacerVisible == that.signalSpacerVisible - && noDefaultNetwork == that.noDefaultNetwork - && noValidatedNetwork == that.noValidatedNetwork - && noNetworksAvailable == that.noNetworksAvailable; - } - - public void copyTo(WifiIconState other) { - super.copyTo(other); - other.resId = resId; - other.airplaneSpacerVisible = airplaneSpacerVisible; - other.signalSpacerVisible = signalSpacerVisible; - other.noDefaultNetwork = noDefaultNetwork; - other.noValidatedNetwork = noValidatedNetwork; - other.noNetworksAvailable = noNetworksAvailable; - } - - public WifiIconState copy() { - WifiIconState newState = new WifiIconState(); - copyTo(newState); - return newState; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), - resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork, - noValidatedNetwork, noNetworksAvailable); - } - - @Override public String toString() { - return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; - } - } - /** * A little different. This one delegates to SignalDrawable instead of a specific resId */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index eb4d963e8525..cd6ccb5b9a8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -27,6 +27,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.SystemBarAttributesListener; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; @@ -73,6 +74,13 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.HOME; + } + + /** */ + @Provides + @StatusBarFragmentScope @Named(START_SIDE_CONTENT) static View startSideContent(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.status_bar_start_side_content); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt index 4a684d9f8e36..29829e46cda7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt @@ -45,18 +45,6 @@ constructor( fun runNewMobileIconsBackend(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS_BACKEND) || useNewMobileIcons() - /** True if we should display the wifi icon using the new status bar data pipeline. */ - fun useNewWifiIcon(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON) - - /** - * True if we should run the new wifi icon backend to get the logging. - * - * Does *not* affect whether we render the wifi icon using the new backend data. See - * [useNewWifiIcon] for that. - */ - fun runNewWifiIconBackend(): Boolean = - featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON_BACKEND) || useNewWifiIcon() - /** * Returns true if we should apply some coloring to the icons that were rendered with the new * pipeline to help with debugging. @@ -71,5 +59,5 @@ constructor( * @return true if this icon is controlled by any of the status bar pipeline flags */ fun isIconControlledByFlags(slotName: String): Boolean = - slotName == wifiSlot && useNewWifiIcon() || slotName == mobileSlot && useNewMobileIcons() + slotName == wifiSlot || (slotName == mobileSlot && useNewMobileIcons()) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt index 83738544ee84..a1b96dd327e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt @@ -101,12 +101,7 @@ open class ModernStatusBarView(context: Context, attrs: AttributeSet?) : this.binding = bindingCreator.invoke() } - /** - * Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. - * - * Mostly duplicated from [com.android.systemui.statusbar.StatusBarWifiView] and - * [com.android.systemui.statusbar.StatusBarMobileView]. - */ + /** Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. */ private fun initDotView() { // TODO(b/238425913): Could we just have this dot view be part of the layout with a dot // drawable so we don't need to inflate it manually? Would that not work with animations? diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt index 4e52be91f0af..7f35dfbe2700 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt @@ -34,6 +34,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable @@ -48,6 +49,7 @@ import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import java.util.concurrent.Executor import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose @@ -60,7 +62,9 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext /** Real implementation of [WifiRepository]. */ @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @@ -76,8 +80,9 @@ constructor( logger: WifiInputLogger, @WifiTableLog wifiTableLogBuffer: TableLogBuffer, @Main mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, @Application scope: CoroutineScope, - wifiManager: WifiManager, + private val wifiManager: WifiManager, ) : RealWifiRepository { private val wifiStateChangeEvents: Flow<Unit> = @@ -93,20 +98,25 @@ constructor( // have changed. override val isWifiEnabled: StateFlow<Boolean> = merge(wifiNetworkChangeEvents, wifiStateChangeEvents) - .mapLatest { wifiManager.isWifiEnabled } + .onStart { emit(Unit) } + .mapLatest { isWifiEnabled() } .distinctUntilChanged() .logDiffsForTable( wifiTableLogBuffer, columnPrefix = "", columnName = "isEnabled", - initialValue = wifiManager.isWifiEnabled, + initialValue = false, ) .stateIn( scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = wifiManager.isWifiEnabled, + started = SharingStarted.Eagerly, + initialValue = false, ) + // [WifiManager.isWifiEnabled] is a blocking IPC call, so fetch it in the background. + private suspend fun isWifiEnabled(): Boolean = + withContext(bgDispatcher) { wifiManager.isWifiEnabled } + override val isWifiDefault: StateFlow<Boolean> = connectivityRepository.defaultConnections // TODO(b/274493701): Should wifi be considered default if it's carrier merged? @@ -289,6 +299,7 @@ constructor( private val logger: WifiInputLogger, @WifiTableLog private val wifiTableLogBuffer: TableLogBuffer, @Main private val mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, ) { fun create(wifiManager: WifiManager): WifiRepositoryImpl { @@ -299,6 +310,7 @@ constructor( logger, wifiTableLogBuffer, mainExecutor, + bgDispatcher, scope, wifiManager, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt index 174298ab6490..6d7182376fab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt @@ -68,12 +68,7 @@ constructor( launch { locationViewModel.wifiIcon.collect { wifiIcon -> // Only notify the icon controller if we want to *render* the new icon. - // Note that this flow may still run if - // [statusBarPipelineFlags.runNewWifiIconBackend] is true because we may - // want to get the logging data without rendering. - if ( - wifiIcon is WifiIcon.Visible && statusBarPipelineFlags.useNewWifiIcon() - ) { + if (wifiIcon is WifiIcon.Visible) { iconController.setNewWifiIcon() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 3d165912a09c..7df083afcd19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; import android.view.View; +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -136,7 +139,7 @@ public interface BatteryController extends DemoMode, * A listener that will be notified whenever a change in battery level or power save mode has * occurred. */ - interface BatteryStateChangeCallback { + interface BatteryStateChangeCallback extends Dumpable { default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { } @@ -158,6 +161,11 @@ public interface BatteryController extends DemoMode, default void onIsBatteryDefenderChanged(boolean isBatteryDefender) { } + + @Override + default void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.println(this); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index e69d86c31abc..d5d8f4d7598e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -22,6 +22,7 @@ import static android.os.BatteryManager.EXTRA_CHARGING_STATUS; import static android.os.BatteryManager.EXTRA_PRESENT; import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_QS; +import static com.android.systemui.util.DumpUtilsKt.asIndenting; import android.annotation.WorkerThread; import android.content.BroadcastReceiver; @@ -33,6 +34,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.PowerSaveState; +import android.util.IndentingPrintWriter; import android.util.Log; import android.view.View; @@ -157,15 +159,29 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void dump(PrintWriter pw, String[] args) { - pw.println("BatteryController state:"); - pw.print(" mLevel="); pw.println(mLevel); - pw.print(" mPluggedIn="); pw.println(mPluggedIn); - pw.print(" mCharging="); pw.println(mCharging); - pw.print(" mCharged="); pw.println(mCharged); - pw.print(" mIsBatteryDefender="); pw.println(mIsBatteryDefender); - pw.print(" mPowerSave="); pw.println(mPowerSave); - pw.print(" mStateUnknown="); pw.println(mStateUnknown); + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + IndentingPrintWriter ipw = asIndenting(pw); + ipw.println("BatteryController state:"); + ipw.increaseIndent(); + ipw.print("mHasReceivedBattery="); ipw.println(mHasReceivedBattery); + ipw.print("mLevel="); ipw.println(mLevel); + ipw.print("mPluggedIn="); ipw.println(mPluggedIn); + ipw.print("mCharging="); ipw.println(mCharging); + ipw.print("mCharged="); ipw.println(mCharged); + ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender); + ipw.print("mPowerSave="); ipw.println(mPowerSave); + ipw.print("mStateUnknown="); ipw.println(mStateUnknown); + ipw.println("Callbacks:------------------"); + // Since the above lines are already indented, we need to indent twice for the callbacks. + ipw.increaseIndent(); + synchronized (mChangeCallbacks) { + final int n = mChangeCallbacks.size(); + for (int i = 0; i < n; i++) { + mChangeCallbacks.get(i).dump(ipw, args); + } + } + ipw.decreaseIndent(); + ipw.println("------------------"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index 3e1c13c1cba8..c1ac800b8159 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -64,9 +64,9 @@ constructor( // These values must only be accessed on the handler. private var batteryCapacity = 1.0f private var suppressed = false - private var inputDeviceId: Int? = null private var instanceId: InstanceId? = null - + @VisibleForTesting var inputDeviceId: Int? = null + private set @VisibleForTesting var instanceIdSequence = InstanceIdSequence(1 shl 13) fun init() { @@ -110,10 +110,10 @@ constructor( fun updateBatteryState(deviceId: Int, batteryState: BatteryState) { handler.post updateBattery@{ + inputDeviceId = deviceId if (batteryState.capacity == batteryCapacity || batteryState.capacity <= 0f) return@updateBattery - inputDeviceId = deviceId batteryCapacity = batteryState.capacity debugLog { "Updating notification battery state to $batteryCapacity " + diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java deleted file mode 100644 index b54d1566eba3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2019 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.tracing; - -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_H; -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_L; - -import android.content.Context; -import android.os.SystemClock; - -import androidx.annotation.NonNull; - -import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.shared.tracing.FrameProtoTracer; -import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams; -import com.android.systemui.shared.tracing.ProtoTraceable; -import com.android.systemui.tracing.nano.SystemUiTraceEntryProto; -import com.android.systemui.tracing.nano.SystemUiTraceFileProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; - -import com.google.protobuf.nano.MessageNano; - -import java.io.File; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Queue; - -import javax.inject.Inject; - -/** - * Controller for coordinating winscope proto tracing. - */ -@SysUISingleton -public class ProtoTracer implements - Dumpable, - ProtoTraceParams< - MessageNano, - SystemUiTraceFileProto, - SystemUiTraceEntryProto, - SystemUiTraceProto> { - - private static final String TAG = "ProtoTracer"; - private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - - private final Context mContext; - private final FrameProtoTracer<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto, - SystemUiTraceProto> mProtoTracer; - - @Inject - public ProtoTracer(Context context, DumpManager dumpManager) { - mContext = context; - mProtoTracer = new FrameProtoTracer<>(this); - dumpManager.registerDumpable(this); - } - - @Override - public File getTraceFile() { - return new File(mContext.getFilesDir(), "sysui_trace.pb"); - } - - @Override - public SystemUiTraceFileProto getEncapsulatingTraceProto() { - return new SystemUiTraceFileProto(); - } - - @Override - public SystemUiTraceEntryProto updateBufferProto(SystemUiTraceEntryProto reuseObj, - ArrayList<ProtoTraceable<SystemUiTraceProto>> traceables) { - SystemUiTraceEntryProto proto = reuseObj != null - ? reuseObj - : new SystemUiTraceEntryProto(); - proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); - proto.systemUi = proto.systemUi != null ? proto.systemUi : new SystemUiTraceProto(); - for (ProtoTraceable t : traceables) { - t.writeToProto(proto.systemUi); - } - return proto; - } - - @Override - public byte[] serializeEncapsulatingProto(SystemUiTraceFileProto encapsulatingProto, - Queue<SystemUiTraceEntryProto> buffer) { - encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE; - encapsulatingProto.entry = buffer.toArray(new SystemUiTraceEntryProto[0]); - return MessageNano.toByteArray(encapsulatingProto); - } - - @Override - public byte[] getProtoBytes(MessageNano proto) { - return MessageNano.toByteArray(proto); - } - - @Override - public int getProtoSize(MessageNano proto) { - return proto.getCachedSize(); - } - - public void start() { - mProtoTracer.start(); - } - - public void stop() { - mProtoTracer.stop(); - } - - public boolean isEnabled() { - return mProtoTracer.isEnabled(); - } - - public void add(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.add(traceable); - } - - public void remove(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.remove(traceable); - } - - public void scheduleFrameUpdate() { - mProtoTracer.scheduleFrameUpdate(); - } - - public void update() { - mProtoTracer.update(); - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("ProtoTracer:"); - pw.print(" "); pw.println("enabled: " + mProtoTracer.isEnabled()); - pw.print(" "); pw.println("usagePct: " + mProtoTracer.getBufferUsagePct()); - pw.print(" "); pw.println("file: " + getTraceFile()); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto deleted file mode 100644 index d940a6b5c460..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package com.android.systemui.tracing; - -option java_multiple_files = true; - -message SystemUiTraceProto { - - optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1; -} - -message EdgeBackGestureHandlerProto { - - optional bool allow_gesture = 1; -} - -/* represents a file full of system ui trace entries. - Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such - that they can be easily identified. */ -message SystemUiTraceFileProto { - - /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L - (this is needed because enums have to be 32 bits and there's no nice way to put 64bit - constants into .proto files. */ - enum MagicNumber { - INVALID = 0; - MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */ - MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */ - } - - optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ - repeated SystemUiTraceEntryProto entry = 2; -} - -/* one system ui trace entry. */ -message SystemUiTraceEntryProto { - /* required: elapsed realtime in nanos since boot of when this entry was logged */ - optional fixed64 elapsed_realtime_nanos = 1; - - optional SystemUiTraceProto system_ui = 3; -} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index d9a8e0cfb53a..38226ec45910 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -45,7 +45,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeControllerImpl; +import com.android.systemui.shade.ShadeControllerEmptyImpl; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationListener; @@ -138,7 +138,7 @@ public abstract class TvSystemUIModule { abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds - abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); + abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController); @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt new file mode 100644 index 000000000000..a804923bafdf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt @@ -0,0 +1,39 @@ +/* + * 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.util.wrapper + +import android.content.Context +import android.util.AttributeSet +import com.airbnb.lottie.LottieAnimationView +import com.android.systemui.util.traceSection + +/** LottieAnimationView that traces each call to invalidate. */ +open class LottieViewWrapper : LottieAnimationView { + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor( + context: Context?, + attrs: AttributeSet?, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) + + override fun invalidate() { + traceSection<Any?>("${this::class} invalidate") { + super.invalidate() + null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 93622200ad46..349f3684659c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -168,6 +168,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, /** Volume dialog slider animation. */ private static final String TYPE_UPDATE = "update"; + /** + * TODO(b/290612381): remove lingering animations or tolerate them + * When false, this will cause this class to not listen to animator events and not record jank + * events. This should never be false in production code, and only is false for unit tests for + * this class. This flag should be true in Scenario/Integration tests. + */ + private final boolean mShouldListenForJank; private final int mDialogShowAnimationDurationMs; private final int mDialogHideAnimationDurationMs; private int mDialogWidth; @@ -304,6 +311,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, InteractionJankMonitor interactionJankMonitor, + boolean shouldListenForJank, CsdWarningDialog.Factory csdWarningDialogFactory, DevicePostureController devicePostureController, Looper looper, @@ -311,6 +319,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); mHandler = new H(looper); + + mShouldListenForJank = shouldListenForJank; mController = volumeDialogController; mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -1368,7 +1378,10 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } private Animator.AnimatorListener getJankListener(View v, String type, long timeout) { - return new Animator.AnimatorListener() { + if (!mShouldListenForJank) { + // TODO(b/290612381): temporary fix to prevent null pointers on leftover JankMonitors + return null; + } else return new Animator.AnimatorListener() { @Override public void onAnimationStart(@NonNull Animator animation) { if (!v.isAttachedToWindow()) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index aa4ee545a500..d0edc6e7ce4c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -72,6 +72,7 @@ public interface VolumeModule { volumePanelFactory, activityStarter, interactionJankMonitor, + true, /* should listen for jank */ csdFactory, devicePostureController, Looper.getMainLooper(), diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 5144d1966222..943e906a1ebc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -51,15 +51,11 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.notetask.NoteTaskInitializer; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; -import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; @@ -94,8 +90,7 @@ import javax.inject.Inject; @SysUISingleton public final class WMShell implements CoreStartable, - CommandQueue.Callbacks, - ProtoTraceable<SystemUiTraceProto> { + CommandQueue.Callbacks { private static final String TAG = WMShell.class.getName(); private static final int INVALID_SYSUI_STATE_MASK = SYSUI_STATE_DIALOG_SHOWING @@ -122,7 +117,6 @@ public final class WMShell implements private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; private final WakefulnessLifecycle mWakefulnessLifecycle; - private final ProtoTracer mProtoTracer; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; private final NoteTaskInitializer mNoteTaskInitializer; @@ -184,7 +178,6 @@ public final class WMShell implements KeyguardUpdateMonitor keyguardUpdateMonitor, ScreenLifecycle screenLifecycle, SysUiState sysUiState, - ProtoTracer protoTracer, WakefulnessLifecycle wakefulnessLifecycle, UserTracker userTracker, DisplayTracker displayTracker, @@ -203,7 +196,6 @@ public final class WMShell implements mOneHandedOptional = oneHandedOptional; mDesktopModeOptional = desktopMode; mWakefulnessLifecycle = wakefulnessLifecycle; - mProtoTracer = protoTracer; mUserTracker = userTracker; mDisplayTracker = displayTracker; mNoteTaskInitializer = noteTaskInitializer; @@ -223,7 +215,6 @@ public final class WMShell implements // Subscribe to user changes mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); - mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); @@ -361,12 +352,6 @@ public final class WMShell implements } @Override - public void writeToProto(SystemUiTraceProto proto) { - // Dump to WMShell proto here - // TODO: Figure out how we want to synchronize while dumping to proto - } - - @Override public void dump(PrintWriter pw, String[] args) { // Handle commands if provided if (mShell.handleCommand(args, pw)) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index fa32835c2695..677d3ff3df69 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -187,9 +187,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { @Test public void testLockedOut_verifyPasswordAndUnlock_doesNotEnableViewInput() { - mKeyguardAbsKeyInputViewController.handleAttemptLockout( - SystemClock.elapsedRealtime() + 1000); - mKeyguardAbsKeyInputViewController.verifyPasswordAndUnlock(); + mKeyguardAbsKeyInputViewController.handleAttemptLockout(SystemClock.elapsedRealtime()); verify(mAbsKeyInputView).setPasswordEntryInputEnabled(false); verify(mAbsKeyInputView).setPasswordEntryEnabled(false); verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 9db267c2c929..d256ee163877 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -105,6 +105,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton) // For posture tests: `when`(keyguardPinView.buttons).thenReturn(arrayOf()) + `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) pinViewController = KeyguardPinViewController( @@ -167,7 +168,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) `when`(passwordTextView.text).thenReturn("") @@ -182,7 +182,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) `when`(passwordTextView.text).thenReturn("") diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 1482f291b57a..40b572934f74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java @@ -33,10 +33,10 @@ import android.provider.Settings; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -61,7 +61,6 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; - private FakeFeatureFlags mFeatureFlags; @Mock private BatteryController mBatteryController; @@ -74,8 +73,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { when(mBatteryMeterView.getContext()).thenReturn(mContext); when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources()); - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); } @Test @@ -134,7 +133,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagDisabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); initController(); @@ -143,7 +143,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagEnabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, true); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, true); initController(); @@ -153,12 +154,12 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private void initController() { mController = new BatteryMeterViewController( mBatteryMeterView, + StatusBarLocation.HOME, mUserTracker, mConfigurationController, mTunerService, mHandler, mContentResolver, - mFeatureFlags, mBatteryController ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt index fff1b81db628..278a43ea1bf1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 87c9e583af4d..91140a9b0fc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -27,11 +27,12 @@ import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality import com.android.systemui.biometrics.extractAuthenticatorTypes import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat @@ -131,20 +132,22 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun plays_haptic_on_authenticated() = runGenericTest { - viewModel.showAuthenticated(testCase.authenticatedModality, 1000L) + fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = + runGenericTest { + val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - verify(vibrator).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) - } + viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - @Test - fun plays_no_haptic_on_confirm() = runGenericTest { - viewModel.confirmAuthenticated() + verify(vibrator, if (expectConfirmation) never() else times(1)) + .vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) - } + if (expectConfirmation) { + viewModel.confirmAuthenticated() + } + + verify(vibrator).vibrateAuthSuccess(any()) + verify(vibrator, never()).vibrateAuthError(any()) + } private suspend fun TestScope.showAuthenticated( authenticatedModality: BiometricModality, @@ -204,7 +207,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test fun plays_haptic_on_errors() = runGenericTest { - viewModel.showTemporaryError("so sad", hapticFeedback = true) + viewModel.showTemporaryError( + "so sad", + messageAfterError = "", + authenticateAfterError = false, + hapticFeedback = true, + ) verify(vibrator).vibrateAuthError(any()) verify(vibrator, never()).vibrateAuthSuccess(any()) @@ -212,7 +220,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test fun plays_haptic_on_errors_unless_skipped() = runGenericTest { - viewModel.showTemporaryError("still sad", hapticFeedback = false) + viewModel.showTemporaryError( + "still sad", + messageAfterError = "", + authenticateAfterError = false, + hapticFeedback = false, + ) verify(vibrator, never()).vibrateAuthError(any()) verify(vibrator, never()).vibrateAuthSuccess(any()) @@ -287,7 +300,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(canTryAgain).isFalse() } - val errorJob = launch { viewModel.showTemporaryError("error") } + val errorJob = launch { + viewModel.showTemporaryError( + "error", + messageAfterError = "", + authenticateAfterError = false, + ) + } verifyNoError() errorJob.join() verifyNoError() @@ -306,12 +325,66 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(messageIsShowing).isTrue() } - // @Test - fun `suppress errors`() = runGenericTest { - val errorMessage = "woot" - val message by collectLastValue(viewModel.message) + @Test + fun suppress_temporary_error() = runGenericTest { + val messages by collectValues(viewModel.message) + + for (error in listOf("never", "see", "me")) { + launch { + viewModel.showTemporaryError( + error, + messageAfterError = "or me", + authenticateAfterError = false, + suppressIf = { _ -> true }, + ) + } + } + + testScheduler.advanceUntilIdle() + assertThat(messages).containsExactly(PromptMessage.Empty) + } - val errorJob = launch { viewModel.showTemporaryError(errorMessage) } + @Test + fun suppress_temporary_error_when_already_showing_when_requested() = + suppress_temporary_error_when_already_showing(suppress = true) + + @Test + fun do_not_suppress_temporary_error_when_already_showing_when_not_requested() = + suppress_temporary_error_when_already_showing(suppress = false) + + private fun suppress_temporary_error_when_already_showing(suppress: Boolean) = runGenericTest { + val errors = listOf("woot", "oh yeah", "nope") + val afterSuffix = "(after)" + val expectedErrorMessage = if (suppress) errors.first() else errors.last() + val messages by collectValues(viewModel.message) + + for (error in errors) { + launch { + viewModel.showTemporaryError( + error, + messageAfterError = "$error $afterSuffix", + authenticateAfterError = false, + suppressIf = { currentMessage -> suppress && currentMessage.isError }, + ) + } + } + + testScheduler.runCurrent() + assertThat(messages) + .containsExactly( + PromptMessage.Empty, + PromptMessage.Error(expectedErrorMessage), + ) + .inOrder() + + testScheduler.advanceUntilIdle() + assertThat(messages) + .containsExactly( + PromptMessage.Empty, + PromptMessage.Error(expectedErrorMessage), + PromptMessage.Help("$expectedErrorMessage $afterSuffix"), + ) + .inOrder() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index c2219a4d82eb..481f36e8ea38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } - @Test - fun switchesToGone_whenUnlocked() = - testScope.runTest { - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene( - SceneTestUtils.CONTAINER_1, - SceneModel(SceneKey.Bouncer) - ) - val currentScene by - collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - - utils.authenticationRepository.setUnlocked(true) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - private fun assertTryAgainMessage( message: String?, time: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 0356036fa78f..0df0a17931f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -52,7 +52,10 @@ class BouncerViewModelTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, sceneInteractor = utils.sceneInteractor(), ) - private val underTest = utils.bouncerViewModel(bouncerInteractor) + private val underTest = + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, + ) @Test fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 7d6c4a1a1455..5c6d4c69b50b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -20,7 +20,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -54,16 +53,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { sceneInteractor = sceneInteractor, ) private val bouncerViewModel = - BouncerViewModel( - applicationContext = context, - applicationScope = testScope.backgroundScope, - interactorFactory = - object : BouncerInteractor.Factory { - override fun create(containerName: String): BouncerInteractor { - return bouncerInteractor - } - }, - containerName = SceneTestUtils.CONTAINER_1, + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, ) private val underTest = PinBouncerViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt index 8b36284b8620..ca6a5b6234b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt @@ -47,7 +47,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { private val underTest = utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -129,22 +128,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { } @Test - fun deviceLockedInNonLockScreenScene_switchesToLockScreenScene() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - runCurrent() - sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone)) - runCurrent() - utils.authenticationRepository.setUnlocked(true) - runCurrent() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - - utils.authenticationRepository.setUnlocked(false) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - } - - @Test fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() = testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 161dd660671e..ba8e0f277b6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -56,7 +56,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt new file mode 100644 index 000000000000..cfbbf768e1fe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt @@ -0,0 +1,128 @@ +/* + * 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.mediaprojection.taskswitcher.ui + +import android.app.Notification +import android.app.NotificationManager +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository +import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager +import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionRepository +import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor +import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.verify + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidTestingRunner::class) +@SmallTest +class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() { + + private val notificationManager: NotificationManager = mock() + + private val dispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(dispatcher) + private val fakeActivityTaskManager = FakeActivityTaskManager() + private val mediaRepo = FakeMediaProjectionRepository() + private val tasksRepo = + ActivityTaskManagerTasksRepository( + activityTaskManager = fakeActivityTaskManager.activityTaskManager, + applicationScope = testScope.backgroundScope, + backgroundDispatcher = dispatcher + ) + private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo) + private val viewModel = TaskSwitcherNotificationViewModel(interactor) + + private val coordinator = + TaskSwitcherNotificationCoordinator( + context, + notificationManager, + testScope.backgroundScope, + dispatcher, + viewModel + ) + + @Before + fun setup() { + coordinator.start() + } + + @Test + fun showNotification() { + testScope.runTest { + switchTask() + + val notification = ArgumentCaptor.forClass(Notification::class.java) + verify(notificationManager).notify(any(), any(), notification.capture()) + assertNotification(notification) + } + } + + @Test + fun hideNotification() { + testScope.runTest { + mediaRepo.stopProjecting() + + verify(notificationManager).cancel(any()) + } + } + + @Test + fun notificationIdIsConsistent() { + testScope.runTest { + mediaRepo.stopProjecting() + val idCancel = argumentCaptor<Int>() + verify(notificationManager).cancel(idCancel.capture()) + + switchTask() + val idNotify = argumentCaptor<Int>() + verify(notificationManager).notify(any(), idNotify.capture(), any()) + + assertEquals(idCancel.value, idNotify.value) + } + } + + private fun switchTask() { + val projectedTask = FakeActivityTaskManager.createTask(taskId = 1) + val foregroundTask = FakeActivityTaskManager.createTask(taskId = 2) + mediaRepo.switchProjectedTask(projectedTask) + fakeActivityTaskManager.moveTaskToForeground(foregroundTask) + } + + private fun assertNotification(notification: ArgumentCaptor<Notification>) { + val text = notification.value.extras.getCharSequence(Notification.EXTRA_TEXT) + assertEquals(context.getString(R.string.media_projection_task_switcher_text), text) + + val actions = notification.value.actions + assertThat(actions).hasLength(2) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt index 023ed061c642..45bb9313264c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt @@ -21,6 +21,10 @@ import android.os.PowerManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -44,6 +48,7 @@ class PowerInteractorTest : SysuiTestCase() { private lateinit var underTest: PowerInteractor private lateinit var repository: FakePowerRepository + private val keyguardRepository = FakeKeyguardRepository() @Mock private lateinit var falsingCollector: FalsingCollector @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -59,6 +64,7 @@ class PowerInteractorTest : SysuiTestCase() { underTest = PowerInteractor( repository, + keyguardRepository, falsingCollector, screenOffAnimationController, statusBarStateController, @@ -125,6 +131,57 @@ class PowerInteractorTest : SysuiTestCase() { verify(falsingCollector).onScreenOnFromTouch() } + @Test + fun wakeUpForFullScreenIntent_notGoingToSleepAndNotDozing_notWoken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNull() + assertThat(repository.lastWakeReason).isNull() + } + + @Test + fun wakeUpForFullScreenIntent_startingToSleep_woken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + + @Test + fun wakeUpForFullScreenIntent_dozing_woken() { + whenever(statusBarStateController.isDozing).thenReturn(true) + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 4eedc99839c6..ed7a59ea7032 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -53,7 +53,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt index df3701edc6de..3e9ddcb06389 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt @@ -14,26 +14,29 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) - package com.android.systemui.scene.domain.startable import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { @@ -41,29 +44,53 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val featureFlags = FakeFeatureFlags() + private val featureFlags = utils.featureFlags + private val authenticationRepository = utils.authenticationRepository() + private val authenticationInteractor = + utils.authenticationInteractor( + repository = authenticationRepository, + ) + private val keyguardRepository = utils.keyguardRepository() + private val keyguardInteractor = + utils.keyguardInteractor( + repository = keyguardRepository, + ) private val underTest = SystemUiDefaultSceneContainerStartable( applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, + authenticationInteractor = authenticationInteractor, + keyguardInteractor = keyguardInteractor, featureFlags = featureFlags, ) + @Before + fun setUp() { + prepareState() + } + @Test - fun start_featureEnabled_keepsVisibilityUpdated() = + fun hydrateVisibility_featureEnabled() = testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, true) + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) val isVisible by collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) assertThat(isVisible).isTrue() underTest.start() - sceneInteractor.setCurrentScene( - SceneContainerNames.SYSTEM_UI_DEFAULT, - SceneModel(SceneKey.Gone) - ) assertThat(isVisible).isFalse() sceneInteractor.setCurrentScene( @@ -74,14 +101,26 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { } @Test - fun start_featureDisabled_doesNotUpdateVisibility() = + fun hydrateVisibility_featureDisabled() = testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, false) + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) val isVisible by collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) assertThat(isVisible).isTrue() underTest.start() + assertThat(isVisible).isTrue() sceneInteractor.setCurrentScene( SceneContainerNames.SYSTEM_UI_DEFAULT, @@ -95,4 +134,269 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { ) assertThat(isVisible).isTrue() } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = false, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToGoneWhenDeviceSleepsUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchToGoneWhenDeviceSleepsUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + } + + @Test + fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + } + + private fun prepareState( + isFeatureEnabled: Boolean = true, + isDeviceUnlocked: Boolean = false, + isBypassEnabled: Boolean = false, + initialSceneKey: SceneKey? = null, + ) { + featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled) + authenticationRepository.setUnlocked(isDeviceUnlocked) + authenticationRepository.setBypassEnabled(isBypassEnabled) + initialSceneKey?.let { + sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it)) + } + } + + companion object { + private val ASLEEP = + WakefulnessModel( + state = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.POWER_BUTTON + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt index 16751c937f9e..5c35913f6e20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.settings.brightness +import android.content.Intent import android.graphics.Rect import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup +import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.R @@ -29,15 +31,20 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.activity.SingleActivityFactory import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.eq import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -48,9 +55,12 @@ class BrightnessDialogTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory - @Mock private lateinit var mainExecutor: Executor @Mock private lateinit var backgroundHandler: Handler @Mock private lateinit var brightnessSliderController: BrightnessSliderController + @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper + + private val clock = FakeSystemClock() + private val mainExecutor = FakeExecutor(clock) private var displayTracker = FakeDisplayTracker(mContext) @@ -64,7 +74,8 @@ class BrightnessDialogTest : SysuiTestCase() { displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) }, /* initialTouchMode= */ false, @@ -77,8 +88,6 @@ class BrightnessDialogTest : SysuiTestCase() { `when`(brightnessSliderControllerFactory.create(any(), any())) .thenReturn(brightnessSliderController) `when`(brightnessSliderController.rootView).thenReturn(View(context)) - - activityRule.launchActivity(null) } @After @@ -88,6 +97,7 @@ class BrightnessDialogTest : SysuiTestCase() { @Test fun testGestureExclusion() { + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container) val lp = frame.layoutParams as ViewGroup.MarginLayoutParams @@ -104,18 +114,83 @@ class BrightnessDialogTest : SysuiTestCase() { .isEqualTo(Rect(-horizontalMargin, 0, frame.width + horizontalMargin, frame.height)) } + @Test + fun testTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testRestartTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // Restart the timeout + activityRule.activity.onResume() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // The dialog should not have disappeared yet + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testNoTimeoutIfNotStartedByBrightnessKey() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isFalse() + } + class TestDialog( userTracker: UserTracker, displayTracker: FakeDisplayTracker, brightnessSliderControllerFactory: BrightnessSliderController.Factory, - mainExecutor: Executor, - backgroundHandler: Handler + mainExecutor: DelayableExecutor, + backgroundHandler: Handler, + accessibilityMgr: AccessibilityManagerWrapper ) : BrightnessDialog( userTracker, displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index a4fab1dbac57..77a22ac9b092 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository @@ -89,6 +90,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { dockManager, PowerInteractor( powerRepository, + FakeKeyguardRepository(), falsingCollector, screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 729c4a9145c2..52e0c9c9936b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -78,11 +78,11 @@ class ShadeControllerImplTest : SysuiTestCase() { deviceProvisionedController, notificationShadeWindowController, windowManager, + Lazy { shadeViewController }, Lazy { assistManager }, Lazy { gutsManager }, ) shadeController.setNotificationShadeWindowViewController(nswvc) - shadeController.setShadeViewController(shadeViewController) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index f542ab099517..bf25f2975253 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -29,6 +29,7 @@ import android.view.DisplayCutout import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets +import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.widget.ConstraintSet @@ -127,6 +128,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { var viewVisibility = View.GONE var viewAlpha = 1f + private val systemIcons = LinearLayout(context) private lateinit var shadeHeaderController: ShadeHeaderController private lateinit var carrierIconSlots: List<String> private val configurationController = FakeConfigurationController() @@ -146,6 +148,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { .thenReturn(batteryMeterView) whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons) + whenever<View>(view.findViewById(R.id.shade_header_system_icons)).thenReturn(systemIcons) viewContext = Mockito.spy(context) whenever(view.context).thenReturn(viewContext) @@ -451,6 +454,17 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @Test + fun testLargeScreenActive_collapseActionRun_onSystemIconsClick() { + shadeHeaderController.largeScreenActive = true + var wasRun = false + shadeHeaderController.shadeCollapseAction = Runnable { wasRun = true } + + systemIcons.performClick() + + assertThat(wasRun).isTrue() + } + + @Test fun testShadeExpandedFraction() { // View needs to be visible for this to actually take effect shadeHeaderController.qsVisible = true diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 309ab058fb64..6e9fba64263b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -22,7 +22,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.SceneTestUtils.Companion.CONTAINER_1 import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat @@ -54,7 +53,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -89,7 +87,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() @@ -102,7 +101,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index ff2f1065049b..4a2518ae6f7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -101,16 +101,18 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback private val disableFlagsRepository = FakeDisableFlagsRepository() + private val keyguardRepository = FakeKeyguardRepository() private val shadeInteractor = ShadeInteractor( testScope.backgroundScope, disableFlagsRepository, - keyguardRepository = FakeKeyguardRepository(), + keyguardRepository, userSetupRepository = FakeUserSetupRepository(), deviceProvisionedController = mock(), userInteractor = mock(), ) private val powerInteractor = PowerInteractor( FakePowerRepository(), + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController = mock(), statusBarStateController = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt new file mode 100644 index 000000000000..55b6be9679f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -0,0 +1,189 @@ +/* + * 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.statusbar.events + +import android.content.Context +import android.graphics.Rect +import android.util.Pair +import android.view.Gravity +import android.view.View +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +class SystemEventChipAnimationControllerTest : SysuiTestCase() { + private lateinit var controller: SystemEventChipAnimationController + + @Mock private lateinit var sbWindowController: StatusBarWindowController + @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider + + private var testView = TestView(mContext) + private var viewCreator: ViewCreator = { testView } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + // StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to + // ensure that the chip view is added to a parent view + whenever(sbWindowController.addViewToWindow(any(), any())).then { + val statusbarFake = FrameLayout(mContext) + statusbarFake.layout( + portraitArea.left, + portraitArea.top, + portraitArea.right, + portraitArea.bottom, + ) + statusbarFake.addView( + it.arguments[0] as View, + it.arguments[1] as FrameLayout.LayoutParams + ) + } + + whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(Pair(insets, insets)) + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(portraitArea) + + controller = + SystemEventChipAnimationController( + context = mContext, + statusBarWindowController = sbWindowController, + contentInsetsProvider = insetsProvider, + featureFlags = FakeFeatureFlags(), + ) + } + + @Test + fun prepareChipAnimation_lazyInitializes() { + // Until Dagger can do our initialization, make sure that the first chip animation calls + // init() + assertFalse(controller.initialized) + controller.prepareChipAnimation(viewCreator) + assertTrue(controller.initialized) + } + + @Test + fun prepareChipAnimation_positionsChip() { + controller.prepareChipAnimation(viewCreator) + val chipRect = controller.chipBounds + + // SB area = 10, 0, 990, 100 + // chip size = 0, 0, 100, 50 + assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75)) + } + + @Test + fun prepareChipAnimation_rotation_repositionsChip() { + controller.prepareChipAnimation(viewCreator) + + // Chip has been prepared, and is located at (890, 25, 990, 75) + // Rotation should put it into its landscape location: + // SB area = 10, 0, 1990, 80 + // chip size = 0, 0, 100, 50 + + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(landscapeArea) + getInsetsListener().onStatusBarContentInsetsChanged() + + val chipRect = controller.chipBounds + assertThat(chipRect).isEqualTo(Rect(1890, 15, 1990, 65)) + } + + /** regression test for (b/289378932) */ + @Test + fun fullScreenStatusBar_positionsChipAtTop_withTopGravity() { + // In the case of a fullscreen status bar window, the content insets area is still correct + // (because it uses the dimens), but the window can be full screen. This seems to happen + // when launching an app from the ongoing call chip. + + // GIVEN layout the status bar window fullscreen portrait + whenever(sbWindowController.addViewToWindow(any(), any())).then { + val statusbarFake = FrameLayout(mContext) + statusbarFake.layout( + fullScreenSb.left, + fullScreenSb.top, + fullScreenSb.right, + fullScreenSb.bottom, + ) + + val lp = it.arguments[1] as FrameLayout.LayoutParams + assertThat(lp.gravity and Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.TOP) + + statusbarFake.addView( + it.arguments[0] as View, + lp, + ) + } + + // GIVEN insets provider gives the correct content area + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(portraitArea) + + // WHEN the controller lays out the chip in a fullscreen window + controller.prepareChipAnimation(viewCreator) + + // THEN it still aligns the chip to the content area provided by the insets provider + val chipRect = controller.chipBounds + assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75)) + } + + class TestView(context: Context) : View(context), BackgroundAnimatableView { + override val view: View + get() = this + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(100, 50) + } + + override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) { + setLeftTopRightBottom(l, t, r, b) + } + } + + private fun getInsetsListener(): StatusBarContentInsetsChangedListener { + val callbackCaptor = argumentCaptor<StatusBarContentInsetsChangedListener>() + verify(insetsProvider).addCallback(capture(callbackCaptor)) + return callbackCaptor.value!! + } + + companion object { + private val portraitArea = Rect(10, 0, 990, 100) + private val landscapeArea = Rect(10, 0, 1990, 80) + private val fullScreenSb = Rect(10, 0, 990, 2000) + + // 10px insets on both sides + private const val insets = 10 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt index 89faa239c5a2..a56fb2c515a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt @@ -3,7 +3,11 @@ package com.android.systemui.statusbar.notification import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -15,8 +19,9 @@ import org.mockito.Mockito.verify @SmallTest @RunWith(JUnit4::class) class RoundableTest : SysuiTestCase() { - val targetView: View = mock() - val roundable = FakeRoundable(targetView) + private val targetView: View = mock() + private val featureFlags = FakeFeatureFlags() + private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags) @Test fun defaultConfig_shouldNotHaveRoundedCorner() { @@ -144,16 +149,62 @@ class RoundableTest : SysuiTestCase() { assertEquals(0.2f, roundable.roundableState.bottomRoundness) } + @Test + fun getCornerRadii_radius_maxed_to_height() { + whenever(targetView.height).thenReturn(10) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(5f, 5f) + } + + @Test + fun getCornerRadii_topRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 0f, SOURCE1) + + assertCornerRadiiEquals(5f, 0f) + } + + @Test + fun getCornerRadii_bottomRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(0f, 1f, SOURCE1) + + assertCornerRadiiEquals(0f, 5f) + } + + @Test + fun getCornerRadii_radii_kept() { + whenever(targetView.height).thenReturn(100) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS) + } + + private fun assertCornerRadiiEquals(top: Float, bottom: Float) { + assertEquals("topCornerRadius", top, roundable.topCornerRadius) + assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius) + } + class FakeRoundable( targetView: View, radius: Float = MAX_RADIUS, + featureFlags: FeatureFlags ) : Roundable { override val roundableState = RoundableState( targetView = targetView, roundable = this, maxRadius = radius, + featureFlags = featureFlags ) + + override val clipHeight: Int + get() = roundableState.targetView.height } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index a87dd2d3d670..8881f42783fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -58,6 +58,7 @@ class NotificationShelfInteractorTest : SysuiTestCase() { private val powerInteractor = PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 7ae150231b98..6221f3e89ad6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -69,6 +69,7 @@ class NotificationShelfViewModelTest : SysuiTestCase() { private val powerInteractor by lazy { PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index 442ba0977cf6..5e0e140563cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -95,6 +95,7 @@ class ActivityStarterImplTest : SysuiTestCase() { Lazy { notifShadeWindowController }, activityLaunchAnimator, context, + DISPLAY_ID, lockScreenUserManager, statusBarWindowController, wakefulnessLifecycle, @@ -274,4 +275,8 @@ class ActivityStarterImplTest : SysuiTestCase() { mainExecutor.runAllReady() verify(statusBarStateController).setLeaveOpenOnKeyguardHide(true) } + + private companion object { + private const val DISPLAY_ID = 0 + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 88d8dfc50b47..3d35233ad646 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -447,10 +447,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mDeviceProvisionedController, mNotificationShadeWindowController, mContext.getSystemService(WindowManager.class), + () -> mNotificationPanelViewController, () -> mAssistManager, () -> mNotificationGutsManager )); - mShadeController.setShadeViewController(mNotificationPanelViewController); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mShadeController.setNotificationPresenter(mNotificationPresenter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 8aaa57ffe2cb..9157cd9e4f43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static junit.framework.Assert.assertTrue; @@ -41,13 +40,11 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; @@ -156,13 +153,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { assertTrue("Expected StatusBarIconView", (manager.getViewAt(0) instanceof StatusBarIconView)); - holder = holderForType(TYPE_WIFI); - manager.onIconAdded(1, "test_wifi", false, holder); - assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView); - holder = holderForType(TYPE_MOBILE); - manager.onIconAdded(2, "test_mobile", false, holder); - assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView); + manager.onIconAdded(1, "test_mobile", false, holder); + assertTrue(manager.getViewAt(1) instanceof StatusBarMobileView); } private StatusBarIconHolder holderForType(int type) { @@ -170,9 +163,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { case TYPE_MOBILE: return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class)); - case TYPE_WIFI: - return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class)); - case TYPE_ICON: default: return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class)); @@ -214,13 +204,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); @@ -254,13 +237,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index c7143debf8a8..ed9cf3f2f158 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.service.trust.TrustAgentService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; @@ -57,6 +58,8 @@ import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; @@ -84,7 +87,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.google.common.truth.Truth; @@ -154,7 +156,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Captor private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor; @Captor - private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback; + private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; @Before @@ -936,18 +938,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void onDeviceUnlocked_hideAlternateBouncerAndClearMessageArea() { + public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() { + // GIVEN keyguard update monitor callback is registered + verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); + reset(mKeyguardUpdateMonitor); reset(mKeyguardMessageAreaController); - // GIVEN keyguard state controller callback is registered - verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture()); - // GIVEN alternate bouncer state = not visible when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - // WHEN the device is unlocked - mKeyguardStateControllerCallback.getValue().onUnlockedChanged(); + // WHEN the device is trusted by active unlock + mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser( + true, + true, + new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD + | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE), + null + ); // THEN the false visibility state is propagated to the keyguardUpdateMonitor verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index d44af885a27e..9c7f6190de44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -21,6 +21,8 @@ import static android.service.notification.NotificationListenerService.REASON_CL import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.AdditionalAnswers.answerVoid; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -61,10 +63,14 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.ShadeControllerImpl; @@ -110,6 +116,8 @@ import java.util.Optional; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { + private static final int DISPLAY_ID = 0; + @Mock private AssistManager mAssistManager; @Mock @@ -118,13 +126,12 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private NotificationClickNotifier mClickNotifier; @Mock private StatusBarStateController mStatusBarStateController; + @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock - private CentralSurfaces mCentralSurfaces; - @Mock private KeyguardStateController mKeyguardStateController; @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider; @@ -150,6 +157,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private InteractionJankMonitor mJankMonitor; + private FakePowerRepository mPowerRepository; + private PowerInteractor mPowerInteractor; @Mock private UserTracker mUserTracker; private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -199,6 +208,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(mUserTracker.getUserHandle()).thenReturn( UserHandle.of(ActivityManager.getCurrentUser())); + mPowerRepository = new FakePowerRepository(); + mPowerInteractor = new PowerInteractor( + mPowerRepository, + new FakeKeyguardRepository(), + new FalsingCollectorFake(), + mScreenOffAnimationController, + mStatusBarStateController); + HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( @@ -209,6 +226,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), + DISPLAY_ID, mHandler, mUiBgExecutor, mVisibilityProvider, @@ -231,13 +249,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), mOnUserInteractionCallback, - mCentralSurfaces, mock(NotificationPresenter.class), mock(ShadeViewController.class), mock(NotificationShadeWindowController.class), mActivityLaunchAnimator, notificationAnimationProvider, mock(LaunchFullScreenIntentProvider.class), + mPowerInteractor, mock(FeatureFlags.class), mUserTracker ); @@ -274,7 +292,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { notification.flags |= Notification.FLAG_AUTO_CANCEL; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mNotificationRow); @@ -340,7 +358,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = null; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -368,7 +386,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = mContentIntent; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -402,11 +420,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH); when(entry.getSbn()).thenReturn(sbn); - // WHEN + // WHEN the intent is launched while dozing + when(mStatusBarStateController.isDozing()).thenReturn(true); mNotificationActivityStarter.launchFullScreenIntent(entry); // THEN display should try wake up for the full screen intent - verify(mCentralSurfaces).wakeUpForFullScreenIntent(); + assertThat(mPowerRepository.getLastWakeReason()).isNotNull(); + assertThat(mPowerRepository.getLastWakeWhy()).isNotNull(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 862eb001becc..c8b6f13d6902 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -159,6 +159,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { mock(), mock(), FakeExecutor(FakeSystemClock()), + dispatcher, testScope.backgroundScope, mock(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt index 30b95ef3b205..5bc98e0d19af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -83,6 +83,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { logger, tableLogger, mainExecutor, + testDispatcher, testScope.backgroundScope, wifiManager, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index 5ed3a5c7aa41..7007345c175c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -76,7 +76,8 @@ class WifiRepositoryImplTest : SysuiTestCase() { private lateinit var executor: Executor private lateinit var connectivityRepository: ConnectivityRepository - private val testScope = TestScope(UnconfinedTestDispatcher()) + private val dispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(dispatcher) @Before fun setUp() { @@ -1301,6 +1302,7 @@ class WifiRepositoryImplTest : SysuiTestCase() { logger, tableLogger, executor, + dispatcher, testScope.backgroundScope, wifiManager, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt index 90821bdef0be..d212c026d66e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt @@ -126,6 +126,16 @@ class StylusUsiPowerUiTest : SysuiTestCase() { } @Test + fun updateBatteryState_capacitySame_inputDeviceChanges_updatesInputDeviceId() { + stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(1, FixedCapacityBatteryState(0.1f)) + + assertThat(stylusUsiPowerUi.inputDeviceId).isEqualTo(1) + verify(notificationManager, times(1)) + .notify(eq(R.string.stylus_battery_low_percentage), any()) + } + + @Test fun updateBatteryState_existingNotification_capacityAboveThreshold_cancelsNotification() { stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.8f)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 8f725bebfb16..0c77529377ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -142,6 +142,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), @@ -378,6 +379,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -397,6 +399,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -415,6 +419,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -433,6 +438,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -451,6 +458,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -469,6 +477,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -489,18 +499,19 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), - mDumpManager - ); + mDumpManager); dialog.init(0, null); verify(mPostureController, never()).removeCallback(any()); - dialog.destroy(); verify(mPostureController).removeCallback(any()); + + cleanUp(dialog); } private void setOrientation(int orientation) { @@ -513,14 +524,18 @@ public class VolumeDialogImplTest extends SysuiTestCase { @After public void teardown() { - if (mDialog != null) { - mDialog.clearInternalHandlerAfterTest(); - } + cleanUp(mDialog); setOrientation(mOriginalOrientation); mTestableLooper.processAllMessages(); reset(mPostureController); } + private void cleanUp(VolumeDialogImpl dialog) { + if (dialog != null) { + dialog.clearInternalHandlerAfterTest(); + } + } + /* @Test public void testContentDescriptions() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 9de7a87c8b82..ef0adbb91a63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,7 +34,6 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; @@ -76,7 +75,6 @@ public class WMShellTest extends SysuiTestCase { @Mock SplitScreen mSplitScreen; @Mock OneHanded mOneHanded; @Mock WakefulnessLifecycle mWakefulnessLifecycle; - @Mock ProtoTracer mProtoTracer; @Mock UserTracker mUserTracker; @Mock ShellExecutor mSysUiMainExecutor; @Mock NoteTaskInitializer mNoteTaskInitializer; @@ -99,7 +97,6 @@ public class WMShellTest extends SysuiTestCase { mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, - mProtoTracer, mWakefulnessLifecycle, mUserTracker, displayTracker, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 60a4951735c5..63097401bc5a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -48,7 +48,7 @@ class FakeKeyguardRepository : KeyguardRepository { override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing private val _isKeyguardUnlocked = MutableStateFlow(false) - override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked + override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow() private val _isKeyguardOccluded = MutableStateFlow(false) override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index fec1187d8d11..47e1daf4008c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -22,9 +22,20 @@ import com.android.systemui.authentication.data.repository.AuthenticationReposit import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.data.repository.BouncerRepository +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeCommandQueue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -51,6 +62,11 @@ class SceneTestUtils( ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) + val featureFlags = + FakeFeatureFlags().apply { + set(Flags.SCENE_CONTAINER, true) + set(Flags.FACE_AUTH_REFACTOR, false) + } private val userRepository: UserRepository by lazy { FakeUserRepository().apply { val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) @@ -64,6 +80,17 @@ class SceneTestUtils( currentTime = { testScope.currentTime }, ) } + val keyguardRepository: FakeKeyguardRepository by lazy { + FakeKeyguardRepository().apply { + setWakefulnessModel( + WakefulnessModel( + WakefulnessState.AWAKE, + WakeSleepReason.OTHER, + WakeSleepReason.OTHER, + ) + ) + } + } private val context = test.context fun fakeSceneContainerRepository( @@ -119,6 +146,20 @@ class SceneTestUtils( ) } + fun keyguardRepository(): FakeKeyguardRepository { + return keyguardRepository + } + + fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor { + return KeyguardInteractor( + repository = repository, + commandQueue = FakeCommandQueue(), + featureFlags = featureFlags, + bouncerRepository = FakeKeyguardBouncerRepository(), + configurationRepository = FakeConfigurationRepository() + ) + } + fun bouncerInteractor( authenticationInteractor: AuthenticationInteractor, sceneInteractor: SceneInteractor, @@ -129,6 +170,7 @@ class SceneTestUtils( repository = BouncerRepository(), authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } @@ -145,13 +187,13 @@ class SceneTestUtils( return bouncerInteractor } }, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } fun lockScreenSceneInteractor( authenticationInteractor: AuthenticationInteractor, - sceneInteractor: SceneInteractor, bouncerInteractor: BouncerInteractor, ): LockscreenSceneInteractor { return LockscreenSceneInteractor( @@ -163,7 +205,6 @@ class SceneTestUtils( return bouncerInteractor } }, - sceneInteractor = sceneInteractor, containerName = CONTAINER_1, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index c664c99cf2a7..56837e8cc7ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -21,7 +21,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.List; @@ -62,10 +61,6 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override - public void setWifiIcon(String slot, WifiIconState state) { - } - - @Override public void setNewWifiIcon() { } diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 82af38200166..7557071d0d4b 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -20,6 +20,8 @@ import static com.android.server.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; @@ -40,6 +42,7 @@ import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import android.widget.RemoteViews; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; @@ -50,6 +53,8 @@ import java.lang.ref.WeakReference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + public final class Helper { @@ -83,6 +88,44 @@ public final class Helper { throw new UnsupportedOperationException("contains static members only"); } + private static boolean checkRemoteViewUriPermissions( + @UserIdInt int userId, @NonNull RemoteViews rView) { + final AtomicBoolean permissionsOk = new AtomicBoolean(true); + + rView.visitUris(uri -> { + int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri); + boolean allowed = uriOwnerId == userId; + permissionsOk.set(allowed && permissionsOk.get()); + }); + + return permissionsOk.get(); + } + + /** + * Checks the URI permissions of the remote view, + * to see if the current userId is able to access it. + * + * Returns the RemoteView that is passed if user is able, null otherwise. + * + * TODO: instead of returning a null remoteview when + * the current userId cannot access an URI, + * return a new RemoteView with the URI removed. + */ + public static @Nullable RemoteViews sanitizeRemoteView(RemoteViews rView) { + if (rView == null) return null; + + int userId = ActivityManager.getCurrentUser(); + + boolean ok = checkRemoteViewUriPermissions(userId, rView); + if (!ok) { + Slog.w(TAG, + "sanitizeRemoteView() user: " + userId + + " tried accessing resource that does not belong to them"); + } + return (ok ? rView : null); + } + + @Nullable static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) { if (set == null) return null; diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java index dbeb624bd202..fa414e3b172b 100644 --- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java @@ -53,6 +53,7 @@ import android.widget.TextView; import com.android.internal.R; import com.android.server.autofill.AutofillManagerService; +import com.android.server.autofill.Helper; import java.io.PrintWriter; import java.util.ArrayList; @@ -208,7 +209,8 @@ final class DialogFillUi { } private void setHeader(View decor, FillResponse response) { - final RemoteViews presentation = response.getDialogHeader(); + final RemoteViews presentation = + Helper.sanitizeRemoteView(response.getDialogHeader()); if (presentation == null) { return; } @@ -243,9 +245,10 @@ final class DialogFillUi { } private void initialAuthenticationLayout(View decor, FillResponse response) { - RemoteViews presentation = response.getDialogPresentation(); + RemoteViews presentation = Helper.sanitizeRemoteView( + response.getDialogPresentation()); if (presentation == null) { - presentation = response.getPresentation(); + presentation = Helper.sanitizeRemoteView(response.getPresentation()); } if (presentation == null) { throw new RuntimeException("No presentation for fill dialog authentication"); @@ -289,7 +292,8 @@ final class DialogFillUi { final Dataset dataset = response.getDatasets().get(i); final int index = dataset.getFieldIds().indexOf(focusedViewId); if (index >= 0) { - RemoteViews presentation = dataset.getFieldDialogPresentation(index); + RemoteViews presentation = Helper.sanitizeRemoteView( + dataset.getFieldDialogPresentation(index)); if (presentation == null) { if (sDebug) { Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because " diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 129ce72e037d..cdfe7bb4f4a7 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -148,8 +148,9 @@ final class FillUi { final LayoutInflater inflater = LayoutInflater.from(mContext); - final RemoteViews headerPresentation = response.getHeader(); - final RemoteViews footerPresentation = response.getFooter(); + final RemoteViews headerPresentation = Helper.sanitizeRemoteView(response.getHeader()); + final RemoteViews footerPresentation = Helper.sanitizeRemoteView(response.getFooter()); + final ViewGroup decor; if (mFullScreen) { decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null); @@ -227,6 +228,9 @@ final class FillUi { ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker); final View content; try { + if (Helper.sanitizeRemoteView(response.getPresentation()) == null) { + throw new RuntimeException("Permission error accessing RemoteView"); + } content = response.getPresentation().applyWithTheme( mContext, decor, interceptionHandler, mThemeId); container.addView(content); @@ -306,7 +310,8 @@ final class FillUi { final Dataset dataset = response.getDatasets().get(i); final int index = dataset.getFieldIds().indexOf(focusedViewId); if (index >= 0) { - final RemoteViews presentation = dataset.getFieldPresentation(index); + final RemoteViews presentation = Helper.sanitizeRemoteView( + dataset.getFieldPresentation(index)); if (presentation == null) { Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because " + "service didn't provide a presentation for it on " + dataset); diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index f035d0764279..70382f1d5274 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -384,8 +384,7 @@ final class SaveUi { return false; } writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION); - - final RemoteViews template = customDescription.getPresentation(); + final RemoteViews template = Helper.sanitizeRemoteView(customDescription.getPresentation()); if (template == null) { Slog.w(TAG, "No remote view on custom description"); return false; diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 7fae31c0bb4b..bca2d60761d1 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -1827,6 +1827,9 @@ public class SystemConfig { soname, soname, new String[0], true); mSharedLibraries.put(entry.name, entry); } + } catch (FileNotFoundException e) { + // Expected for /vendor/etc/public.libraries.txt on some devices + Slog.d(TAG, listFile + " does not exist"); } catch (IOException e) { Slog.w(TAG, "Failed to read public libraries file " + listFile, e); } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index e51fc0a2cef7..a682c85f03b2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1713,6 +1713,11 @@ public class OomAdjuster { } } + private boolean isScreenOnOrAnimatingLocked(ProcessStateRecord state) { + return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE + || state.isRunningRemoteAnimation(); + } + @GuardedBy({"mService", "mProcLock"}) private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj, ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval, @@ -1794,8 +1799,7 @@ public class OomAdjuster { state.setSystemNoUi(false); } if (!state.isSystemNoUi()) { - if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE - || state.isRunningRemoteAnimation()) { + if (isScreenOnOrAnimatingLocked(state)) { // screen on or animating, promote UI state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI); state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP); @@ -3281,8 +3285,10 @@ public class OomAdjuster { } else { setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST); } - initialSchedGroup = SCHED_GROUP_TOP_APP; - initialProcState = PROCESS_STATE_TOP; + if (isScreenOnOrAnimatingLocked(state)) { + initialSchedGroup = SCHED_GROUP_TOP_APP; + initialProcState = PROCESS_STATE_TOP; + } initialCapability = PROCESS_CAPABILITY_ALL; initialCached = false; } catch (Exception e) { diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index ca15dd79adbc..c6d6122aeed6 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -40,6 +40,7 @@ import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.app.StatsManager; import android.app.UidObserver; import android.content.BroadcastReceiver; @@ -148,6 +149,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); private final Object mGameModeListenerLock = new Object(); + private final Object mGameStateListenerLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final Handler mHandler; private final PackageManager mPackageManager; @@ -164,6 +166,8 @@ public final class GameManagerService extends IGameManagerService.Stub { // listener to caller uid map @GuardedBy("mGameModeListenerLock") private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>(); + @GuardedBy("mGameStateListenerLock") + private final ArrayMap<IGameStateListener, Integer> mGameStateListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; private final Object mUidObserverLock = new Object(); @@ -352,6 +356,16 @@ public final class GameManagerService extends IGameManagerService.Stub { loadingBoostDuration); } } + synchronized (mGameStateListenerLock) { + for (IGameStateListener listener : mGameStateListeners.keySet()) { + try { + listener.onGameStateChanged(packageName, gameState, userId); + } catch (RemoteException ex) { + Slog.w(TAG, "Cannot notify game state change for listener added by " + + mGameStateListeners.get(listener)); + } + } + } break; } case CANCEL_GAME_LOADING_MODE: { @@ -1474,6 +1488,43 @@ public final class GameManagerService extends IGameManagerService.Stub { } /** + * Adds a game state listener. + */ + @Override + public void addGameStateListener(@NonNull IGameStateListener listener) { + try { + final IBinder listenerBinder = listener.asBinder(); + listenerBinder.linkToDeath(new DeathRecipient() { + @Override public void binderDied() { + removeGameStateListenerUnchecked(listener); + listenerBinder.unlinkToDeath(this, 0 /*flags*/); + } + }, 0 /*flags*/); + synchronized (mGameStateListenerLock) { + mGameStateListeners.put(listener, Binder.getCallingUid()); + } + } catch (RemoteException ex) { + Slog.e(TAG, + "Failed to link death recipient for IGameStateListener from caller " + + Binder.getCallingUid() + ", abandoned its listener registration", ex); + } + } + + /** + * Removes a game state listener. + */ + @Override + public void removeGameStateListener(@NonNull IGameStateListener listener) { + removeGameStateListenerUnchecked(listener); + } + + private void removeGameStateListenerUnchecked(IGameStateListener listener) { + synchronized (mGameStateListenerLock) { + mGameStateListeners.remove(listener); + } + } + + /** * Notified when boot is completed. */ @VisibleForTesting diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 06417d725e7e..f51b62d77ab9 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -66,7 +66,6 @@ import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; import com.android.server.biometrics.sensors.BaseClientMonitor; -import java.util.ArrayList; import java.util.List; public class Utils { @@ -98,33 +97,6 @@ public class Utils { } /** - * Get the enabled HAL instances. If virtual is enabled and available it will be returned as - * the only instance, otherwise all other instances will be returned. - * - * @param context system context - * @param declaredInstances known instances - * @return filtered list of enabled instances - */ - @NonNull - public static List<String> filterAvailableHalInstances(@NonNull Context context, - @NonNull List<String> declaredInstances) { - if (declaredInstances.size() <= 1) { - return declaredInstances; - } - - final int virtualAt = declaredInstances.indexOf("virtual"); - if (isVirtualEnabled(context) && virtualAt != -1) { - return List.of(declaredInstances.get(virtualAt)); - } - - declaredInstances = new ArrayList<>(declaredInstances); - if (virtualAt != -1) { - declaredInstances.remove(virtualAt); - } - return declaredInstances; - } - - /** * Combines {@link PromptInfo#setDeviceCredentialAllowed(boolean)} with * {@link PromptInfo#setAuthenticators(int)}, as the former is not flexible enough. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 28cb7d9445ee..7cc6940f4b9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -871,19 +871,22 @@ public class FingerprintService extends SystemService { super.registerAuthenticators_enforcePermission(); mRegistry.registerAll(() -> { - final List<ServiceProvider> providers = new ArrayList<>(); - providers.addAll(getHidlProviders(hidlSensors)); List<String> aidlSensors = new ArrayList<>(); final String[] instances = mAidlInstanceNameSupplier.get(); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - providers.addAll(getAidlProviders( - Utils.filterAvailableHalInstances(getContext(), aidlSensors))); + + final Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filteredInstances = filterAvailableHalInstances(hidlSensors, aidlSensors); + + final List<ServiceProvider> providers = new ArrayList<>(); + providers.addAll(getHidlProviders(filteredInstances.first)); + providers.addAll(getAidlProviders(filteredInstances.second)); + return providers; }); } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( @@ -1038,6 +1041,33 @@ public class FingerprintService extends SystemService { }); } + private Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filterAvailableHalInstances( + @NonNull List<FingerprintSensorPropertiesInternal> hidlInstances, + @NonNull List<String> aidlInstances) { + if ((hidlInstances.size() + aidlInstances.size()) <= 1) { + return new Pair(hidlInstances, aidlInstances); + } + + final int virtualAt = aidlInstances.indexOf("virtual"); + if (Utils.isVirtualEnabled(getContext())) { + if (virtualAt != -1) { + //only virtual instance should be returned + return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); + } else { + Slog.e(TAG, "Could not find virtual interface while it is enabled"); + return new Pair(hidlInstances, aidlInstances); + } + } else { + //remove virtual instance + aidlInstances = new ArrayList<>(aidlInstances); + if (virtualAt != -1) { + aidlInstances.remove(virtualAt); + } + return new Pair(hidlInstances, aidlInstances); + } + } + @NonNull private List<ServiceProvider> getHidlProviders( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 02ee96a04b1f..7bda2c1fa5ab 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1175,6 +1175,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId); + resolver.registerContentObserver(Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED), false, this); mRegistered = true; } @@ -1183,6 +1185,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); + final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED); synchronized (ImfLock.class) { if (showImeUri.equals(uri)) { mMenuController.updateKeyboardFromSettingsLocked(); @@ -1200,6 +1204,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub showCurrentInputImplicitLocked(mCurFocusedWindow, SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } + } else if (stylusHandwritingEnabledUri.equals(uri)) { + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); } else { boolean enabledChanged = false; String newEnabled = mSettings.getEnabledInputMethodsStr(); @@ -2363,7 +2369,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mCurVirtualDisplayToScreenMatrix = null; ImeTracker.forLogging().onFailed(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); mCurStatsToken = null; - + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); mMenuController.hideInputMethodMenuLocked(); } } diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS index 55b0cffb32df..5d4986307835 100644 --- a/services/core/java/com/android/server/locksettings/OWNERS +++ b/services/core/java/com/android/server/locksettings/OWNERS @@ -1,3 +1,4 @@ +# Bug component: 1333694 ebiggers@google.com jaggies@google.com rubinxu@google.com diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f1c0e130b2be..3325ddd58330 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7008,7 +7008,7 @@ public class NotificationManagerService extends SystemService { */ private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) { return notification.isMediaNotification() || isEnterpriseExempted(ai) - || isCallNotification(ai.packageName, ai.uid, notification) + || notification.isStyle(Notification.CallStyle.class) || isDefaultSearchSelectorPackage(ai.packageName); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 279a48084252..5cfbcaafe82e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3211,8 +3211,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { minLinearBrightness, maxLinearBrightness); mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness); - startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), - UserHandle.CURRENT_OR_SELF); + Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true); + startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN: diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index db3b267529e6..0c7d007dc48a 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -723,6 +723,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE; return; } + // Activity doesn't need to capture snapshot if the starting window has associated to task. + if (wc.asActivityRecord() != null) { + final ActivityRecord activityRecord = wc.asActivityRecord(); + if (activityRecord.mStartingData != null + && activityRecord.mStartingData.mAssociatedTask != null) { + return; + } + } if (mContainerFreezer == null) { mContainerFreezer = new ScreenshotFreezer(); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d1b00a38701d..4ce150e03558 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; @@ -1335,6 +1336,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub taskFragment.setAnimationParams(animationParams); break; } + case OP_TYPE_REORDER_TO_FRONT: { + final Task task = taskFragment.getTask(); + if (task != null) { + final TaskFragment topTaskFragment = task.getTaskFragment( + tf -> tf.asTask() == null); + if (topTaskFragment != null && topTaskFragment != taskFragment) { + final int index = task.mChildren.indexOf(topTaskFragment); + task.mChildren.remove(taskFragment); + task.mChildren.add(index, taskFragment); + } + } + break; + } } return effects; } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index a1199d99f1c3..6747cea80115 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -91,8 +91,6 @@ public final class CredentialManagerService CredentialManagerService, CredentialManagerServiceImpl> { private static final String TAG = "CredManSysService"; - private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API = - "enable_credential_description_api"; private static final String PERMISSION_DENIED_ERROR = "permission_denied"; private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR = "Caller is missing WRITE_SECURE_SETTINGS permission"; @@ -311,14 +309,7 @@ public final class CredentialManagerService } public static boolean isCredentialDescriptionApiEnabled() { - final long origId = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, - false); - } finally { - Binder.restoreCallingIdentity(origId); - } + return true; } @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index f2a8fdde8aa1..0e1ab7c29afe 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -434,6 +434,10 @@ public final class SystemServer implements Dumpable { + "OnDevicePersonalizationSystemService$Lifecycle"; private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS = "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle"; + private static final String DEVICE_LOCK_SERVICE_CLASS = + "com.android.server.devicelock.DeviceLockService"; + private static final String DEVICE_LOCK_APEX_PATH = + "/apex/com.android.devicelock/javalib/service-devicelock.jar"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -2867,6 +2871,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(HEALTHCONNECT_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_LOCK)) { + t.traceBegin("DeviceLockService"); + mSystemServiceManager.startServiceFromJar(DEVICE_LOCK_SERVICE_CLASS, + DEVICE_LOCK_APEX_PATH); + t.traceEnd(); + } + // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkPolicyManagerService networkPolicyF = networkPolicy; diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 4aba30a661ad..f660b42457db 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -16,7 +16,9 @@ package com.android.server.midi; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; @@ -48,6 +50,7 @@ import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.util.EventLog; import android.util.Log; @@ -81,6 +84,11 @@ import java.util.concurrent.atomic.AtomicLong; // 2. synchronized (mDeviceConnections) //TODO Introduce a single lock object to lock the whole state and avoid the requirement above. +// All users should be able to connect to USB and Bluetooth MIDI devices. +// All users can create can install an app that provides, a Virtual MIDI Device Service. +// Users can not open virtual MIDI devices created by other users. +// getDevices() surfaces devices that can be opened by that user. +// openDevice() rejects devices that are cannot be opened by that user. public class MidiService extends IMidiManager.Stub { public static class Lifecycle extends SystemService { @@ -97,10 +105,21 @@ public class MidiService extends IMidiManager.Stub { } @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + public void onUserStarting(@NonNull TargetUser user) { + mMidiService.onStartOrUnlockUser(user, false /* matchDirectBootUnaware */); + } + + @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public void onUserUnlocking(@NonNull TargetUser user) { - if (user.getUserIdentifier() == UserHandle.USER_SYSTEM) { - mMidiService.onUnlockUser(); - } + mMidiService.onStartOrUnlockUser(user, true /* matchDirectBootUnaware */); } } @@ -134,6 +153,7 @@ public class MidiService extends IMidiManager.Stub { private int mNextDeviceId = 1; private final PackageManager mPackageManager; + private final UserManager mUserManager; private static final String MIDI_LEGACY_STRING = "MIDI 1.0"; private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0"; @@ -159,21 +179,24 @@ public class MidiService extends IMidiManager.Stub { private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>(); // PackageMonitor for listening to package changes + // uid is the uid of the package so use getChangingUserId() to fetch the userId. private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageAdded(String packageName, int uid) { - addPackageDeviceServers(packageName); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageModified(String packageName) { - removePackageDeviceServers(packageName); - addPackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override public void onPackageRemoved(String packageName, int uid) { - removePackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); } }; @@ -202,6 +225,10 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + private int getUserId() { + return UserHandle.getUserId(mUid); + } + public void addListener(IMidiDeviceListener listener) { if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) { throw new SecurityException( @@ -219,8 +246,12 @@ public class MidiService extends IMidiManager.Stub { } } - public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) { - Log.d(TAG, "addDeviceConnection() device:" + device); + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback, + int userId) { + Log.d(TAG, "addDeviceConnection() device:" + device + " userId:" + userId); if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) { Log.i(TAG, "too many MIDI connections for UID = " + mUid); throw new SecurityException( @@ -228,7 +259,7 @@ public class MidiService extends IMidiManager.Stub { } DeviceConnection connection = new DeviceConnection(device, this, callback); mDeviceConnections.put(connection.getToken(), connection); - device.addDeviceConnection(connection); + device.addDeviceConnection(connection, userId); } // called from MidiService.closeDevice() @@ -251,8 +282,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceAdded(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -265,8 +296,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceRemoved(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -279,8 +310,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceStatusChanged(Device device, MidiDeviceStatus status) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; try { for (IMidiDeviceListener listener : mListeners.values()) { @@ -354,6 +385,8 @@ public class MidiService extends IMidiManager.Stub { private final ServiceInfo mServiceInfo; // UID of device implementation private final int mUid; + // User Id of the app. Only used for virtual devices + private final int mUserId; // ServiceConnection for implementing Service (virtual devices only) // mServiceConnection is non-null when connected or attempting to connect to the service @@ -375,19 +408,24 @@ public class MidiService extends IMidiManager.Stub { private AtomicInteger mTotalOutputBytes = new AtomicInteger(); public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo, - ServiceInfo serviceInfo, int uid) { + ServiceInfo serviceInfo, int uid, int userId) { mDeviceInfo = deviceInfo; mServiceInfo = serviceInfo; mUid = uid; + mUserId = userId; mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable( MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);; setDeviceServer(server); } + @RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public Device(BluetoothDevice bluetoothDevice) { mBluetoothDevice = bluetoothDevice; mServiceInfo = null; mUid = mBluetoothServiceUid; + mUserId = mUserManager.getMainUser().getIdentifier(); } private void setDeviceServer(IMidiDeviceServer server) { @@ -468,11 +506,22 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + public int getUserId() { + return mUserId; + } + public boolean isUidAllowed(int uid) { return (!mDeviceInfo.isPrivate() || mUid == uid); } - public void addDeviceConnection(DeviceConnection connection) { + public boolean isUserIdAllowed(int userId) { + return (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL || mUserId == userId); + } + + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(DeviceConnection connection, int userId) { Log.d(TAG, "addDeviceConnection() [A] connection:" + connection); synchronized (mDeviceConnections) { mDeviceConnectionsAdded.incrementAndGet(); @@ -537,8 +586,8 @@ public class MidiService extends IMidiManager.Stub { new ComponentName(mServiceInfo.packageName, mServiceInfo.name)); } - if (!mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE)) { + if (!mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE, UserHandle.of(mUserId))) { Log.e(TAG, "Unable to bind service: " + intent); setDeviceServer(null); mServiceConnection = null; @@ -886,6 +935,8 @@ public class MidiService extends IMidiManager.Stub { public MidiService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); // TEMPORARY - Disable BTL-MIDI //FIXME - b/25689266 @@ -913,32 +964,41 @@ public class MidiService extends IMidiManager.Stub { // mNonMidiUUIDs.add(BluetoothUuid.BATTERY); } - private void onUnlockUser() { - mPackageMonitor.register(mContext, null, true); - + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) { + Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: " + + matchDirectBootUnaware); Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); - List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices(intent, - PackageManager.GET_META_DATA); + int resolveFlags = PackageManager.GET_META_DATA; + if (matchDirectBootUnaware) { + resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + } + List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent, + resolveFlags, user.getUserIdentifier()); if (resolveInfos != null) { int count = resolveInfos.size(); for (int i = 0; i < count; i++) { ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo; if (serviceInfo != null) { - addPackageDeviceServer(serviceInfo); + addPackageDeviceServer(serviceInfo, user.getUserIdentifier()); } } } - PackageInfo info; - try { - info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0); - } catch (PackageManager.NameNotFoundException e) { - info = null; - } - if (info != null && info.applicationInfo != null) { - mBluetoothServiceUid = info.applicationInfo.uid; - } else { - mBluetoothServiceUid = -1; + if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) { + PackageInfo info; + try { + info = mPackageManager.getPackageInfoAsUser( + MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0, user.getUserIdentifier()); + } catch (PackageManager.NameNotFoundException e) { + info = null; + } + if (info != null && info.applicationInfo != null) { + mBluetoothServiceUid = info.applicationInfo.uid; + } } } @@ -960,10 +1020,11 @@ public class MidiService extends IMidiManager.Stub { // Inform listener of the status of all known devices. private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { + int userId = UserHandle.getUserId(uid); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - // ignore private devices that our client cannot access - if (device.isUidAllowed(uid)) { + // ignore devices that our client cannot access + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { try { MidiDeviceStatus status = device.getDeviceStatus(); if (status != null) { @@ -989,10 +1050,11 @@ public class MidiService extends IMidiManager.Stub { public MidiDeviceInfo[] getDevicesForTransport(int transport) { ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>(); int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - if (device.isUidAllowed(uid)) { + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { // UMP devices have protocols that are not PROTOCOL_UNKNOWN if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) { if (device.getDeviceInfo().getDefaultProtocol() @@ -1029,6 +1091,9 @@ public class MidiService extends IMidiManager.Stub { if (!device.isUidAllowed(Binder.getCallingUid())) { throw new SecurityException("Attempt to open private device with wrong UID"); } + if (!device.isUserIdAllowed(getCallingUserId())) { + throw new SecurityException("Attempt to open virtual device with wrong user id"); + } } if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) { @@ -1044,7 +1109,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [B] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1106,7 +1171,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [C] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1124,6 +1189,7 @@ public class MidiService extends IMidiManager.Stub { int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, int type, int defaultProtocol) { int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) { throw new SecurityException("only system can create USB devices"); } else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) { @@ -1133,7 +1199,7 @@ public class MidiService extends IMidiManager.Stub { synchronized (mDevicesByInfo) { return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames, outputPortNames, properties, server, null, false, uid, - defaultProtocol); + defaultProtocol, userId); } } @@ -1210,7 +1276,8 @@ public class MidiService extends IMidiManager.Stub { private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, IMidiDeviceServer server, ServiceInfo serviceInfo, - boolean isPrivate, int uid, int defaultProtocol) { + boolean isPrivate, int uid, int defaultProtocol, int userId) { + Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type); // Limit the number of devices per app. int deviceCountForApp = 0; @@ -1250,7 +1317,7 @@ public class MidiService extends IMidiManager.Stub { } } if (device == null) { - device = new Device(server, deviceInfo, serviceInfo, uid); + device = new Device(server, deviceInfo, serviceInfo, uid, userId); } mDevicesByInfo.put(deviceInfo, device); if (bluetoothDevice != null) { @@ -1281,12 +1348,14 @@ public class MidiService extends IMidiManager.Stub { } } - private void addPackageDeviceServers(String packageName) { + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + private void addPackageDeviceServers(String packageName, int userId) { PackageInfo info; try { - info = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + info = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; @@ -1295,13 +1364,14 @@ public class MidiService extends IMidiManager.Stub { ServiceInfo[] services = info.services; if (services == null) return; for (int i = 0; i < services.length; i++) { - addPackageDeviceServer(services[i]); + addPackageDeviceServer(services[i], userId); } } private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private void addPackageDeviceServer(ServiceInfo serviceInfo) { + private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) { + Log.d(TAG, "addPackageDeviceServer()" + userId); XmlResourceParser parser = null; try { @@ -1404,8 +1474,8 @@ public class MidiService extends IMidiManager.Stub { int uid; try { - ApplicationInfo appInfo = mPackageManager.getApplicationInfo( - serviceInfo.packageName, 0); + ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( + serviceInfo.packageName, 0, userId); uid = appInfo.uid; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "could not fetch ApplicationInfo for " @@ -1419,7 +1489,7 @@ public class MidiService extends IMidiManager.Stub { inputPortNames.toArray(EMPTY_STRING_ARRAY), outputPortNames.toArray(EMPTY_STRING_ARRAY), properties, null, serviceInfo, isPrivate, uid, - MidiDeviceInfo.PROTOCOL_UNKNOWN); + MidiDeviceInfo.PROTOCOL_UNKNOWN, userId); } // setting properties to null signals that we are no longer // processing a <device> @@ -1437,12 +1507,13 @@ public class MidiService extends IMidiManager.Stub { } } - private void removePackageDeviceServers(String packageName) { + private void removePackageDeviceServers(String packageName, int userId) { synchronized (mDevicesByInfo) { Iterator<Device> iterator = mDevicesByInfo.values().iterator(); while (iterator.hasNext()) { Device device = iterator.next(); - if (packageName.equals(device.getPackageName())) { + if (packageName.equals(device.getPackageName()) + && (device.getUserId() == userId)) { iterator.remove(); removeDeviceLocked(device); } @@ -1571,4 +1642,11 @@ public class MidiService extends IMidiManager.Stub { String extractUsbDeviceTag(String propertyName) { return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length()); } + + /** + * @return the user id of the calling user. + */ + private int getCallingUserId() { + return UserHandle.getUserId(Binder.getCallingUid()); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/OWNERS b/services/tests/displayservicetests/OWNERS index 6ce1ee4d3de2..6ce1ee4d3de2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/OWNERS +++ b/services/tests/displayservicetests/OWNERS diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 212a243c6a9e..cd3a78ed5e17 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -52,6 +52,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -1578,6 +1579,71 @@ public class GameManagerServiceTests { assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); } + @Test + public void testAddGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + gameManagerService.addGameStateListener(mockListener); + verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO); + GameState gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + mDeathRecipientCaptor.getValue().binderDied(); + verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + + @Test + public void testRemoveGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + + gameManagerService.addGameStateListener(mockListener); + gameManagerService.removeGameStateListener(mockListener); + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + GameState gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + private List<String> readGameModeInterventionList() throws Exception { final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(), "system/game_mode_intervention.list"); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 30180e8f43a7..7e81ef22eb65 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -11269,7 +11269,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Given: a call notification has the flag FLAG_ONGOING_EVENT set // feature flag: ALLOW_DISMISS_ONGOING is on mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); - when(mTelecomManager.isInManagedCall()).thenReturn(true); Person person = new Person.Builder() .setName("caller") diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 99ab715ab987..54b935132957 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; @@ -1590,6 +1591,46 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertEquals(taskFragmentBounds, mTaskFragment.getBounds()); } + @Test + public void testApplyTransaction_reorderTaskFragmentToFront() { + final Task task = createTask(mDisplayContent); + // Create a TaskFragment. + final IBinder token0 = new Binder(); + final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token0) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create another TaskFragment + final IBinder token1 = new Binder(); + final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token1) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create a non-embedded Activity on top. + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(task) + .build(); + mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); + mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); + + // Reorder TaskFragment to front + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + mTransaction.addTaskFragmentOperation(token0, operation); + assertApplyTransactionAllowed(mTransaction); + + // Ensure the non-embedded activity still on top. + assertEquals(topActivity, task.getTopChild().asActivityRecord()); + + // Ensure the TaskFragment is moved to front. + final TaskFragment frontMostTaskFragment = task.getTaskFragment(tf -> tf.asTask() == null); + assertEquals(frontMostTaskFragment, tf0); + } + /** * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the diff --git a/tests/Input/OWNERS b/tests/Input/OWNERS index d701f23cb9b8..3cffce960b1c 100644 --- a/tests/Input/OWNERS +++ b/tests/Input/OWNERS @@ -1 +1,2 @@ +# Bug component: 136048 include /core/java/android/hardware/input/OWNERS diff --git a/tests/StagedInstallTest/OWNERS b/tests/StagedInstallTest/OWNERS index aac68e994a39..d7301dc9c895 100644 --- a/tests/StagedInstallTest/OWNERS +++ b/tests/StagedInstallTest/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 36137 + include /services/core/java/com/android/server/pm/OWNERS dariofreni@google.com |