diff options
246 files changed, 4311 insertions, 1240 deletions
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 7d0d97917a4b..285eb4ac150e 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3863,6 +3863,17 @@ message LmkStateChanged { * system/core/lmkd/lmkd.c */ message LmkKillOccurred { + enum Reason { + UNKNOWN = 0; + PRESSURE_AFTER_KILL = 1; + NOT_RESPONDING = 2; + LOW_SWAP_AND_THRASHING = 3; + LOW_MEM_AND_SWAP = 4; + LOW_MEM_AND_THRASHING = 5; + DIRECT_RECL_AND_THRASHING = 6; + LOW_MEM_AND_SWAP_UTIL = 7; + } + // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -3892,6 +3903,15 @@ message LmkKillOccurred { // Min oom adj score considered by lmkd. optional int32 min_oom_score = 10; + + // Free physical memory on device at LMK time. + optional int32 free_mem_kb = 11; + + // Free swap on device at LMK time. + optional int32 free_swap_kb = 12; + + // What triggered the LMK event. + optional Reason reason = 13; } /* diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index af36260fedf2..c273cf08d03b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5151,10 +5151,18 @@ public class Notification implements Parcelable bindHeaderChronometerAndTime(contentView, p); bindProfileBadge(contentView, p); bindAlertedIcon(contentView, p); + bindActivePermissions(contentView, p); bindExpandButton(contentView, p); mN.mUsesStandardHeader = true; } + private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) { + int color = getNeutralColor(p); + contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP); + contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP); + contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP); + } + private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) { int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p); contentView.setDrawableTint(R.id.expand_button, false, color, diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index d2a15357aa1f..735980beebb1 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; - + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; /** @hide */ @@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; @@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; @@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @ChannelMode int channelMode, long codecSpecific1, @@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; @@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } @@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } @@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 1e394b830d51..7b567b4098e7 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index bd02210259b8..31c77eeb5424 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -963,7 +963,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { /** @hide */ public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2; /** @hide */ - public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3; + public static final int LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED = 3; /** @hide */ public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) { @@ -974,8 +974,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { return "LOCK_TASK_LAUNCH_MODE_NEVER"; case LOCK_TASK_LAUNCH_MODE_ALWAYS: return "LOCK_TASK_LAUNCH_MODE_ALWAYS"; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED"; + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + return "LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED"; default: return "unknown=" + lockTaskLaunchMode; } diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index aca5b458a2d6..08b23b04f2ae 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -221,6 +221,14 @@ public class UserInfo implements Parcelable { public boolean preCreated; /** + * When {@code true}, it indicates this user was created by converting a {@link #preCreated} + * user. + * + * <p><b>NOTE: </b>only used for debugging purposes, it's not set when marshalled to a parcel. + */ + public boolean convertedFromPreCreated; + + /** * Creates a UserInfo whose user type is determined automatically by the flags according to * {@link #getDefaultUserType}; can only be used for user types handled there. */ @@ -413,6 +421,7 @@ public class UserInfo implements Parcelable { lastLoggedInFingerprint = orig.lastLoggedInFingerprint; partial = orig.partial; preCreated = orig.preCreated; + convertedFromPreCreated = orig.convertedFromPreCreated; profileGroupId = orig.profileGroupId; restrictedProfileParentId = orig.restrictedProfileParentId; guestToRemove = orig.guestToRemove; @@ -440,6 +449,7 @@ public class UserInfo implements Parcelable { + ", type=" + userType + ", flags=" + flagsToString(flags) + (preCreated ? " (pre-created)" : "") + + (convertedFromPreCreated ? " (converted)" : "") + (partial ? " (partial)" : "") + "]"; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 10ac27ca57fd..8b45ba994d7e 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8929,14 +8929,6 @@ public final class Settings { public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption"; /** - * Controls which packages are blocked from persisting in media controls when resumption is - * enabled. The list of packages is set by the user in the Settings app. - * @see Settings.Secure#MEDIA_CONTROLS_RESUME - * @hide - */ - public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; - - /** * Controls if window magnification is enabled. * @hide */ diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 2c66910529f1..718f2de33d5c 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3954,9 +3954,7 @@ public final class Telephony { public static final String APN_SET_ID = "apn_set_id"; /** - * Possible value for the {@link #APN_SET_ID} field. By default APNs will not belong to a - * set. If the user manually selects an APN without apn set id, there is no need to - * prioritize any specific APN set ids. + * Possible value for the {@link #APN_SET_ID} field. By default APNs are added to set 0. * <p>Type: INTEGER</p> * @hide */ @@ -3964,6 +3962,15 @@ public final class Telephony { public static final int NO_APN_SET_ID = 0; /** + * Possible value for the {@link #APN_SET_ID} field. + * APNs with MATCH_ALL_APN_SET_ID will be used regardless of any set ids of + * the selected APN. + * <p>Type: INTEGER</p> + * @hide + */ + public static final int MATCH_ALL_APN_SET_ID = -1; + + /** * A unique carrier id associated with this APN * {@see TelephonyManager#getSimCarrierId()} * <p>Type: STRING</p> diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 593b37af26ad..0b43547f03de 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -453,9 +453,11 @@ public class InsetsState implements Parcelable { final ArraySet<Integer> result = new ArraySet<>(); if ((types & Type.STATUS_BARS) != 0) { result.add(ITYPE_STATUS_BAR); + result.add(ITYPE_CLIMATE_BAR); } if ((types & Type.NAVIGATION_BARS) != 0) { result.add(ITYPE_NAVIGATION_BAR); + result.add(ITYPE_EXTRA_NAVIGATION_BAR); } if ((types & Type.CAPTION_BAR) != 0) { result.add(ITYPE_CAPTION_BAR); diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 7a467d6ad73f..0c50cb782c24 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -52,11 +52,13 @@ public class NotificationHeaderView extends ViewGroup { private View mHeaderText; private View mSecondaryHeaderText; private OnClickListener mExpandClickListener; + private OnClickListener mAppOpsListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); private LinearLayout mTransferChip; private NotificationExpandButton mExpandButton; private CachingIconView mIcon; private View mProfileBadge; + private View mAppOps; private boolean mExpanded; private boolean mShowExpandButtonAtEnd; private boolean mShowWorkBadgeAtEnd; @@ -113,6 +115,7 @@ public class NotificationHeaderView extends ViewGroup { mExpandButton = findViewById(com.android.internal.R.id.expand_button); mIcon = findViewById(com.android.internal.R.id.icon); mProfileBadge = findViewById(com.android.internal.R.id.profile_badge); + mAppOps = findViewById(com.android.internal.R.id.app_ops); } @Override @@ -140,6 +143,7 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge + || child == mAppOps || child == mTransferChip) { iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); } else { @@ -204,6 +208,7 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge + || child == mAppOps || child == mTransferChip) { if (end == getMeasuredWidth()) { layoutRight = end - mContentEndMargin; @@ -272,10 +277,22 @@ public class NotificationHeaderView extends ViewGroup { } private void updateTouchListener() { + if (mExpandClickListener == null && mAppOpsListener == null) { + setOnTouchListener(null); + return; + } setOnTouchListener(mTouchListener); mTouchListener.bindTouchRects(); } + /** + * Sets onclick listener for app ops icons. + */ + public void setAppOpsOnClickListener(OnClickListener l) { + mAppOpsListener = l; + updateTouchListener(); + } + @Override public void setOnClickListener(@Nullable OnClickListener l) { mExpandClickListener = l; @@ -363,6 +380,7 @@ public class NotificationHeaderView extends ViewGroup { private final ArrayList<Rect> mTouchRects = new ArrayList<>(); private Rect mExpandButtonRect; + private Rect mAppOpsRect; private int mTouchSlop; private boolean mTrackGesture; private float mDownX; @@ -375,6 +393,8 @@ public class NotificationHeaderView extends ViewGroup { mTouchRects.clear(); addRectAroundView(mIcon); mExpandButtonRect = addRectAroundView(mExpandButton); + mAppOpsRect = addRectAroundView(mAppOps); + setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps)); addWidthRect(); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -435,6 +455,11 @@ public class NotificationHeaderView extends ViewGroup { break; case MotionEvent.ACTION_UP: if (mTrackGesture) { + if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y) + || mAppOpsRect.contains((int) mDownX, (int) mDownY))) { + mAppOps.performClick(); + return true; + } mExpandButton.performClick(); } break; diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 970d70cf1fb4..f56c357c57a8 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -52,6 +52,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -306,9 +307,6 @@ public class ProgressBar extends View { setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); - // onProgressRefresh() is only called when the progress changes. So we should set - // stateDescription during initialization here. - super.setStateDescription(formatStateDescription(mProgress)); setSecondaryProgress(a.getInt( R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); @@ -1601,7 +1599,8 @@ public class ProgressBar extends View { } void onProgressRefresh(float scale, boolean fromUser, int progress) { - if (mCustomStateDescription == null) { + if (AccessibilityManager.getInstance(mContext).isEnabled() + && mCustomStateDescription == null) { super.setStateDescription(formatStateDescription(mProgress)); } } @@ -2325,6 +2324,7 @@ public class ProgressBar extends View { AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(), getProgress()); info.setRangeInfo(rangeInfo); + info.setStateDescription(formatStateDescription(mProgress)); } } diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index e3a456cc94a4..0791ed3c42ec 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -167,6 +167,8 @@ public class ConversationLayout extends FrameLayout private int mFacePileProtectionWidthExpanded; private boolean mImportantConversation; private TextView mUnreadBadge; + private ViewGroup mAppOps; + private Rect mAppOpsTouchRect = new Rect(); private float mMinTouchSize; private Icon mConversationIcon; private Icon mShortcutIcon; @@ -208,6 +210,7 @@ public class ConversationLayout extends FrameLayout mConversationIconView = findViewById(R.id.conversation_icon); mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); + mAppOps = findViewById(com.android.internal.R.id.app_ops); mMinTouchSize = 48 * getResources().getDisplayMetrics().density; mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring); mConversationIconBadge = findViewById(R.id.conversation_icon_badge); @@ -1163,6 +1166,47 @@ public class ConversationLayout extends FrameLayout } }); } + if (mAppOps.getWidth() > 0) { + + // Let's increase the touch size of the app ops view if it's here + mAppOpsTouchRect.set( + mAppOps.getLeft(), + mAppOps.getTop(), + mAppOps.getRight(), + mAppOps.getBottom()); + for (int i = 0; i < mAppOps.getChildCount(); i++) { + View child = mAppOps.getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + // Make sure each child has at least a minTouchSize touch target around it + float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f + - mMinTouchSize / 2.0f; + float childTouchRight = childTouchLeft + mMinTouchSize; + mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left, + mAppOps.getLeft() + childTouchLeft); + mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right, + mAppOps.getLeft() + childTouchRight); + } + + // Increase the height + int heightIncrease = 0; + if (mAppOpsTouchRect.height() < mMinTouchSize) { + heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height()) + / 2.0f); + } + mAppOpsTouchRect.inset(0, -heightIncrease); + + // Let's adjust the hitrect since app ops isn't a direct child + ViewGroup viewGroup = (ViewGroup) mAppOps.getParent(); + while (viewGroup != this) { + mAppOpsTouchRect.offset(viewGroup.getLeft(), viewGroup.getTop()); + viewGroup = (ViewGroup) viewGroup.getParent(); + } + // + // Extend the size of the app opps to be at least 48dp + setTouchDelegate(new TouchDelegate(mAppOpsTouchRect, mAppOps)); + } } public MessagingLinearLayout getMessagingLinearLayout() { diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 5c045b65be22..7a5c38385f32 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -20,6 +20,7 @@ #include "android_media_AudioTrack.h" #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include "core_jni_helpers.h" #include <utils/Log.h> @@ -251,7 +252,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, jlong nativeAudioTrack, jboolean offload, jint encapsulationMode, - jobject tunerConfiguration) { + jobject tunerConfiguration, jstring opPackageName) { ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d," " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p", jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, @@ -337,7 +338,8 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we } // create the native AudioTrack object - lpTrack = new AudioTrack(); + ScopedUtfChars opPackageNameStr(env, opPackageName); + lpTrack = new AudioTrack(opPackageNameStr.c_str()); // read the AudioAttributes values auto paa = JNIAudioAttributeHelper::makeUnique(); @@ -371,23 +373,24 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we status_t status = NO_ERROR; switch (memoryMode) { case MODE_STREAM: - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - offload ? 0 : frameCount, - offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - 0,// shared mem - true,// thread can call Java - sessionId,// audio session ID - offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC, - offload ? &offloadInfo : NULL, - -1, -1, // default uid, pid values - paa.get()); - + status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed + // in paa (last argument) + sampleRateInHertz, + format, // word length, PCM + nativeChannelMask, offload ? 0 : frameCount, + offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD + : AUDIO_OUTPUT_FLAG_NONE, + audioCallback, + &(lpJniStorage->mCallbackData), // callback, callback data (user) + 0, // notificationFrames == 0 since not using EVENT_MORE_DATA + // to feed the AudioTrack + 0, // shared mem + true, // thread can call Java + sessionId, // audio session ID + offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK + : AudioTrack::TRANSFER_SYNC, + offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values + paa.get()); break; case MODE_STATIC: @@ -398,22 +401,22 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we goto native_init_failure; } - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - frameCount, - AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - lpJniStorage->mMemBase,// shared mem - true,// thread can call Java - sessionId,// audio session ID - AudioTrack::TRANSFER_SHARED, - NULL, // default offloadInfo - -1, -1, // default uid, pid values - paa.get()); + status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed + // in paa (last argument) + sampleRateInHertz, + format, // word length, PCM + nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, + audioCallback, + &(lpJniStorage->mCallbackData), // callback, callback data (user) + 0, // notificationFrames == 0 since not using EVENT_MORE_DATA + // to feed the AudioTrack + lpJniStorage->mMemBase, // shared mem + true, // thread can call Java + sessionId, // audio session ID + AudioTrack::TRANSFER_SHARED, + NULL, // default offloadInfo + -1, -1, // default uid, pid values + paa.get()); break; default: @@ -1428,7 +1431,8 @@ static const JNINativeMethod gMethods[] = { {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I", + {"native_setup", + "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I", (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d0ca0a86efb0..6a92a83c9899 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5036,6 +5036,10 @@ <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" android:protectionLevel="signature|appPredictor" /> + <!-- @hide Allows an application to create/destroy input consumer. --> + <permission android:name="android.permission.INPUT_CONSUMER" + android:protectionLevel="signature" /> + <!-- Attribution for Country Detector. --> <attribution android:tag="CountryDetector" android:label="@string/country_detector"/> <!-- Attribution for Location service. --> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 03e130e8023b..23b8bd34829e 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -146,6 +146,43 @@ android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" /> + <LinearLayout + android:id="@+id/app_ops" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_marginStart="6dp" + android:background="?android:selectableItemBackgroundBorderless" + android:orientation="horizontal"> + <ImageView + android:id="@+id/camera" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_camera" + android:visibility="gone" + android:focusable="false" + android:contentDescription="@string/notification_appops_camera_active" + /> + <ImageView + android:id="@+id/mic" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_mic" + android:layout_marginStart="4dp" + android:visibility="gone" + android:focusable="false" + android:contentDescription="@string/notification_appops_microphone_active" + /> + <ImageView + android:id="@+id/overlay" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_alert_window_layer" + android:layout_marginStart="4dp" + android:visibility="gone" + android:focusable="false" + android:contentDescription="@string/notification_appops_overlay_active" + /> + </LinearLayout> <include layout="@layout/notification_material_media_transfer_action" android:id="@+id/media_seamless" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index ea8498ab33da..57f2d6aea741 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1216,7 +1216,7 @@ <string name="dump_heap_ready_text" msgid="5849618132123045516">"የ<xliff:g id="PROC">%1$s</xliff:g> ሂደት ተራጋፊ ክምር ለማጋራት ለእርስዎ ይገኛል። ይጠንቀቁ፦ ይህ ተራጋፊ ክምር ሂደቱ ሊደርስባቸው የሚችለው ማንኛውም የግል መረጃ ሊኖረው ይችላል፣ ይህ እርስዎ የተየቧቸውን ነገሮች ሊያካትት ይችላል።"</string> <string name="sendText" msgid="493003724401350724">"ለፅሁፍ ድርጊት ምረጥ"</string> <string name="volume_ringtone" msgid="134784084629229029">"የስልክ ጥሪ ድምፅ"</string> - <string name="volume_music" msgid="7727274216734955095">" ማህደረ መረጃ ክፍልፍል"</string> + <string name="volume_music" msgid="7727274216734955095">"የማህደረ መረጃ ድምጽ መጠን"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"በብሉቱዝ በኩል ማጫወት"</string> <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"የፀጥታ የስልክ የደውል ድምፅ ተዘጋጅቷል"</string> <string name="volume_call" msgid="7625321655265747433">"የጥሪ ላይ ድም ፅ መጨመሪያ/መቀነሻ"</string> @@ -1227,7 +1227,7 @@ <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"የብሉቱዝ ድምፅ መጠን"</string> <string name="volume_icon_description_ringer" msgid="2187800636867423459">"የስልክ ጥሪ ድምፅ መጠን"</string> <string name="volume_icon_description_incall" msgid="4491255105381227919">"የስልክ ጥሪ ድምፅ መጠን"</string> - <string name="volume_icon_description_media" msgid="4997633254078171233">"የማህደረ መረጃ ክፍልፍል"</string> + <string name="volume_icon_description_media" msgid="4997633254078171233">"የማህደረ መረጃ ድምጽ መጠን"</string> <string name="volume_icon_description_notification" msgid="579091344110747279">"የማሳወቂያ ክፍልፍል"</string> <string name="ringtone_default" msgid="9118299121288174597">"ነባሪ የስልክ ላይ ጥሪ"</string> <string name="ringtone_default_with_actual" msgid="2709686194556159773">"ነባሪ (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 6978a9aefb17..4a99ec8021e8 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -270,9 +270,9 @@ <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"وضع صامت"</string> <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"الصوت متوقف"</string> <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"الصوت قيد التفعيل"</string> - <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطائرة"</string> - <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطائرة قيد التفعيل"</string> - <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطائرة متوقف"</string> + <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطيران"</string> + <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطيران قيد التفعيل"</string> + <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطيران متوقف"</string> <string name="global_action_settings" msgid="4671878836947494217">"الإعدادات"</string> <string name="global_action_assist" msgid="2517047220311505805">"مساعدة"</string> <string name="global_action_voice_assist" msgid="6655788068555086695">"المساعد الصوتي"</string> @@ -398,7 +398,7 @@ <string name="permlab_getPackageSize" msgid="375391550792886641">"قياس مساحة تخزين التطبيق"</string> <string name="permdesc_getPackageSize" msgid="742743530909966782">"للسماح للتطبيق باسترداد شفرته وبياناته وأحجام ذاكرات التخزين المؤقت"</string> <string name="permlab_writeSettings" msgid="8057285063719277394">"تعديل إعدادات النظام"</string> - <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة تهيئة نظامك."</string> + <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة إعداد نظامك."</string> <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"العمل عند بدء التشغيل"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"للسماح للتطبيق ببدء تشغيل نفسه عقب انتهاء النظام من التشغيل. قد يؤدي ذلك إلى استغراق المزيد من الوقت عند بدء الجهاز اللوحي والسماح للتطبيق بإبطاء الأداء الإجمالي للجهاز اللوحي من خلال تشغيله دائمًا."</string> <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"للسماح بتشغيل التطبيق تلقائيًا بعد الانتهاء من بدء تشغيل النظام. وقد يؤدي ذلك إلى إطالة فترة بدء تشغيل جهاز Android TV، بالإضافة إلى أنه يسمح للتطبيق بإبطاء أداء الجهاز بشكل عام لأنه يتم تشغيله بشكل دائم."</string> @@ -507,7 +507,7 @@ <string name="permlab_accessWifiState" msgid="5552488500317911052">"عرض اتصالات Wi-Fi"</string> <string name="permdesc_accessWifiState" msgid="6913641669259483363">"للسماح للتطبيق بعرض معلومات حول شبكات Wi-Fi، كعرض معلومات حول ما إذا تم تفعيل Wi-Fi واسم أجهزة Wi-Fi المتصلة."</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"التوصيل والفصل من Wi-Fi"</string> - <string name="permdesc_changeWifiState" msgid="7170350070554505384">"للسماح للتطبيق بالاتصال بنقاط الوصول إلى Wi-Fi وقطع الاتصال بها، وإجراء تغييرات على تهيئة الجهاز لشبكات Wi-Fi."</string> + <string name="permdesc_changeWifiState" msgid="7170350070554505384">"للسماح للتطبيق بالاتصال بنقاط الوصول إلى Wi-Fi وقطع الاتصال بها، وإجراء تغييرات على إعداد الجهاز لشبكات Wi-Fi."</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"السماح باستقبال بث Wi-Fi متعدد"</string> <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"للسماح للتطبيق بتلقي الحزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهازك اللوحي فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق وضع البث غير المتعدد."</string> <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"للسماح للتطبيق بتلقّي الحِزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهاز Android TV فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق ما يتم استهلاكه في وضع البث غير المتعدد."</string> @@ -523,9 +523,9 @@ <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"للسماح للتطبيق بتوصيل جهاز Android TV بشبكات WiMAX وقطع اتصاله بها."</string> <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"للسماح للتطبيق بتوصيل الهاتف بشبكات WiMAX وقطع اتصاله بها."</string> <string name="permlab_bluetooth" msgid="586333280736937209">"الاتصال بأجهزة بلوتوث"</string> - <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"للسماح للتطبيق بعرض تهيئة البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> + <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"للسماح للتطبيق بعرض إعداد البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"للسماح للتطبيق بعرض بيانات ضبط البلوتوث على جهاز Android TV وإجراء اتصالات مع الأجهزة المقترنة وقبولها."</string> - <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"للسماح للتطبيق بعرض تهيئة البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> + <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"للسماح للتطبيق بعرض إعداد البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"يسمح هذا الإذن للتطبيق بالحصول على معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل، مثلاً المساعدات المسجّلة ووجهة المسار."</string> <string name="permlab_nfc" msgid="1904455246837674977">"التحكم في اتصال الحقل القريب"</string> @@ -661,8 +661,8 @@ <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"للسماح للمالك بالربط بواجهة المستوى العلوي لخدمة موفر الحالة. لن تكون هناك حاجة إلى هذا الإعداد مطلقًا مع التطبيقات العادية."</string> <string name="permlab_bindDreamService" msgid="4776175992848982706">"الالتزام بخدمة dream"</string> <string name="permdesc_bindDreamService" msgid="9129615743300572973">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة dream. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> - <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال"</string> - <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> + <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال"</string> + <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"الاستماع إلى ملاحظات حول أحوال الشبكة"</string> <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"للسماح للتطبيق بالاستماع إلى ملاحظات حول أحوال الشبكة. لا حاجة إلى هذا مع التطبيقات العادية."</string> <string name="permlab_setInputCalibration" msgid="932069700285223434">"تغيير معايرة أجهزة الإدخال"</string> @@ -678,7 +678,7 @@ <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"الالتزام بخدمات مشغل شبكة الجوال"</string> <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"للسماح للمالك بالالتزام بخدمات مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_access_notification_policy" msgid="5524112842876975537">"الوصول إلى إعداد \"عدم الإزعاج\""</string> - <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة تهيئة \"عدم الإزعاج\" وكتابتها."</string> + <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string> @@ -1298,7 +1298,7 @@ <string name="volume_ringtone" msgid="134784084629229029">"مستوى صوت الرنين"</string> <string name="volume_music" msgid="7727274216734955095">"مستوى صوت الوسائط"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"تشغيل من خلال البلوتوث"</string> - <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"تم تعيين نغمة الرنين الصامتة"</string> + <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"تم ضبط نغمة الرنين الصامتة"</string> <string name="volume_call" msgid="7625321655265747433">"مستوى صوت المكالمات الواردة"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"مستوى صوت المكالمة الواردة بالبلوتوث"</string> <string name="volume_alarm" msgid="4486241060751798448">"مستوى صوت المنبّه"</string> @@ -1462,7 +1462,7 @@ <string name="ext_media_status_unmountable" msgid="7043574843541087748">"تالف"</string> <string name="ext_media_status_unsupported" msgid="5460509911660539317">"غير متوافق"</string> <string name="ext_media_status_ejecting" msgid="7532403368044013797">"جارٍ إنهاء التحميل…"</string> - <string name="ext_media_status_formatting" msgid="774148701503179906">"جارٍ التهيئة…"</string> + <string name="ext_media_status_formatting" msgid="774148701503179906">"تجري التهيئة..."</string> <string name="ext_media_status_missing" msgid="6520746443048867314">"لم يتم الإدخال"</string> <string name="activity_list_empty" msgid="4219430010716034252">"لم يتم العثور على أي أنشطة متطابقة."</string> <string name="permlab_route_media_output" msgid="8048124531439513118">"توجيه إخراج الوسائط"</string> @@ -2155,7 +2155,7 @@ <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"جدول بيانات: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string> <string name="mime_type_presentation" msgid="1145384236788242075">"عرض تقديمي"</string> <string name="mime_type_presentation_ext" msgid="8761049335564371468">"عرض تقديمي: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string> - <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطائرة\"."</string> + <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطيران\"."</string> <string name="car_loading_profile" msgid="8219978381196748070">"جارٍ التحميل"</string> <plurals name="file_count" formatted="false" msgid="7063513834724389247"> <item quantity="zero"><xliff:g id="FILE_NAME_2">%s</xliff:g> و<xliff:g id="COUNT_3">%d</xliff:g> ملف</item> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 18b69875b901..40fe31d873b2 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -241,7 +241,7 @@ <string name="global_action_power_off" msgid="4404936470711393203">"Isključi"</string> <string name="global_action_power_options" msgid="1185286119330160073">"Napajanje"</string> <string name="global_action_restart" msgid="4678451019561687074">"Restartuj"</string> - <string name="global_action_emergency" msgid="1387617624177105088">"Hitni poziv"</string> + <string name="global_action_emergency" msgid="1387617624177105088">"Hitan poziv"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"Izveštaj o grešci"</string> <string name="global_action_logout" msgid="6093581310002476511">"Završi sesiju"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"Snimak ekrana"</string> @@ -832,7 +832,7 @@ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string> - <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitni poziv"</string> + <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitan poziv"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 74f0e68f5f3f..df7fca0bc6e1 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -717,10 +717,10 @@ <item msgid="6216981255272016212">"Асаблівы"</item> </string-array> <string-array name="emailAddressTypes"> - <item msgid="7786349763648997741">"Хатні"</item> - <item msgid="435564470865989199">"Працоўны"</item> - <item msgid="4199433197875490373">"Іншы"</item> - <item msgid="3233938986670468328">"Карыстальніцкі"</item> + <item msgid="7786349763648997741">"Асабістая"</item> + <item msgid="435564470865989199">"Працоўная"</item> + <item msgid="4199433197875490373">"Іншая"</item> + <item msgid="3233938986670468328">"Карыстальніцкая"</item> </string-array> <string-array name="postalAddressTypes"> <item msgid="3861463339764243038">"На Галоўную старонку"</item> @@ -770,14 +770,14 @@ <string name="phoneTypeWorkPager" msgid="3748332310638505234">"Працоўны пэйджар"</string> <string name="phoneTypeAssistant" msgid="757550783842231039">"Асістэнт"</string> <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string> - <string name="eventTypeCustom" msgid="3257367158986466481">"Карыстальніцкі"</string> + <string name="eventTypeCustom" msgid="3257367158986466481">"Карыстальніцкае"</string> <string name="eventTypeBirthday" msgid="7770026752793912283">"Дзень нараджэння"</string> <string name="eventTypeAnniversary" msgid="4684702412407916888">"Гадавіна"</string> <string name="eventTypeOther" msgid="530671238533887997">"Іншае"</string> - <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкі"</string> + <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкая"</string> <string name="emailTypeHome" msgid="1597116303154775999">"Хатні"</string> - <string name="emailTypeWork" msgid="2020095414401882111">"Працоўны"</string> - <string name="emailTypeOther" msgid="5131130857030897465">"Іншы"</string> + <string name="emailTypeWork" msgid="2020095414401882111">"Працоўная"</string> + <string name="emailTypeOther" msgid="5131130857030897465">"Іншая"</string> <string name="emailTypeMobile" msgid="787155077375364230">"Мабільны"</string> <string name="postalTypeCustom" msgid="5645590470242939129">"Карыстальніцкі"</string> <string name="postalTypeHome" msgid="7562272480949727912">"Хатні"</string> @@ -800,19 +800,19 @@ <string name="orgTypeWork" msgid="8684458700669564172">"Працоўная"</string> <string name="orgTypeOther" msgid="5450675258408005553">"Іншая"</string> <string name="orgTypeCustom" msgid="1126322047677329218">"Карыстальніцкі"</string> - <string name="relationTypeCustom" msgid="282938315217441351">"Карыстальніцкі"</string> + <string name="relationTypeCustom" msgid="282938315217441351">"Карыстальніцкае"</string> <string name="relationTypeAssistant" msgid="4057605157116589315">"Памочнік"</string> <string name="relationTypeBrother" msgid="7141662427379247820">"Брат"</string> <string name="relationTypeChild" msgid="9076258911292693601">"Дзіця"</string> - <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Унутраны Партнёр"</string> + <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Сужыцель/сужыцелька"</string> <string name="relationTypeFather" msgid="3856225062864790596">"Бацька"</string> <string name="relationTypeFriend" msgid="3192092625893980574">"Сябар/сяброўка"</string> <string name="relationTypeManager" msgid="2272860813153171857">"Кіраўнік"</string> <string name="relationTypeMother" msgid="2331762740982699460">"Маці"</string> - <string name="relationTypeParent" msgid="4177920938333039882">"Бацька"</string> + <string name="relationTypeParent" msgid="4177920938333039882">"Бацька/маці"</string> <string name="relationTypePartner" msgid="4018017075116766194">"Партнёр"</string> - <string name="relationTypeReferredBy" msgid="5285082289602849400">"Запрошаны"</string> - <string name="relationTypeRelative" msgid="3396498519818009134">"Адносны"</string> + <string name="relationTypeReferredBy" msgid="5285082289602849400">"Рэкамендацыя"</string> + <string name="relationTypeRelative" msgid="3396498519818009134">"Радня"</string> <string name="relationTypeSister" msgid="3721676005094140671">"Сястра"</string> <string name="relationTypeSpouse" msgid="6916682664436031703">"Муж/жонка"</string> <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Карыстальніцкі"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 02dd4f2bfe3c..32495371ad01 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -970,7 +970,7 @@ <string name="save_password_remember" msgid="6490888932657708341">"Zapamti"</string> <string name="save_password_never" msgid="6776808375903410659">"Nikad"</string> <string name="open_permission_deny" msgid="5136793905306987251">"Nemate odobrenje za otvaranje ove stranice."</string> - <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međuspremnik."</string> + <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međumemoriju."</string> <string name="copied" msgid="4675902854553014676">"Kopirano"</string> <string name="more_item_label" msgid="7419249600215749115">"Više"</string> <string name="prepend_shortcut_label" msgid="1743716737502867951">"Meni+"</string> @@ -1280,7 +1280,7 @@ <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string> <string name="accept" msgid="5447154347815825107">"Prihvati"</string> <string name="decline" msgid="6490507610282145874">"Odbijte"</string> - <string name="select_character" msgid="3352797107930786979">"Umetni karakter"</string> + <string name="select_character" msgid="3352797107930786979">"Umetni znak"</string> <string name="sms_control_title" msgid="4748684259903148341">"Slanje SMS poruka"</string> <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> šalje veliki broj SMS poruka. Da li želite dozvoliti ovoj aplikaciji da nastavi slanje poruka?"</string> <string name="sms_control_yes" msgid="4858845109269524622">"Dozvoli"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 6529de8af786..1c882acd40ee 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1221,7 +1221,7 @@ <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"S\'ha establert el so de silenci"</string> <string name="volume_call" msgid="7625321655265747433">"Volum en trucada"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"Volum en trucada per Bluetooth"</string> - <string name="volume_alarm" msgid="4486241060751798448">"Volum de l\'alarma"</string> + <string name="volume_alarm" msgid="4486241060751798448">"Volum d\'alarma"</string> <string name="volume_notification" msgid="6864412249031660057">"Volum de notificacions"</string> <string name="volume_unknown" msgid="4041914008166576293">"Volum"</string> <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Volum del Bluetooth"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index b8c00d049721..1024729b53fe 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -421,7 +421,7 @@ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Esta app puede agregar, quitar o cambiar eventos del calendario en tu teléfono. Puede enviar mensajes que parecen proceder de propietarios del calendario o cambiar eventos sin notificarlos."</string> <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"acceder a comandos adicionales del proveedor del lugar"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string> - <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación exacta solo en primer plano"</string> + <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación precisa solo en primer plano"</string> <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Mientras la usas, esta app puede obtener tu ubicación exacta mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados. Es posible que esto aumente el uso de batería."</string> <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"acceder a la ubicación aproximada solo en primer plano"</string> <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Mientras la usas, esta app puede obtener tu ubicación aproximada mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index e5600b0e414d..b7a332efed21 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -849,7 +849,7 @@ <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausar"</string> <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Reproducir"</string> <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Deter"</string> - <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Rebobinar"</string> + <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Retroceder"</string> <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Avance rápido"</string> <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 5ba54d3ab9e2..6726be21ae67 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1135,7 +1135,7 @@ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"ઍક્સેસ આપો"</string> <string name="whichEditApplication" msgid="6191568491456092812">"આનાથી સંપાદિત કરો"</string> <string name="whichEditApplicationNamed" msgid="8096494987978521514">"%1$s સાથે સંપાદિત કરો"</string> - <string name="whichEditApplicationLabel" msgid="1463288652070140285">"સંપાદિત કરો"</string> + <string name="whichEditApplicationLabel" msgid="1463288652070140285">"ફેરફાર કરો"</string> <string name="whichSendApplication" msgid="4143847974460792029">"શેર કરો"</string> <string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$s સાથે શેર કરો"</string> <string name="whichSendApplicationLabel" msgid="7467813004769188515">"શેર કરો"</string> @@ -1221,7 +1221,7 @@ <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"સાઇલેન્ટ રિંગટોન સેટ કરી"</string> <string name="volume_call" msgid="7625321655265747433">"ઇન-કૉલ વૉલ્યૂમ"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"બ્લૂટૂથ ઇન-કૉલ વૉલ્યૂમ"</string> - <string name="volume_alarm" msgid="4486241060751798448">"એલાર્મ વૉલ્યૂમ"</string> + <string name="volume_alarm" msgid="4486241060751798448">"અલાર્મ વૉલ્યૂમ"</string> <string name="volume_notification" msgid="6864412249031660057">"સૂચના વૉલ્યૂમ"</string> <string name="volume_unknown" msgid="4041914008166576293">"વૉલ્યૂમ"</string> <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"બ્લૂટૂથ વૉલ્યૂમ"</string> @@ -1513,7 +1513,7 @@ <string name="storage_usb_drive" msgid="448030813201444573">"USB ડ્રાઇવ"</string> <string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ડ્રાઇવ"</string> <string name="storage_usb" msgid="2391213347883616886">"USB સ્ટોરેજ"</string> - <string name="extract_edit_menu_button" msgid="63954536535863040">"સંપાદિત કરો"</string> + <string name="extract_edit_menu_button" msgid="63954536535863040">"ફેરફાર કરો"</string> <string name="data_usage_warning_title" msgid="9034893717078325845">"ડેટા ચેતવણી"</string> <string name="data_usage_warning_body" msgid="1669325367188029454">"તમે <xliff:g id="APP">%s</xliff:g> ડેટા વાપર્યો છે"</string> <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"મોબાઇલ ડેટાની મર્યાદા આવી ગઈ"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index b66d7cb3136e..34e1db5ef4ce 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -307,7 +307,7 @@ <string name="permgroupdesc_microphone" msgid="1047786732792487722">"hanganyag rögzítése"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Testmozgás"</string> <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"hozzáférés a testmozgási adatokhoz"</string> - <string name="permgrouplab_camera" msgid="9090413408963547706">"Fényképezőgép"</string> + <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string> <string name="permgroupdesc_camera" msgid="7585150538459320326">"fotók és videók készítése"</string> <string name="permgrouplab_calllog" msgid="7926834372073550288">"Hívásnaplók"</string> <string name="permgroupdesc_calllog" msgid="2026996642917801803">"hívásnapló olvasása és írása"</string> @@ -1900,8 +1900,8 @@ <string name="profile_encrypted_message" msgid="1128512616293157802">"A feloldáshoz koppintson rá"</string> <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Csatlakoztatva a(z) <xliff:g id="PRODUCT_NAME">%1$s</xliff:g> eszközhöz"</string> <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Koppintson ide a fájlok megtekintéséhez"</string> - <string name="pin_target" msgid="8036028973110156895">"Rögzítés"</string> - <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítése"</string> + <string name="pin_target" msgid="8036028973110156895">"Kitűzés"</string> + <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> kitűzése"</string> <string name="unpin_target" msgid="3963318576590204447">"Feloldás"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítésének feloldása"</string> <string name="app_info" msgid="6113278084877079851">"Alkalmazásinformáció"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index b531d02998e4..30b092d92a80 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1979,7 +1979,7 @@ <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Չհաջողվեց վերականգնել դյուրանցումը, քանի որ հավելվածների ստորագրությունները տարբեր են"</string> <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Չհաջողվեց վերականգնել դյուրանցումը"</string> <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Դյուրանցումն անջատված է"</string> - <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ՀԵՌԱՑՆԵԼ"</string> + <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ԱՊԱՏԵՂԱԴՐԵԼ"</string> <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ԲԱՑԵԼ"</string> <string name="harmful_app_warning_title" msgid="8794823880881113856">"Հայտնաբերվել է վնասաբեր հավելված"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index b9e7f4f8c955..7a36d13fbe9d 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1231,7 +1231,7 @@ <string name="volume_icon_description_notification" msgid="579091344110747279">"Volume pemberitahuan"</string> <string name="ringtone_default" msgid="9118299121288174597">"Nada dering default"</string> <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Default (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> - <string name="ringtone_silent" msgid="397111123930141876">"Tidak Ada"</string> + <string name="ringtone_silent" msgid="397111123930141876">"Tidak ada"</string> <string name="ringtone_picker_title" msgid="667342618626068253">"Nada dering"</string> <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Suara alarm"</string> <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Suara notifikasi"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 692547b2d581..61754b25f4f4 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1792,7 +1792,7 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Battery Saver:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Батареяны үнемдеу режимі:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string> <string name="battery_saver_description" msgid="6794188153647295212">"Батарея ұзағырақ жұмыс істеуі үшін, Battery Saver:\n\n• қараңғы тақырыпты қосады;\n•фондық жұмысты, кейбір визуалды әсерлерді және \"Ok Google\" сияқты басқа функцияларды өшіреді не шектейді."</string> <string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Data Saver функциясы кейбір қолданбаларға деректерді фондық режимде жіберуге және алуға жол бермейді. Ашық тұрған қолданба деректерді пайдаланады, бірақ шектеулі шамада (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Data Saver функциясын қосу керек пе?"</string> @@ -1912,7 +1912,7 @@ <string name="conference_call" msgid="5731633152336490471">"Конференциялық қоңырау"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"Қалқыма сөзкөмек"</string> <string name="app_category_game" msgid="4534216074910244790">"Ойындар"</string> - <string name="app_category_audio" msgid="8296029904794676222">"Музыка және аудиомазмұн"</string> + <string name="app_category_audio" msgid="8296029904794676222">"Музыка және аудио"</string> <string name="app_category_video" msgid="2590183854839565814">"Фильм және бейне"</string> <string name="app_category_image" msgid="7307840291864213007">"Суреттер және кескіндер"</string> <string name="app_category_social" msgid="2278269325488344054">"Әлеуметтік қолданба мен байланыс"</string> @@ -2000,7 +2000,7 @@ <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режим туралы хабарландыру"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея заряды азаюы мүмкін"</string> <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарея ұзаққа жетуі үшін, Battery Saver іске қосылды"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Battery Saver"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Батареяны үнемдеу режимі"</string> <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Battery Saver өшірілді"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Телефонның заряды жеткілікті. Функцияларға енді шектеу қойылмайды."</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Планшеттің заряды жеткілікті. Функцияларға енді шектеу қойылмайды."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index b939c1ac21ea..738e984aed49 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2000,7 +2000,7 @@ <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ការជូនដំណឹងព័ត៌មានរបស់មុខងារទម្លាប់"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ថ្មអាចនឹងអស់ មុនពេលសាកថ្មធម្មតា"</string> <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បានបើកដំណើរការកម្មវិធីសន្សំថ្ម ដើម្បីបង្កើនកម្រិតថាមពលថ្ម"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"កម្មវិធីសន្សំថ្ម"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"មុខងារសន្សំថ្ម"</string> <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"កម្មវិធីសន្សំថ្មត្រូវបានបិទ"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ទូរសព្ទមានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"ថេប្លេតមានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index edc4de685334..1517c17d9615 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1317,7 +1317,7 @@ <string name="console_running_notification_title" msgid="6087888939261635904">"Сериялык консоль иштетилди"</string> <string name="console_running_notification_message" msgid="7892751888125174039">"Майнаптуулугуна таасири тиет. Аны өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string> <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB портунда суюктук же урандылар бар"</string> - <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string> + <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчтү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string> <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"USB портун колдонууга болот"</string> <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Телефон суюктук менен урандыларды аныктаган жок."</string> <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Мүчүлүштүк тууралуу кабар алынууда…"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 6af5d5ee87ca..3833605fa78b 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1117,7 +1117,7 @@ <string name="yes" msgid="9069828999585032361">"ОК"</string> <string name="no" msgid="5122037903299899715">"Цуцлах"</string> <string name="dialog_alert_title" msgid="651856561974090712">"Анхаар"</string> - <string name="loading" msgid="3138021523725055037">"Ачааллаж байна..."</string> + <string name="loading" msgid="3138021523725055037">"Ачаалж байна..."</string> <string name="capital_on" msgid="2770685323900821829">"Идэвхтэй"</string> <string name="capital_off" msgid="7443704171014626777">"Идэвхгүй"</string> <string name="checked" msgid="9179896827054513119">"тэмдэглэсэн"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 95d5f8f3ab64..bae44fd801ba 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -711,7 +711,7 @@ <item msgid="6216981255272016212">"Aangepast"</item> </string-array> <string-array name="emailAddressTypes"> - <item msgid="7786349763648997741">"Thuis"</item> + <item msgid="7786349763648997741">"Privé"</item> <item msgid="435564470865989199">"Werk"</item> <item msgid="4199433197875490373">"Overig"</item> <item msgid="3233938986670468328">"Aangepast"</item> @@ -769,7 +769,7 @@ <string name="eventTypeAnniversary" msgid="4684702412407916888">"Jubileum"</string> <string name="eventTypeOther" msgid="530671238533887997">"Overig"</string> <string name="emailTypeCustom" msgid="1809435350482181786">"Aangepast"</string> - <string name="emailTypeHome" msgid="1597116303154775999">"Thuis"</string> + <string name="emailTypeHome" msgid="1597116303154775999">"Privé"</string> <string name="emailTypeWork" msgid="2020095414401882111">"Werk"</string> <string name="emailTypeOther" msgid="5131130857030897465">"Overig"</string> <string name="emailTypeMobile" msgid="787155077375364230">"Mobiel"</string> @@ -931,9 +931,9 @@ <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nWeet u zeker dat u deze pagina wilt verlaten?"</string> <string name="save_password_label" msgid="9161712335355510035">"Bevestigen"</string> <string name="double_tap_toast" msgid="7065519579174882778">"Tip: dubbeltik om in en uit te zoomen."</string> - <string name="autofill_this_form" msgid="3187132440451621492">"Autom. aanvullen"</string> - <string name="setup_autofill" msgid="5431369130866618567">"Autom. aanvullen instellen"</string> - <string name="autofill_window_title" msgid="4379134104008111961">"Automatisch aanvullen met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> + <string name="autofill_this_form" msgid="3187132440451621492">"Autom. invullen"</string> + <string name="setup_autofill" msgid="5431369130866618567">"Autom. invullen instellen"</string> + <string name="autofill_window_title" msgid="4379134104008111961">"Automatisch invullen met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> <string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string> <string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="760522655085707045">", "</string> @@ -1101,7 +1101,7 @@ <string name="selectTextMode" msgid="3225108910999318778">"Tekst selecteren"</string> <string name="undo" msgid="3175318090002654673">"Ongedaan maken"</string> <string name="redo" msgid="7231448494008532233">"Opnieuw"</string> - <string name="autofill" msgid="511224882647795296">"Automatisch aanvullen"</string> + <string name="autofill" msgid="511224882647795296">"Automatisch invullen"</string> <string name="textSelectionCABTitle" msgid="5151441579532476940">"Tekstselectie"</string> <string name="addToDictionary" msgid="8041821113480950096">"Toevoegen aan woordenboek"</string> <string name="deleteText" msgid="4200807474529938112">"Verwijderen"</string> @@ -1276,7 +1276,7 @@ <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nooit toestaan"</string> <string name="sim_removed_title" msgid="5387212933992546283">"Simkaart verwijderd"</string> <string name="sim_removed_message" msgid="9051174064474904617">"Het mobiele netwerk is niet beschikbaar totdat u het apparaat opnieuw start met een geldige simkaart."</string> - <string name="sim_done_button" msgid="6464250841528410598">"Gereed"</string> + <string name="sim_done_button" msgid="6464250841528410598">"Klaar"</string> <string name="sim_added_title" msgid="7930779986759414595">"Simkaart aangesloten"</string> <string name="sim_added_message" msgid="6602906609509958680">"Start je apparaat opnieuw voor toegang tot het mobiele netwerk."</string> <string name="sim_restart_button" msgid="8481803851341190038">"Opnieuw starten"</string> @@ -1289,7 +1289,7 @@ <string name="time_picker_dialog_title" msgid="9053376764985220821">"Tijd instellen"</string> <string name="date_picker_dialog_title" msgid="5030520449243071926">"Datum instellen"</string> <string name="date_time_set" msgid="4603445265164486816">"Instellen"</string> - <string name="date_time_done" msgid="8363155889402873463">"Gereed"</string> + <string name="date_time_done" msgid="8363155889402873463">"Klaar"</string> <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NIEUW: "</font></string> <string name="perms_description_app" msgid="2747752389870161996">"Geleverd door <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="no_permissions" msgid="5729199278862516390">"Geen rechten nodig"</string> @@ -1376,7 +1376,7 @@ <string name="ext_media_status_removed" msgid="241223931135751691">"Verwijderd"</string> <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Uitgeworpen"</string> <string name="ext_media_status_checking" msgid="159013362442090347">"Controleren…"</string> - <string name="ext_media_status_mounted" msgid="3459448555811203459">"Gereed"</string> + <string name="ext_media_status_mounted" msgid="3459448555811203459">"Klaar"</string> <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Alleen lezen"</string> <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Onveilig verwijderd"</string> <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Beschadigd"</string> @@ -1401,7 +1401,7 @@ <string name="ime_action_search" msgid="4501435960587287668">"Zoeken"</string> <string name="ime_action_send" msgid="8456843745664334138">"Verzenden"</string> <string name="ime_action_next" msgid="4169702997635728543">"Volgende"</string> - <string name="ime_action_done" msgid="6299921014822891569">"Gereed"</string> + <string name="ime_action_done" msgid="6299921014822891569">"Klaar"</string> <string name="ime_action_previous" msgid="6548799326860401611">"Vorige"</string> <string name="ime_action_default" msgid="8265027027659800121">"Uitvoeren"</string> <string name="dial_number_using" msgid="6060769078933953531">"Nummer bellen\nmet <xliff:g id="NUMBER">%s</xliff:g>"</string> @@ -1448,7 +1448,7 @@ <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> van <xliff:g id="TOTAL">%d</xliff:g></item> <item quantity="one">1 overeenkomst</item> </plurals> - <string name="action_mode_done" msgid="2536182504764803222">"Gereed"</string> + <string name="action_mode_done" msgid="2536182504764803222">"Klaar"</string> <string name="progress_erasing" msgid="6891435992721028004">"Gedeelde opslag wissen…"</string> <string name="share" msgid="4157615043345227321">"Delen"</string> <string name="find" msgid="5015737188624767706">"Vinden"</string> @@ -1492,7 +1492,7 @@ <string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string> <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Annuleren"</string> <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Delete"</string> - <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Gereed"</string> + <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Klaar"</string> <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Modus wijzigen"</string> <string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string> <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string> @@ -1645,7 +1645,7 @@ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string> <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgeschakeld"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string> - <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gereed"</string> + <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string> <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Sneltoets uitschakelen"</string> <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string> @@ -1774,7 +1774,7 @@ <string name="immersive_cling_title" msgid="2307034298721541791">"Volledig scherm wordt weergegeven"</string> <string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string> <string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string> - <string name="done_label" msgid="7283767013231718521">"Gereed"</string> + <string name="done_label" msgid="7283767013231718521">"Klaar"</string> <string name="hour_picker_description" msgid="5153757582093524635">"Ronde schuifregelaar voor uren"</string> <string name="minute_picker_description" msgid="9029797023621927294">"Ronde schuifregelaar voor minuten"</string> <string name="select_hours" msgid="5982889657313147347">"Uren selecteren"</string> @@ -1928,13 +1928,13 @@ <string name="time_picker_prompt_label" msgid="303588544656363889">"Typ een tijd"</string> <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Schakel naar de tekstinvoermodus om de tijd in te voeren."</string> <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Schakel naar de klokmodus om de tijd in te voeren."</string> - <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opties voor automatisch aanvullen"</string> - <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Opslaan voor Automatisch aanvullen"</string> + <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opties voor automatisch invullen"</string> + <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Opslaan voor Automatisch invullen"</string> <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Content kan niet automatisch worden aangevuld"</string> - <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Geen suggesties van Automatisch aanvullen"</string> + <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Geen suggesties van Automatisch invullen"</string> <plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978"> - <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch aanvullen</item> - <item quantity="one">Eén suggestie van Automatisch aanvullen</item> + <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch invullen</item> + <item quantity="one">Eén suggestie van Automatisch invullen</item> </plurals> <string name="autofill_save_title" msgid="7719802414283739775">"Opslaan in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> <string name="autofill_save_title_with_type" msgid="3002460014579799605">"<xliff:g id="TYPE">%1$s</xliff:g> opslaan in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index dc63de6bbc7a..b9d041ec67f6 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -896,7 +896,7 @@ <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"ਅਣਲਾਕ ਖੇਤਰ ਨਸ਼ਟ ਕੀਤਾ।"</string> <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ਵਿਜੇਟ।"</string> <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"ਉਪਭੋਗਤਾ ਚੋਣਕਾਰ"</string> - <string name="keyguard_accessibility_status" msgid="6792745049712397237">"ਅਵਸਥਾ"</string> + <string name="keyguard_accessibility_status" msgid="6792745049712397237">"ਸਥਿਤੀ"</string> <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"ਕੈਮਰਾ"</string> <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"ਮੀਡੀਆ ਨਿਯੰਤਰਣ"</string> <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"ਵਿਜੇਟ ਨੂੰ ਪੁਨਰ ਤਰਤੀਬ ਦੇਣਾ ਸ਼ੁਰੂ ਹੋਇਆ।"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 30d3afead0b6..35b5b3a88782 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -241,7 +241,7 @@ <string name="global_action_power_off" msgid="4404936470711393203">"Искључи"</string> <string name="global_action_power_options" msgid="1185286119330160073">"Напајање"</string> <string name="global_action_restart" msgid="4678451019561687074">"Рестартуј"</string> - <string name="global_action_emergency" msgid="1387617624177105088">"Хитни позив"</string> + <string name="global_action_emergency" msgid="1387617624177105088">"Хитан позив"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"Извештај о грешци"</string> <string name="global_action_logout" msgid="6093581310002476511">"Заврши сесију"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"Снимак екрана"</string> @@ -832,7 +832,7 @@ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string> - <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитни позив"</string> + <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитан позив"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index fe647cb70d4c..279c619c65fc 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1829,7 +1829,7 @@ <item quantity="other">I %d tim</item> <item quantity="one">I en 1 tim</item> </plurals> - <string name="zen_mode_until" msgid="2250286190237669079">"Till kl. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> + <string name="zen_mode_until" msgid="2250286190237669079">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> <string name="zen_mode_alarm" msgid="7046911727540499275">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string> <string name="zen_mode_forever" msgid="740585666364912448">"Tills du stänger av"</string> <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Tills du inaktiverar Stör ej"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 8276905e2ef0..e168ae44a2d0 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -303,7 +303,7 @@ <string name="permgroupdesc_sms" msgid="5726462398070064542">"itume na iangalie SMS"</string> <string name="permgrouplab_storage" msgid="1938416135375282333">"Faili na maudhui"</string> <string name="permgroupdesc_storage" msgid="6351503740613026600">"ifikie picha, maudhui na faili kwenye kifaa chako"</string> - <string name="permgrouplab_microphone" msgid="2480597427667420076">"Kipokea sauti"</string> + <string name="permgrouplab_microphone" msgid="2480597427667420076">"Maikrofoni"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"irekodi sauti"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Shughuli za kimwili"</string> <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ifikie shughuli zako za kimwili"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 173a8ac900f1..05e00f2a2b6f 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1793,7 +1793,7 @@ <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi\n\n"<annotation id="url">"Batafsil"</annotation></string> - <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi"</string> + <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi"</string> <string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a1723c0716de..186af6ea5a3a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2781,6 +2781,7 @@ <item>power</item> <item>restart</item> <item>logout</item> + <item>screenshot</item> <item>bugreport</item> </string-array> @@ -3484,6 +3485,7 @@ mode --> <string-array translatable="false" name="config_priorityOnlyDndExemptPackages"> <item>com.android.dialer</item> + <item>com.android.server.telecom</item> <item>com.android.systemui</item> <item>android</item> </string-array> diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index c7d835ca7c7e..576cd78606d7 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -124,6 +124,24 @@ public class InsetsStateTest { } @Test + public void testCalculateInsets_extraNavRightClimateTop() throws Exception { + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_CLIMATE_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_CLIMATE_BAR).setVisible(true); + mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); + mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, + false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null); + // ITYPE_CLIMATE_BAR is a type of status bar and ITYPE_EXTRA_NAVIGATION_BAR is a type + // of navigation bar. + assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); + assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars())); + assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars())); + } + } + + @Test public void testCalculateInsets_imeIgnoredWithoutAdjustResize() { try (final InsetsModeSession session = new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { @@ -331,6 +349,8 @@ public class InsetsStateTest { public void testGetDefaultVisibility() { assertTrue(InsetsState.getDefaultVisibility(ITYPE_STATUS_BAR)); assertTrue(InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR)); + assertTrue(InsetsState.getDefaultVisibility(ITYPE_CLIMATE_BAR)); + assertTrue(InsetsState.getDefaultVisibility(ITYPE_EXTRA_NAVIGATION_BAR)); assertTrue(InsetsState.getDefaultVisibility(ITYPE_CAPTION_BAR)); assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME)); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index e08e42aec391..5f34a5eb58a1 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -224,6 +224,7 @@ applications that come with the platform <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.UPDATE_DEVICE_STATS"/> + <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> </privapp-permissions> <privapp-permissions package="com.android.providers.media.module"> diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index a362bd220936..667a7517a24c 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -139,7 +139,7 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { +static void setBufferCount(ANativeWindow* window) { int query_value; int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { @@ -148,7 +148,9 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { } auto min_undequeued_buffers = static_cast<uint32_t>(query_value); - int bufferCount = min_undequeued_buffers + 2 + extraBuffers; + // We only need to set min_undequeued + 2 because the renderahead amount was already factored into the + // query for min_undequeued + int bufferCount = min_undequeued_buffers + 2; native_window_set_buffer_count(window, bufferCount); } @@ -179,7 +181,8 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior); if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) { - setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity); + setBufferCount(mNativeSurface->getNativeWindow()); + } mFrameNumber = -1; diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 75ea0cbada92..7ed5b579ebb4 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -45,9 +45,9 @@ import com.android.internal.location.ProviderProperties; interface ILocationManager { Location getLastLocation(in LocationRequest request, String packageName, String featureId); - boolean getCurrentLocation(in LocationRequest request, - in ICancellationSignal cancellationSignal, in ILocationListener listener, - String packageName, String featureId, String listenerId); + @nullable ICancellationSignal getCurrentLocation(in LocationRequest request, + in ILocationListener listener, String packageName, String featureId, + String listenerId); void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, in PendingIntent intent, String packageName, String featureId, String listenerId); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 05a9863cb266..6bf6034bbbf4 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -726,16 +726,15 @@ public class LocationManager { cancellationSignal.throwIfCanceled(); } - ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport(); - try { - if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal, - transport, mContext.getPackageName(), mContext.getAttributionTag(), - transport.getListenerId())) { + ICancellationSignal cancelRemote = mService.getCurrentLocation( + currentLocationRequest, transport, mContext.getPackageName(), + mContext.getAttributionTag(), transport.getListenerId()); + if (cancelRemote != null) { transport.register(mContext.getSystemService(AlarmManager.class), - remoteCancellationSignal); + cancellationSignal); if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener(transport::cancel); + cancellationSignal.setRemote(cancelRemote); } } else { transport.fail(); @@ -2540,7 +2539,7 @@ public class LocationManager { } private static class GetCurrentLocationTransport extends ILocationListener.Stub implements - AlarmManager.OnAlarmListener { + AlarmManager.OnAlarmListener, CancellationSignal.OnCancelListener { @GuardedBy("this") @Nullable @@ -2572,7 +2571,7 @@ public class LocationManager { } public synchronized void register(AlarmManager alarmManager, - ICancellationSignal remoteCancellationSignal) { + CancellationSignal cancellationSignal) { if (mConsumer == null) { return; } @@ -2585,16 +2584,18 @@ public class LocationManager { this, null); - mRemoteCancellationSignal = remoteCancellationSignal; + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(this); + } } - public void cancel() { + @Override + public void onCancel() { remove(); } private Consumer<Location> remove() { Consumer<Location> consumer; - ICancellationSignal cancellationSignal; synchronized (this) { mExecutor = null; consumer = mConsumer; @@ -2604,18 +2605,6 @@ public class LocationManager { mAlarmManager.cancel(this); mAlarmManager = null; } - - // ensure only one cancel event will go through - cancellationSignal = mRemoteCancellationSignal; - mRemoteCancellationSignal = null; - } - - if (cancellationSignal != null) { - try { - cancellationSignal.cancel(); - } catch (RemoteException e) { - // ignore - } } return consumer; @@ -2637,11 +2626,6 @@ public class LocationManager { @Override public void onLocationChanged(Location location) { - synchronized (this) { - // save ourselves a pointless x-process call to cancel the location request - mRemoteCancellationSignal = null; - } - deliverResult(location); } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 1c0a526f536c..de2a7b2c15e3 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -807,7 +807,8 @@ public class AudioTrack extends PlayerBase int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/, - offload, encapsulationMode, tunerConfiguration); + offload, encapsulationMode, tunerConfiguration, + getCurrentOpPackageName()); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -893,7 +894,8 @@ public class AudioTrack extends PlayerBase nativeTrackInJavaObj, false /*offload*/, ENCAPSULATION_MODE_NONE, - null /* tunerConfiguration */); + null /* tunerConfiguration */, + "" /* opPackagename */); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -4062,7 +4064,8 @@ public class AudioTrack extends PlayerBase Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack, - boolean offload, int encapsulationMode, Object tunerConfiguration); + boolean offload, int encapsulationMode, Object tunerConfiguration, + @NonNull String opPackageName); private native final void native_finalize(); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 49e416080041..36ae3ec75a99 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -672,7 +672,8 @@ public class MediaPlayer extends PlayerBase /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ - native_setup(new WeakReference<MediaPlayer>(this)); + native_setup(new WeakReference<MediaPlayer>(this), + getCurrentOpPackageName()); baseRegisterPlayer(); } @@ -2378,7 +2379,7 @@ public class MediaPlayer extends PlayerBase private native final int native_setMetadataFilter(Parcel request); private static native final void native_init(); - private native final void native_setup(Object mediaplayer_this); + private native void native_setup(Object mediaplayerThis, @NonNull String opPackageName); private native final void native_finalize(); /** diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index ee8f1b3eec77..df5e85edbc30 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -27,6 +27,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -622,4 +623,8 @@ public abstract class PlayerBase { Log.w(className, "See the documentation of " + opName + " for what to use instead with " + "android.media.AudioAttributes to qualify your playback use case"); } + + protected String getCurrentOpPackageName() { + return TextUtils.emptyIfNull(ActivityThread.currentOpPackageName()); + } } diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 5cb42a9a96cc..1516c5152054 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -33,6 +33,7 @@ #include <utils/threads.h> #include "jni.h" #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/Log.h" @@ -944,10 +945,12 @@ android_media_MediaPlayer_native_init(JNIEnv *env) } static void -android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) +android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jstring opPackageName) { ALOGV("native_setup"); - sp<MediaPlayer> mp = new MediaPlayer(); + ScopedUtfChars opPackageNameStr(env, opPackageName); + sp<MediaPlayer> mp = new MediaPlayer(opPackageNameStr.c_str()); if (mp == NULL) { jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; @@ -1403,7 +1406,7 @@ static const JNINativeMethod gMethods[] = { {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter}, {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata}, {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, - {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml index 8b235e6f246e..d57875f8daba 100644 --- a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -53,7 +53,7 @@ android:textColor="?attr/wallpaperTextColor" android:textAppearance="?android:attr/textAppearanceMedium" android:imeOptions="flagForceAscii|actionDone" - android:maxLength="@integer/password_text_view_scale" + android:maxLength="@integer/password_max_length" /> <TextView diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 2c9788955bfa..e7295aa6383d 100644 --- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml @@ -25,7 +25,8 @@ <ViewStub android:id="@+id/notification_panel_stub" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout="@layout/notification_panel_container"/> + android:layout="@layout/notification_panel_container" + android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/> <ViewStub android:id="@+id/keyguard_stub" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 039f2c039ded..24157f9e2038 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -53,6 +53,16 @@ <integer name="config_rightSystemBarZOrder">0</integer> <integer name="config_bottomSystemBarZOrder">10</integer> + <!-- If set to true, the corresponding system bar will be hidden when Keyboard (IME) appears. + NOTE: hideBottomSystemBarKeyboard must not be overlaid directly here. To change its value, + overlay config_automotiveHideNavBarForKeyboard in framework/base/core/res/res. --> + <bool name="config_hideTopSystemBarForKeyboard">false</bool> + <bool name="config_hideBottomSystemBarForKeyboard"> + @*android:bool/config_automotiveHideNavBarForKeyboard + </bool> + <bool name="config_hideLeftSystemBarForKeyboard">false</bool> + <bool name="config_hideRightSystemBarForKeyboard">false</bool> + <!-- Disable normal notification rendering; we handle that ourselves --> <bool name="config_renderNotifications">false</bool> diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 8359dac6a30f..e8ac9e14eca1 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -194,6 +194,12 @@ <dimen name="car_navigation_bar_width">760dp</dimen> <dimen name="car_left_navigation_bar_width">96dp</dimen> <dimen name="car_right_navigation_bar_width">96dp</dimen> + <!-- In order to change the height of the bottom nav bar, overlay navigation_bar_height in + frameworks/base/core/res/res instead. --> + <dimen name="car_bottom_navigation_bar_height">@*android:dimen/navigation_bar_height</dimen> + <!-- In order to change the height of the top nav bar, overlay status_bar_height in + frameworks/base/core/res/res instead. --> + <dimen name="car_top_navigation_bar_height">@*android:dimen/status_bar_height</dimen> <dimen name="car_user_switcher_container_height">420dp</dimen> <!-- This must be the negative of car_user_switcher_container_height for the animation. --> diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java index 9584850fde7c..b6d251fbfe16 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java @@ -91,7 +91,11 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks private ActivityManagerWrapper mActivityManagerWrapper; // If the nav bar should be hidden when the soft keyboard is visible. - private boolean mHideNavBarForKeyboard; + private boolean mHideTopBarForKeyboard; + private boolean mHideLeftBarForKeyboard; + private boolean mHideRightBarForKeyboard; + private boolean mHideBottomBarForKeyboard; + private boolean mBottomNavBarVisible; // Nav bar views. @@ -160,8 +164,13 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks @Override public void start() { // Set initial state. - mHideNavBarForKeyboard = mResources.getBoolean( - com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); + mHideTopBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP); + mHideBottomBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide( + SystemBarConfigs.BOTTOM); + mHideLeftBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.LEFT); + mHideRightBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide( + SystemBarConfigs.RIGHT); + mBottomNavBarVisible = false; // Connect into the status bar manager service @@ -407,17 +416,30 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { - if (!mHideNavBarForKeyboard) { - return; - } - if (mContext.getDisplayId() != displayId) { return; } boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; - mCarNavigationBarController.setBottomWindowVisibility( - isKeyboardVisible ? View.GONE : View.VISIBLE); + + if (mHideTopBarForKeyboard) { + mCarNavigationBarController.setTopWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } + + if (mHideBottomBarForKeyboard) { + mCarNavigationBarController.setBottomWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } + + if (mHideLeftBarForKeyboard) { + mCarNavigationBarController.setLeftWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } + if (mHideRightBarForKeyboard) { + mCarNavigationBarController.setRightWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); + } } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java index fe26040c5eae..e522d19249e3 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java @@ -79,9 +79,7 @@ public class CarNavigationBarController { * Hides all system bars. */ public void hideBars() { - if (mTopView != null) { - mTopView.setVisibility(View.GONE); - } + setTopWindowVisibility(View.GONE); setBottomWindowVisibility(View.GONE); setLeftWindowVisibility(View.GONE); setRightWindowVisibility(View.GONE); @@ -91,9 +89,7 @@ public class CarNavigationBarController { * Shows all system bars. */ public void showBars() { - if (mTopView != null) { - mTopView.setVisibility(View.VISIBLE); - } + setTopWindowVisibility(View.VISIBLE); setBottomWindowVisibility(View.VISIBLE); setLeftWindowVisibility(View.VISIBLE); setRightWindowVisibility(View.VISIBLE); @@ -135,6 +131,11 @@ public class CarNavigationBarController { return mShowRight ? mNavigationBarViewFactory.getRightWindow() : null; } + /** Toggles the top nav bar visibility. */ + public boolean setTopWindowVisibility(@View.Visibility int visibility) { + return setWindowVisibility(getTopWindow(), visibility); + } + /** Toggles the bottom nav bar visibility. */ public boolean setBottomWindowVisibility(@View.Visibility int visibility) { return setWindowVisibility(getBottomWindow(), visibility); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java index e7d31949eb24..e72df6bcf28f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java @@ -98,6 +98,7 @@ public class SystemBarConfigs { readConfigs(); checkEnabledBarsHaveUniqueBarTypes(); checkSystemBarEnabledForNotificationPanel(); + checkHideBottomBarForKeyboardConfigSync(); setInsetPaddingsForOverlappingCorners(); sortSystemBarSidesByZOrder(); } @@ -122,6 +123,11 @@ public class SystemBarConfigs { } } + protected boolean getHideForKeyboardBySide(@SystemBarSide int side) { + return mSystemBarConfigMap.get(side) != null + && mSystemBarConfigMap.get(side).getHideForKeyboard(); + } + protected void insetSystemBar(@SystemBarSide int side, CarNavigationBarView view) { int[] paddings = mSystemBarConfigMap.get(side).getPaddings(); view.setPadding(paddings[2], paddings[0], paddings[3], paddings[1]); @@ -164,9 +170,11 @@ public class SystemBarConfigs { new SystemBarConfigBuilder() .setSide(TOP) .setGirth(mResources.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height)) + R.dimen.car_top_navigation_bar_height)) .setBarType(mResources.getInteger(R.integer.config_topSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideTopSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(TOP, topBarConfig); } @@ -176,10 +184,12 @@ public class SystemBarConfigs { new SystemBarConfigBuilder() .setSide(BOTTOM) .setGirth(mResources.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height)) + R.dimen.car_bottom_navigation_bar_height)) .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType)) .setZOrder( mResources.getInteger(R.integer.config_bottomSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideBottomSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(BOTTOM, bottomBarConfig); } @@ -192,6 +202,8 @@ public class SystemBarConfigs { R.dimen.car_left_navigation_bar_width)) .setBarType(mResources.getInteger(R.integer.config_leftSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_leftSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideLeftSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(LEFT, leftBarConfig); } @@ -204,6 +216,8 @@ public class SystemBarConfigs { R.dimen.car_right_navigation_bar_width)) .setBarType(mResources.getInteger(R.integer.config_rightSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_rightSystemBarZOrder)) + .setHideForKeyboard(mResources.getBoolean( + R.bool.config_hideRightSystemBarForKeyboard)) .build(); mSystemBarConfigMap.put(RIGHT, rightBarConfig); } @@ -239,19 +253,37 @@ public class SystemBarConfigs { e.printStackTrace(); } - if (!mTopNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom( - TopNotificationPanelViewMediator.class)) { + if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom( + notificationPanelMediatorUsed)) { throw new RuntimeException( "Top System Bar must be enabled to use " + notificationPanelMediatorName); } - if (!mBottomNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom( - BottomNotificationPanelViewMediator.class)) { + if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom( + notificationPanelMediatorUsed)) { throw new RuntimeException("Bottom System Bar must be enabled to use " + notificationPanelMediatorName); } } + private void checkHideBottomBarForKeyboardConfigSync() throws RuntimeException { + if (mBottomNavBarEnabled) { + boolean actual = mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard); + boolean expected = mResources.getBoolean( + com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard); + + if (actual != expected) { + throw new RuntimeException("config_hideBottomSystemBarForKeyboard must not be " + + "overlaid directly and should always refer to" + + "config_automotiveHideNavBarForKeyboard. However, their values " + + "currently do not sync. Set config_hideBottomSystemBarForKeyguard to " + + "@*android:bool/config_automotiveHideNavBarForKeyboard. To change its " + + "value, overlay config_automotiveHideNavBarForKeyboard in " + + "framework/base/core/res/res."); + } + } + } + private void setInsetPaddingsForOverlappingCorners() { setInsetPaddingForOverlappingCorner(TOP, LEFT); setInsetPaddingForOverlappingCorner(TOP, RIGHT); @@ -320,14 +352,17 @@ public class SystemBarConfigs { private final int mBarType; private final int mGirth; private final int mZOrder; + private final boolean mHideForKeyboard; private int[] mPaddings = new int[]{0, 0, 0, 0}; - private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder) { + private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder, + boolean hideForKeyboard) { mSide = side; mBarType = barType; mGirth = girth; mZOrder = zOrder; + mHideForKeyboard = hideForKeyboard; } private int getSide() { @@ -346,6 +381,10 @@ public class SystemBarConfigs { return mZOrder; } + private boolean getHideForKeyboard() { + return mHideForKeyboard; + } + private int[] getPaddings() { return mPaddings; } @@ -383,6 +422,7 @@ public class SystemBarConfigs { private int mBarType; private int mGirth; private int mZOrder; + private boolean mHideForKeyboard; private SystemBarConfigBuilder setSide(@SystemBarSide int side) { mSide = side; @@ -404,8 +444,13 @@ public class SystemBarConfigs { return this; } + private SystemBarConfigBuilder setHideForKeyboard(boolean hide) { + mHideForKeyboard = hide; + return this; + } + private SystemBarConfig build() { - return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder); + return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard); } } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index 7cd559a11158..65de46d36dd0 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -16,8 +16,6 @@ package com.android.systemui.car.notification; -import static android.view.WindowInsets.Type.navigationBars; - import android.app.ActivityManager; import android.car.Car; import android.car.drivingstate.CarUxRestrictionsManager; @@ -25,6 +23,8 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.inputmethodservice.InputMethodService; +import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.GestureDetector; @@ -82,6 +82,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController private final StatusBarStateController mStatusBarStateController; private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen; private final NotificationVisibilityLogger mNotificationVisibilityLogger; + private final int mNavBarHeight; private float mInitialBackgroundAlpha; private float mBackgroundAlphaDiff; @@ -138,7 +139,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController mStatusBarStateController = statusBarStateController; mNotificationVisibilityLogger = notificationVisibilityLogger; + mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height); + mCommandQueue.addCallback(this); + // Notification background setup. mInitialBackgroundAlpha = (float) mResources.getInteger( R.integer.config_initialNotificationBackgroundAlpha) / 100; @@ -179,6 +183,21 @@ public class NotificationPanelViewController extends OverlayPanelViewController } } + @Override + public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { + if (mContext.getDisplayId() != displayId) { + return; + } + boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; + int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight; + ViewGroup container = (ViewGroup) getLayout(); + ViewGroup.MarginLayoutParams params = + (ViewGroup.MarginLayoutParams) container.getLayoutParams(); + params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin); + container.setLayoutParams(params); + } + // OverlayViewController @Override @@ -204,7 +223,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController @Override protected int getInsetTypesToFit() { - return navigationBars(); + return 0; } @Override diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java index 84c840477302..3fd0852bc0ff 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java @@ -251,6 +251,28 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { } @Test + public void testSetTopWindowVisibility_setTrue_isVisible() { + mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true); + mCarNavigationBar = createNavigationBarController(); + + ViewGroup window = mCarNavigationBar.getTopWindow(); + mCarNavigationBar.setTopWindowVisibility(View.VISIBLE); + + assertThat(window.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testSetTopWindowVisibility_setFalse_isGone() { + mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true); + mCarNavigationBar = createNavigationBarController(); + + ViewGroup window = mCarNavigationBar.getTopWindow(); + mCarNavigationBar.setTopWindowVisibility(View.GONE); + + assertThat(window.getVisibility()).isEqualTo(View.GONE); + } + + @Test public void testSetBottomWindowVisibility_setTrue_isVisible() { mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); mCarNavigationBar = createNavigationBarController(); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java index 8b1589913d1d..96f05045cd38 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java @@ -17,6 +17,7 @@ package com.android.systemui.car.navigationbar; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -31,7 +32,14 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarSystemUiTest; +import com.android.systemui.car.notification.NotificationPanelViewController; +import com.android.systemui.car.notification.NotificationPanelViewMediator; +import com.android.systemui.car.notification.PowerManagerHelper; +import com.android.systemui.car.notification.TopNotificationPanelViewMediator; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; @@ -92,6 +100,43 @@ public class SystemBarConfigsTest extends SysuiTestCase { mSystemBarConfigs = new SystemBarConfigs(mResources); } + @Test(expected = RuntimeException.class) + public void onInit_hideBottomSystemBarForKeyboardValueDoNotSync_throwsRuntimeException() { + when(mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard)).thenReturn(false); + when(mResources.getBoolean( + com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard)).thenReturn( + true); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + + @Test + public void onInit_topNotifPanelViewMediatorUsed_topBarEnabled_doesNotThrowException() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(true); + when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn( + TestTopNotificationPanelViewMediator.class.getName()); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + + @Test(expected = RuntimeException.class) + public void onInit_topNotifPanelViewMediatorUsed_topBarNotEnabled_throwsRuntimeException() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false); + when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn( + TestTopNotificationPanelViewMediator.class.getName()); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + + @Test + public void onInit_notificationPanelViewMediatorUsed_topBarNotEnabled_doesNotThrowException() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false); + when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn( + NotificationPanelViewMediator.class.getName()); + + mSystemBarConfigs = new SystemBarConfigs(mResources); + } + @Test public void getTopSystemBarLayoutParams_topBarEnabled_returnsTopSystemBarLayoutParams() { mSystemBarConfigs = new SystemBarConfigs(mResources); @@ -112,6 +157,26 @@ public class SystemBarConfigsTest extends SysuiTestCase { } @Test + public void getTopSystemBarHideForKeyboard_hideBarForKeyboard_returnsTrue() { + when(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).thenReturn(true); + mSystemBarConfigs = new SystemBarConfigs(mResources); + + boolean hideKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP); + + assertTrue(hideKeyboard); + } + + @Test + public void getTopSystemBarHideForKeyboard_topBarNotEnabled_returnsFalse() { + when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false); + mSystemBarConfigs = new SystemBarConfigs(mResources); + + boolean hideKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP); + + assertFalse(hideKeyboard); + } + + @Test public void topSystemBarHasHigherZOrderThanHuns_topSystemBarIsNavigationBarPanelType() { when(mResources.getInteger(R.integer.config_topSystemBarZOrder)).thenReturn( SystemBarConfigs.getHunZOrder() + 1); @@ -158,5 +223,30 @@ public class SystemBarConfigsTest extends SysuiTestCase { when(mResources.getInteger(R.integer.config_bottomSystemBarZOrder)).thenReturn(10); when(mResources.getInteger(R.integer.config_leftSystemBarZOrder)).thenReturn(2); when(mResources.getInteger(R.integer.config_rightSystemBarZOrder)).thenReturn(3); + + when(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).thenReturn(false); + when(mResources.getBoolean( + com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard)).thenReturn( + false); + when(mResources.getBoolean(R.bool.config_hideLeftSystemBarForKeyboard)).thenReturn( + false); + when(mResources.getBoolean(R.bool.config_hideRightSystemBarForKeyboard)).thenReturn( + false); + } + + // Intentionally using a subclass of TopNotificationPanelViewMediator for testing purposes to + // ensure that OEM's will be able to implement and use their own NotificationPanelViewMediator. + private class TestTopNotificationPanelViewMediator extends + TopNotificationPanelViewMediator { + TestTopNotificationPanelViewMediator( + CarNavigationBarController carNavigationBarController, + NotificationPanelViewController notificationPanelViewController, + PowerManagerHelper powerManagerHelper, + BroadcastDispatcher broadcastDispatcher, + CarDeviceProvisionedController carDeviceProvisionedController, + ConfigurationController configurationController) { + super(carNavigationBarController, notificationPanelViewController, powerManagerHelper, + broadcastDispatcher, carDeviceProvisionedController, configurationController); + } } } diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml index 87f89ce192c4..c840374d3f6b 100644 --- a/packages/PackageInstaller/res/values-ar/strings.xml +++ b/packages/PackageInstaller/res/values-ar/strings.xml @@ -57,7 +57,7 @@ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string> <string name="uninstall_update_text" msgid="863648314632448705">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string> - <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string> + <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد إعادة ضبط هذا التطبيق على الإعدادات الأصلية؟ سؤدي ذلك إلى إزالة جميع البيانات، كما سيؤثر على جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string> <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بالحجم <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"عمليات إلغاء التثبيت الجارية"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"عمليات إلغاء التثبيت غير الناجحة"</string> diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml index 108c86fe8369..d3a958922d1c 100644 --- a/packages/PackageInstaller/res/values-nl/strings.xml +++ b/packages/PackageInstaller/res/values-nl/strings.xml @@ -18,7 +18,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="7488448184431507488">"Pakket-installatie"</string> <string name="install" msgid="711829760615509273">"Installeren"</string> - <string name="done" msgid="6632441120016885253">"Gereed"</string> + <string name="done" msgid="6632441120016885253">"Klaar"</string> <string name="cancel" msgid="1018267193425558088">"Annuleren"</string> <string name="installing" msgid="4921993079741206516">"Installeren…"</string> <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeren…"</string> diff --git a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml index ea66717b1e87..82c8613b5c8c 100644 --- a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml +++ b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="help_feedback_label" msgid="7106780063063027882">"సహాయం & అభిప్రాయం"</string> + <string name="help_feedback_label" msgid="7106780063063027882">"సహాయం & ఫీడ్బ్యాక్"</string> </resources> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 9ca1814783b5..0b4538097e41 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -153,7 +153,7 @@ <string name="unknown" msgid="3544487229740637809">"غير معروف"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string> - <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string> + <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم ضبط إعدادات تلقائية"</string> <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string> <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"معدل سرعة الكلام"</string> @@ -245,7 +245,7 @@ <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"هل تريد السماح بإلغاء قفل المصنّع الأصلي للجهاز؟"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"تحذير: لن تعمل ميزات الحماية على هذا الجهاز أثناء تفعيل هذا الإعداد."</string> <string name="mock_location_app" msgid="6269380172542248304">"اختيار تطبيق الموقع الزائف"</string> - <string name="mock_location_app_not_set" msgid="6972032787262831155">"لم يتم تعيين تطبيق موقع زائف"</string> + <string name="mock_location_app_not_set" msgid="6972032787262831155">"لم يتم ضبط تطبيق موقع زائف"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"تطبيق الموقع الزائف: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"الشبكات"</string> <string name="wifi_display_certification" msgid="1805579519992520381">"شهادة عرض شاشة لاسلكي"</string> @@ -318,7 +318,7 @@ <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"تعيين سلوك التحقق من HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"تصحيح الأخطاء"</string> <string name="debug_app" msgid="8903350241392391766">"اختيار التطبيق لتصحيحه"</string> - <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم تعيين تطبيق لتصحيحه"</string> + <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم ضبط تطبيق لتصحيحه"</string> <string name="debug_app_set" msgid="6599535090477753651">"تطبيق التصحيح: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="select_application" msgid="2543228890535466325">"اختيار تطبيق"</string> <string name="no_application" msgid="9038334538870247690">"لا شيء"</string> @@ -381,9 +381,9 @@ <string name="local_backup_password_title" msgid="4631017948933578709">"كلمة مرور احتياطية للكمبيوتر"</string> <string name="local_backup_password_summary_none" msgid="7646898032616361714">"النُسخ الاحتياطية الكاملة لسطح المكتب غير محمية في الوقت الحالي"</string> <string name="local_backup_password_summary_change" msgid="1707357670383995567">"انقر لتغيير كلمة مرور النسخ الاحتياطية الكاملة لسطح المكتب أو إزالتها."</string> - <string name="local_backup_password_toast_success" msgid="4891666204428091604">"تم تعيين كلمة مرور احتياطية جديدة"</string> + <string name="local_backup_password_toast_success" msgid="4891666204428091604">"تم ضبط كلمة مرور احتياطية جديدة"</string> <string name="local_backup_password_toast_confirmation_mismatch" msgid="2994718182129097733">"كلمة المرور الجديدة وتأكيدها لا يتطابقان"</string> - <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"تعذّر تعيين كلمة مرور احتياطية"</string> + <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"تعذّر ضبط كلمة مرور احتياطية"</string> <string name="loading_injected_setting_summary" msgid="8394446285689070348">"جارٍ التحميل…"</string> <string-array name="color_mode_names"> <item msgid="3836559907767149216">"نابض بالحياة (تلقائي)"</item> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 5fc311af26b2..3373f815ebaf 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -211,8 +211,8 @@ <string name="adb_wireless_error" msgid="721958772149779856">"خطا"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"اشکالزدایی بیسیم"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"برای مشاهده و استفاده از دستگاههای در دسترس، اشکالزدایی بیسیم را روشن کنید"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با کد QR"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاههای جدید را بااستفاده از اسکنر کد QR مرتبط کنید"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با رمزینه پاسخسریع"</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاههای جدید را بااستفاده از اسکنر رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"مرتبط کردن دستگاه با کد مرتبطسازی"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"دستگاههای جدید را با استفاده از کد شش رقمی مرتبط کنید"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"دستگاههای مرتبطشده"</string> @@ -226,12 +226,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"کد مرتبطسازی Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"مرتبطسازی ناموفق"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"مطمئن شوید که دستگاه به همان شبکه متصل باشد."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن کد QR مرتبط کنید"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"مرتبطسازی دستگاه…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا کد QR اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا رمزینه پاسخسریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"نشانی IP و درگاه"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن کد QR"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن کد QR مرتبط کنید"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن رمزینه پاسخسریع"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"لطفاً به شبکه Wi-Fi متصل شوید"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"ADB (پل اشکالزدایی Android)، اشکالزدایی کردن، برنامهنویس"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"میانبر گزارش مشکل"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 3261f69b1105..894a14ac3b74 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -487,8 +487,8 @@ <string name="status_unavailable" msgid="5279036186589861608">"અનુપલબ્ધ"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MACને રેન્ડમ કરેલ છે"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> - <item quantity="one">%1$d ઉપકરણ કનેક્ટ કર્યું</item> - <item quantity="other">%1$d ઉપકરણો કનેક્ટ કર્યા</item> + <item quantity="one">%1$d ડિવાઇસ કનેક્ટ કર્યું</item> + <item quantity="other">%1$d ડિવાઇસ કનેક્ટ કર્યા</item> </plurals> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"વધુ સમય."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ઓછો સમય."</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 8b92640e9996..ffd95a45b61e 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -57,7 +57,7 @@ <string name="osu_sign_up_complete" msgid="7640183358878916847">"Գրանցումն ավարտված է: Միացում…"</string> <string name="speed_label_very_slow" msgid="8526005255731597666">"Շատ դանդաղ"</string> <string name="speed_label_slow" msgid="6069917670665664161">"Դանդաղ"</string> - <string name="speed_label_okay" msgid="1253594383880810424">"Հաստատել"</string> + <string name="speed_label_okay" msgid="1253594383880810424">"Լավ"</string> <string name="speed_label_medium" msgid="9078405312828606976">"Միջին"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Արագ"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Շատ արագ"</string> @@ -493,7 +493,7 @@ <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Ավելացնել ժամանակը:"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Պակասեցնել ժամանակը:"</string> <string name="cancel" msgid="5665114069455378395">"Չեղարկել"</string> - <string name="okay" msgid="949938843324579502">"Հաստատել"</string> + <string name="okay" msgid="949938843324579502">"Եղավ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Միացնել"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Միացրեք «Չանհանգստացնել» ռեժիմը"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Երբեք"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 60001a0b0ecd..e252eca5d72a 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -510,7 +510,7 @@ <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string> - <string name="help_label" msgid="3528360748637781274">"సహాయం & అభిప్రాయం"</string> + <string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string> <string name="storage_category" msgid="2287342585424631813">"నిల్వ"</string> <string name="shared_data_title" msgid="1017034836800864953">"షేర్ చేసిన డేటా"</string> <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, సవరించండి"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9d06c8467e41..72a6074ff89c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -465,7 +465,16 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); - mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + // Add disconnected bluetooth devices only when phone output device is available. + for (MediaDevice device : devices) { + final int type = device.getDeviceType(); + if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + break; + } + } } final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index a654fd47ea12..8e850b25159c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -585,6 +585,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -683,6 +684,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index efaef2191987..a311f0702a4b 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -165,7 +165,6 @@ public class SecureSettings { Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, Settings.Secure.PEOPLE_STRIP, Settings.Secure.MEDIA_CONTROLS_RESUME, - Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 6436355255a8..9c5b60065118 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -244,8 +244,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME_BLOCKED, - COLON_SEPARATED_PACKAGE_LIST_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE, new InclusiveIntegerRangeValidator( Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index b55854131709..6e74184cef02 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -113,6 +113,7 @@ <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MONITOR_INPUT" /> + <uses-permission android:name="android.permission.INPUT_CONSUMER" /> <!-- DreamManager --> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java index 7463d3fd3650..8f24e7927e3a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java @@ -75,6 +75,11 @@ public interface NotificationMenuRowPlugin extends Plugin { public MenuItem getLongpressMenuItem(Context context); /** + * @return the {@link MenuItem} to display when app ops icons are pressed. + */ + public MenuItem getAppOpsMenuItem(Context context); + + /** * @return the {@link MenuItem} to display when snooze item is pressed. */ public MenuItem getSnoozeMenuItem(Context context); diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index 6d86a78360d8..65e3f0dbd176 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -112,7 +112,7 @@ <string name="kg_pin_accepted" msgid="1625501841604389716">"تم قبول الرمز"</string> <string name="keyguard_carrier_default" msgid="6359808469637388586">"لا تتوفر خدمة."</string> <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string> - <string name="airplane_mode" msgid="2528005343938497866">"وضع الطائرة"</string> + <string name="airplane_mode" msgid="2528005343938497866">"وضع الطيران"</string> <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string> <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string> <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 24b5c23a6732..52b7fab768c5 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -100,7 +100,7 @@ <string name="kg_pin_accepted" msgid="1625501841604389716">"កូដត្រូវបានទទួលយក!"</string> <string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មានសេវាទេ។"</string> <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរវិធីបញ្ចូល"</string> - <string name="airplane_mode" msgid="2528005343938497866">"មុខងារពេលជិះយន្តហោះ"</string> + <string name="airplane_mode" msgid="2528005343938497866">"ពេលជិះយន្តហោះ"</string> <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 74386bc7a487..d44003bc5b95 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -134,7 +134,7 @@ <item quantity="other">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం కారియర్ను సంప్రదించండి.</item> <item quantity="one">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది వివరాల కోసం కారియర్ను సంప్రదించండి.</item> </plurals> - <string name="clock_title_default" msgid="6342735240617459864">"డిఫాల్ట్"</string> + <string name="clock_title_default" msgid="6342735240617459864">"ఆటోమేటిక్"</string> <string name="clock_title_bubble" msgid="2204559396790593213">"బబుల్"</string> <string name="clock_title_analog" msgid="8409262532900918273">"ఎనలాగ్"</string> </resources> diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index ae7f44d19430..b9e711e54b3b 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -50,7 +50,7 @@ <FrameLayout android:layout_width="match_parent" - android:layout_height="72dp"> + android:layout_height="@dimen/controls_management_footer_height"> <View android:layout_width="match_parent" @@ -61,7 +61,8 @@ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="@dimen/controls_management_footer_side_margin"> + android:paddingHorizontal="@dimen/controls_management_footer_side_margin" + android:paddingVertical="@dimen/controls_management_footer_top_margin" > <Button android:id="@+id/other_apps" diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml index 4850e7534943..0ddd0e38acb9 100644 --- a/packages/SystemUI/res/layout/controls_management_favorites.xml +++ b/packages/SystemUI/res/layout/controls_management_favorites.xml @@ -36,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="@dimen/controls_management_page_indicator_height" android:layout_gravity="center" - android:layout_marginTop="@dimen/controls_management_list_margin" + android:layout_marginTop="@dimen/controls_management_indicator_top_margin" android:visibility="invisible" /> <androidx.viewpager2.widget.ViewPager2 diff --git a/packages/SystemUI/res/layout/controls_structure_page.xml b/packages/SystemUI/res/layout/controls_structure_page.xml index f048d62d46d7..412ed566ff1b 100644 --- a/packages/SystemUI/res/layout/controls_structure_page.xml +++ b/packages/SystemUI/res/layout/controls_structure_page.xml @@ -21,4 +21,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:layout_marginTop="@dimen/controls_management_zone_top_margin"/>
\ No newline at end of file + android:layout_marginTop="@dimen/controls_management_favorites_top_margin"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 5fa5dc332c7a..18a1eba7a36b 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -318,7 +318,7 @@ <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"تم الوصول إلى حد البيانات الذي عيَّنته. لم يُعد بإمكانك استخدام بيانات الجوال.\n\nفي حالة الاستئناف، قد يتم تطبيق الرسوم لاستخدام البيانات."</string> <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"استئناف"</string> <string name="gps_notification_searching_text" msgid="231304732649348313">"جارٍ البحث عن GPS"</string> - <string name="gps_notification_found_text" msgid="3145873880174658526">"تم تعيين الموقع بواسطة GPS"</string> + <string name="gps_notification_found_text" msgid="3145873880174658526">"تم ضبط الموقع بواسطة GPS"</string> <string name="accessibility_location_active" msgid="2845747916764660369">"طلبات الموقع نشطة"</string> <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"أجهزة الاستشعار غير مفعّلة"</string> <string name="accessibility_clear_all" msgid="970525598287244592">"محو جميع الإشعارات."</string> @@ -697,7 +697,7 @@ <string name="tuner_full_importance_settings" msgid="1388025816553459059">"عناصر التحكم في إشعارات التشغيل"</string> <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"تشغيل"</string> <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"إيقاف"</string> - <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك تعيين مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string> + <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك ضبط مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string> <string name="notification_header_default_channel" msgid="225454696914642444">"الإشعارات"</string> <string name="notification_channel_disabled" msgid="928065923928416337">"لن تتلقى هذه الإشعارات بعد الآن."</string> <string name="notification_channel_minimized" msgid="6892672757877552959">"سيتم تصغير هذه الإشعارات."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 77c1606d4130..6fcc5cff69ef 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -720,7 +720,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя. Размовы ў праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартна паяўляюцца ў выглядзе ўсплывальных апавяшчэнняў."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Прыцягвае ўвагу да гэтага змесціва ўсплывальнай кнопкай."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў у выглядзе ўсплывальнага апавяшчэння, а на экране блакіроўкі – у выглядзе відарыса профілю"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў, як усплывальнае апавяшчэнне, паказвае фота профілю на экране блакіроўкі"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Налады"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае функцыі размовы"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6ff1bfcee29c..e24ece4b296a 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да звъни или да вибрира въз основа на настройките за телефона"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звъни или да вибрира въз основа на настройките за телефона. Разговорите от <xliff:g id="APP_NAME">%1$s</xliff:g> се показват като балончета по подразбиране."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Задържа вниманието ви посредством плаващ пряк път към това съдържание."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори и показва снимката на потребителския профил на заключения екран"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори, показва снимката на потр. профил на заключения екран"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддържа функциите за разговор"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 529e37a59e07..6acad2146534 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -92,10 +92,10 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Vols iniciar la gravació?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"Quan graves contingut, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou les contrasenyes, la informació de pagament, les fotos, els missatges i l\'àudio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"Durant la gravació, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou contrasenyes, informació de pagament, fotos, missatges i àudio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Àudio del dispositiu"</string> - <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons del dispositiu, com ara la música, les trucades i els sons de trucada"</string> + <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"So del dispositiu, com ara música, trucades i sons de trucada"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Micròfon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Àudio del dispositiu i micròfon"</string> <string name="screenrecord_start" msgid="330991441575775004">"Inicia"</string> @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"AUTOMÀTICA"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix els colors"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix colors"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"Mode de correcció de color"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string> <string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 9d661badd48e..0851910e7ea1 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -720,7 +720,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> ve výchozím nastavení bublají."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Přitahuje pozornost pomocí plovoucí zkratky k tomuto obsahu."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzace a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzací a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nastavení"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string> <string name="no_shortcut" msgid="8257177117568230126">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> funkce konverzace nepodporuje"</string> @@ -1033,7 +1033,7 @@ <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Pohotovostní režim"</string> <string name="priority_onboarding_title" msgid="2893070698479227616">"Konverzace byla nastavena jako prioritní"</string> <string name="priority_onboarding_behavior" msgid="5342816047020432929">"Chování prioritních konverzací:"</string> - <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Zobrazovat v horní části sekce konverzace"</string> + <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Zobrazovat v horní části sekce konverzací"</string> <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Zobrazovat profilovou fotku na zámku obrazovky"</string> <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Zobrazuje se jako plovoucí bublina nad aplikacemi"</string> <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Přerušit režim Nerušit"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 30f92c039377..9bd741517bcd 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringe eller vibrere baseret på telefonens indstillinger"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere baseret på telefonens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Fastholder din opmærksomhed med en svævende genvej til indholdet."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen, som en svævende boble og med profilbillede på låseskærmen"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen som en svævende boble og med profilbillede på låseskærmen"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Indstillinger"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> understøtter ikke samtalefunktioner"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 4137f8e75a26..76bd133853f0 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου. Οι συζητήσεις από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανίζονται σε συννεφάκι από προεπιλογή."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Κρατάει την προσοχή σας με μια κινούμενη συντόμευση προς αυτό το περιεχόμενο."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται στο επάνω μέρος της ενότητας συζητήσεων, προβάλλεται ως κινούμενο συννεφάκι, εμφανίζει τη φωτογραφία προφίλ στην οθόνη κλειδώματος"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται επάνω στις συζητήσεις, προβάλλεται ως κιν. συννεφάκι, εμφανίζει τη φωτ. προφίλ στην οθ. κλειδ."</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ρυθμίσεις"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Προτεραιότητα"</string> <string name="no_shortcut" msgid="8257177117568230126">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν υποστηρίζει τις λειτουργίες συζήτησης"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 3a52a7269186..e5fd43a66227 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 11e0fb9a1852..60576f2df09d 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 3a52a7269186..e5fd43a66227 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 3a52a7269186..e5fd43a66227 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index c27bdd0f2c0c..87a7f8fe59a6 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -706,17 +706,17 @@ <string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"Seguir recibiendo alertas"</string> <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactivar notificaciones"</string> <string name="inline_keep_showing_app" msgid="4393429060390649757">"¿Quieres seguir viendo las notificaciones de esta app?"</string> - <string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string> + <string name="notification_silence_title" msgid="8608090968400832335">"Silenciada"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Cuadro"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en una parte inferior de la sección de conversaciones"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono."</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puede sonar o vibrar en función de la configuración del teléfono. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Retiene tu atención con un acceso directo flotante a este contenido."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo."</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaria"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"No hay burbujas recientes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index aa4adaaf2fc5..ec6e79f9d02b 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -710,11 +710,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminado"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Burbuja"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración y se muestra más abajo en la sección de conversaciones"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración, y se muestra más abajo en la sección de conversaciones"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Es posible que suene o vibre según los ajustes del teléfono"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Es posible que suene o vibre según los ajustes del teléfono. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Llama tu atención con un acceso directo flotante a este contenido."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones en forma de burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones, como burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ajustes"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string> <string name="no_shortcut" msgid="8257177117568230126">"No se pueden usar funciones de conversación con <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 72e16203b4c7..853b5790d525 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -379,7 +379,7 @@ <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi on käytössä"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Ei Wi-Fi-verkkoja käytettävissä"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Otetaan käyttöön…"</string> - <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön suoratoisto"</string> + <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön striimaus"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"Lähetetään"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Nimetön laite"</string> <string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Valmis lähetystä varten"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 539da14d8e52..d22bd33f3f45 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -710,11 +710,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Configuración predeterminada"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Burbulla"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Non soa nin vibra, e aparece máis abaixo na sección de conversas"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podería soar ou vibrar en función da configuración do teléfono"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sen son nin vibración, e aparecen máis abaixo na sección de conversas"</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Poderían soar ou vibrar en función da configuración do teléfono"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Podería soar ou vibrar en función da configuración do teléfono. Conversas desde a burbulla da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantén a túa atención cun atallo flotante a este contido."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstrase na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstranse na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non admite funcións de conversa"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 1e58b04c6dfb..a5d806b27793 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -857,7 +857,7 @@ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ટાઇલને ફરીથી ગોઠવવા માટે આંગળી દબાવીને ખેંચો"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"દૂર કરવા માટે અહીં ખેંચો"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"તમને ઓછામાં ઓછી <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ટાઇલની જરૂર છે"</string> - <string name="qs_edit" msgid="5583565172803472437">"સંપાદિત કરો"</string> + <string name="qs_edit" msgid="5583565172803472437">"ફેરફાર કરો"</string> <string name="tuner_time" msgid="2450785840990529997">"સમય"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"કલાક, મિનિટ અને સેકન્ડ બતાવો"</item> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 24f2e6dfdd78..ae51e2e176b8 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -512,7 +512,7 @@ <string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string> <string name="notification_section_header_gentle" msgid="6804099527336337197">"बिना आवाज़ किए मिलने वाली सूचनाएं"</string> - <string name="notification_section_header_alerting" msgid="5581175033680477651">"वाइब्रेशन या आवाज़ के साथ मिलने वाली सूचनाएं"</string> + <string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"बातचीत"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string> @@ -712,11 +712,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"बबल"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और \'बातचीत\', सेक्शन में सबसे नीचे दिखती है"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और बातचीत, सेक्शन में सबसे नीचे दिखती है"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है. <xliff:g id="APP_NAME">%1$s</xliff:g> में होने वाली बातचीत, डिफ़ॉल्ट रूप से बबल के तौर पर दिखती है."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ़्लोट करने वाले शॉर्टकट की मदद से इस सामग्री पर आपका ध्यान बना रहता है."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत की सुविधा, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिंग"</string> <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर बातचीत की सुविधाएं काम नहीं करतीं"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 65f4325e515e..6c557443fb3b 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -511,7 +511,7 @@ <string name="notification_section_header_incoming" msgid="850925217908095197">"Նոր"</string> <string name="notification_section_header_gentle" msgid="6804099527336337197">"Անձայն"</string> <string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string> - <string name="notification_section_header_conversations" msgid="821834744538345661">"Խոսակցություններ"</string> + <string name="notification_section_header_conversations" msgid="821834744538345661">"Զրույցներ"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ջնջել բոլոր անձայն ծանուցումները"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string> <string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index ea0dd97c5b37..e7b0778f9b67 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -854,7 +854,7 @@ <string name="left_icon" msgid="5036278531966897006">"Ikon kiri"</string> <string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk mengatur ulang kartu"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index f185c193c952..3e6f37f7b4bc 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -853,10 +853,10 @@ <string name="right_keycode" msgid="2480715509844798438">"Lykiltákn til hægri"</string> <string name="left_icon" msgid="5036278531966897006">"Tákn til vinstri"</string> <string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við reitum"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string> - <string name="drag_to_remove_disabled" msgid="933046987838658850">"Reitirnir mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> + <string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Breyta"</string> <string name="tuner_time" msgid="2450785840990529997">"Tími"</string> <string-array name="clock_options"> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 6ee4415387b1..1d00c60b6a5e 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Può suonare o vibrare in base alle impostazioni del telefono"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Può suonare o vibrare in base alle impostazioni del telefono. Conversazioni dalla bolla <xliff:g id="APP_NAME">%1$s</xliff:g> per impostazione predefinita."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantiene la tua attenzione con una scorciatoia mobile a questi contenuti."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Viene mostrata in cima alla sezione delle conversazioni, appare sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Appare in cima alla sezione delle conversazioni e sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Impostazioni"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Priorità"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non supporta le funzionalità delle conversazioni"</string> @@ -987,7 +987,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Il Risparmio energetico verrà attivato automaticamente quando la carica della batteria sarà inferiore a <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Impostazioni"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"Esegui dump heap SysUI"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 0fbabcd9b43d..f41f0b56625a 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"იპყრობს თქვენს ყურადღებას ამ კონტენტის მოლივლივე მალსახმობით."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების სექციის ზედა ნაწილში მოლივლივე ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების ზედა ნაწილში ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"პარამეტრები"</string> <string name="notification_priority_title" msgid="2079708866333537093">"პრიორიტეტი"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს არ აქვს მიმოწერის ფუნქციების მხარდაჭერა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 0906c7163d69..a8b865597169 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -28,7 +28,7 @@ <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды"</string> <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Пайдалану барысына байланысты <xliff:g id="PERCENTAGE">%1$s</xliff:g> заряд, шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string> <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> заряд, шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string> - <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Battery Saver қосулы."</string> + <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Батареяны үнемдеу режимі қосулы."</string> <string name="invalid_charger" msgid="4370074072117767416">"USB арқылы зарядтау мүмкін емес. Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз."</string> <string name="invalid_charger_title" msgid="938685362320735167">"USB арқылы зарядтау мүмкін емес"</string> <string name="invalid_charger_text" msgid="2339310107232691577">"Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз"</string> @@ -419,7 +419,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> дейін"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Қараңғы тақырып"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Батареяны үнемдеу режимі"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Күн батқанда қосу"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string> @@ -710,13 +710,13 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Әдепкі"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Көпіршік"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс не діріл қолданылмайды"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, төменде әңгімелер бөлімінде шығады"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, әңгімелер бөлімінің төмен жағында шығады"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін. <xliff:g id="APP_NAME">%1$s</xliff:g> чаттары әдепкісінше қалқымалы етіп көрсетіледі."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Осы мазмұнға бекітілген қалқымалы таңбашамен назарыңызды өзіне тартады."</string> <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Әңгімелер бөлімінің жоғарғы жағында тұрады, қалқыма хабар түрінде шығады, құлыптаулы экранда профиль суретін көрсетеді"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Параметрлер"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Маңыздылығы"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Маңызды"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> әңгімелесу функцияларын қолдамайды."</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Жақындағы қалқыма хабарлар жоқ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string> @@ -766,7 +766,7 @@ </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Батареяны пайдалану"</string> <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнемдеу режимі"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Өнімділікті және фондық деректерді азайтады"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> @@ -857,7 +857,7 @@ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string> - <string name="qs_edit" msgid="5583565172803472437">"Өңдеу"</string> + <string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string> <string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 5bf8f48104db..fc27dd7198b7 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -419,7 +419,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"រហូតដល់ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"រចនាប័ទ្មងងឹត"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"កម្មវិធីសន្សំថ្ម"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"មុខងារសន្សំថ្ម"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"បើកនៅពេលថ្ងៃលិច"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"រហូតដល់ពេលថ្ងៃរះ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> @@ -766,7 +766,7 @@ </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"ការប្រើប្រាស់ថ្ម"</string> <string name="battery_detail_charging_summary" msgid="8821202155297559706">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"កម្មវិធីសន្សំថ្ម"</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"មុខងារសន្សំថ្ម"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"កាត់បន្ថយប្រតិបត្តិការ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 044ecda5230e..c4f531c2fc1a 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"ಸ್ವಯಂ"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಇನ್ವರ್ಟ್ ಮಾಡಿ"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ ಮೋಡ್"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 30ed951177c2..b7e8581c2010 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있음"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있습니다. 기본적으로 <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 대화창으로 표시됩니다."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"이 콘텐츠로 연결되는 플로팅 바로가기로 사용자의 주의를 끕니다."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됩니다."</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됨"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"설정"</string> <string name="notification_priority_title" msgid="2079708866333537093">"우선순위"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 대화 기능을 지원하지 않습니다."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 8eab2f19ec17..d7390d07c313 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер түрүндө көрүнөт."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Калкыма ыкчам баскыч менен көңүлүңүздү бул мазмунга буруп турат."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуу бөлүмүнүн жогорку жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуулар тизмесинин өйдө жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Жөндөөлөр"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> жазышуу функцияларын колдоого албайт"</string> @@ -854,7 +854,7 @@ <string name="left_icon" msgid="5036278531966897006">"¨Солго¨ сүрөтчөсү"</string> <string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн, кармап туруп, сүйрөңүз"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Алып салуу үчүн бул жерге сүйрөңүз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string> <string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 9e1b66f07758..b584dfee1e60 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -47,4 +47,15 @@ <dimen name="global_actions_power_dialog_item_height">130dp</dimen> <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen> + + <dimen name="controls_management_top_padding">12dp</dimen> + <dimen name="controls_management_titles_margin">8dp</dimen> + <dimen name="controls_management_indicator_top_margin">8dp</dimen> + <dimen name="controls_management_list_margin">4dp</dimen> + <dimen name="controls_management_footer_height">56dp</dimen> + <dimen name="controls_management_zone_top_margin">24dp</dimen> + + <!-- (footer_height -48dp)/2 --> + <dimen name="controls_management_footer_top_margin">4dp</dimen> + <dimen name="controls_management_favorites_top_margin">8dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index f8f1dc03905c..4aa16727cff7 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -776,7 +776,7 @@ </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Akum. energ. vartoj."</string> <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"Akumuliat. taus. pr."</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"Akum. taus. pr."</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Sumažinamas našumas ir foninių duomenų naudojimas"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index ea1bc77db679..1ef1233989ac 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -713,7 +713,7 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Noklusējums"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Burbulis"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaņas signāla vai vibrācijas"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas zemāk sarunu sadaļā"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas tālāk sarunu sadaļā"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Piesaista jūsu uzmanību, rādot peldošu saīsni uz šo saturu."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 9ec598a7090f..4f6d8244039a 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -710,13 +710,13 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Стандардно"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Балонче"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува под делот за разговор"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да ѕвони или вибрира во зависност од поставките на телефонот"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да ѕвони или вибрира во зависност од поставките на телефонот Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Ви го задржува вниманието со лебдечка кратенка на содржинава."</string> <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Се појавува на горниот дел од секцијата на разговорот во вид на лебдечко меурче, покажувајќи ја профилната слика на заклучениот екран"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Поставки"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддржува функции за разговор"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Нема неодамнешни балончиња"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 277169e2f630..d505a8f52fad 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്തിട്ടില്ല"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"യാന്ത്രികം"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നിറം മാറ്റുക"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നെഗറ്റീവ് ലുക്ക്"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"വർണ്ണം ശരിയാക്കൽ മോഡ്"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string> <string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 49421f6d3ea8..4f040fa33ec4 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -710,7 +710,7 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Өгөгдмөл"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Бөмбөлөг"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсвэл чичиргээ байхгүй"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харицан ярианы хэсгийн доод талд харагдана"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харилцан ярианы хэсгийн доод талд харагдана"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй. <xliff:g id="APP_NAME">%1$s</xliff:g>-н харилцан яриаг өгөгдмөл тохиргооны дагуу бөмбөлөг болгоно."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Энэ контентын хөвөн гарч ирэх товчлолтойгоор таны анхаарлыг татдаг."</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index f003d270549c..5739e9a459f7 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -43,7 +43,7 @@ <string name="status_bar_settings_mute_label" msgid="914392730086057522">"म्युट गर्नुहोस्"</string> <string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"स्वतः"</string> <string name="status_bar_settings_notifications" msgid="5285316949980621438">"सूचनाहरू"</string> - <string name="bluetooth_tethered" msgid="4171071193052799041">"ब्लुटुथ टेथर भयो"</string> + <string name="bluetooth_tethered" msgid="4171071193052799041">"ब्लुटुथ टेदर भयो"</string> <string name="status_bar_input_method_settings_configure_input_methods" msgid="2972273031043777851">"इनपुट विधिहरू सेटअप गर्नुहोस्"</string> <string name="status_bar_use_physical_keyboard" msgid="4849251850931213371">"वास्तविक किबोर्ड"</string> <string name="usb_device_permission_prompt" msgid="4414719028369181772">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> माथि पहुँच राख्ने अनुमति दिने हो?"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 4631559ad5f8..df93cab8cd46 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -710,11 +710,11 @@ <string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Bubbel"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt op een lagere positie in het gedeelte met gesprekken weergegeven"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken weergegeven"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Trekt de aandacht met een zwevende snelkoppeling naar deze content."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gedeelte met gesprekken weergegeven, verschijnt als zwevende bubbel, geeft de profielfoto weer op het vergrendelscherm"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gespreksgedeelte weergegeven, verschijnt als zwevende bubbel, geeft profielfoto weer op vergrendelscherm"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Instellingen"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 8cacc97aa79b..1cf7c4baa0a9 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ। <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଡିଫଲ୍ଟ ଭାବରେ ବବଲ୍ ହୁଏ।"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ଏହି ବିଷୟବସ୍ତୁ ପାଇଁ ଏକ ଭାସମାନ ସର୍ଟକଟ୍ ସହ ଆପଣଙ୍କର ଧ୍ୟାନ ଦିଅନ୍ତୁ।"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଭାସମାନ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଫ୍ଲୋଟିଂ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string> <string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string> @@ -853,7 +853,7 @@ <string name="right_keycode" msgid="2480715509844798438">"ଡାହାଣ କୀ\'କୋଡ୍"</string> <string name="left_icon" msgid="5036278531966897006">"ବାମ ଆଇକନ୍"</string> <string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଡ଼ିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ୍ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 6918ad8ffa09..b23fba93fab6 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -711,12 +711,12 @@ <string name="notification_bubble_title" msgid="8330481035191903164">"Bolha"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podem vibrar ou tocar com base nas configurações do smartphone"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparecem na parte superior de uma seção de conversa, em forma de balões, mostrando a foto do perfil na tela de bloqueio"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string> <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 907199217827..a29ca5ad44c6 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode tocar ou vibrar com base nas definições do telemóvel."</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode tocar ou vibrar com base nas definições do telemóvel. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém a sua atenção com um atalho flutuante para este conteúdo."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior da secção de conversas, surge como um balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece no topo da secção de conversas, surge como balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Definições"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> <string name="no_shortcut" msgid="8257177117568230126">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> não suporta funcionalidades de conversa."</string> @@ -853,7 +853,7 @@ <string name="right_keycode" msgid="2480715509844798438">"Código de tecla direito"</string> <string name="left_icon" msgid="5036278531966897006">"Ícone esquerdo"</string> <string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para adicionar mosaicos."</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tocar sem soltar e arrastar para adicionar mosaicos"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tocar sem soltar e arrastar para reorganizar os mosaicos"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 6918ad8ffa09..b23fba93fab6 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -711,12 +711,12 @@ <string name="notification_bubble_title" msgid="8330481035191903164">"Bolha"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string> - <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string> + <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podem vibrar ou tocar com base nas configurações do smartphone"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparecem na parte superior de uma seção de conversa, em forma de balões, mostrando a foto do perfil na tela de bloqueio"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string> - <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string> + <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string> <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string> <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 117e2b28c95a..b30afd61a0bb 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -720,7 +720,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Звонок или вибрация в зависимости от настроек телефона"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Звонок или вибрация в зависимости от настроек телефона. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата."</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Привлекает ваше внимание к контенту с помощью плавающего ярлыка"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, а также показывает фото профиля на заблокированном экране"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, фото профиля показывается на заблок. экране"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string> <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string> <string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 937bb79de3c8..4962435058db 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -95,7 +95,7 @@ <string name="screenrecord_description" msgid="1123231719680353736">"Med snemanjem lahko sistem Android zajame morebitne občutljive podatke, ki so prikazani na zaslonu ali se predvajajo v napravi. To vključuje gesla, podatke za plačilo, fotografije, sporočila in zvok."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Snemanje zvoka"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvok v napravi"</string> - <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvok v napravi, kot so glasba, klici in toni zvonjenja"</string> + <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvoki v napravi, kot so glasba, klici in toni zvonjenja"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Mikrofon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Zvok v napravi in mikrofon"</string> <string name="screenrecord_start" msgid="330991441575775004">"Začni"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index c9d211cf2b86..73f969f485a6 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -693,7 +693,7 @@ <string name="notification_channel_unsilenced" msgid="94878840742161152">"ఈ నోటిఫికేషన్లు మిమ్మల్ని హెచ్చరిస్తాయి"</string> <string name="inline_blocking_helper" msgid="2891486013649543452">"మీరు సాధారణంగా ఈ నోటిఫికేషన్లను విస్మరిస్తారు. \nవాటి ప్రదర్శనను కొనసాగించాలా?"</string> <string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string> - <string name="inline_ok_button" msgid="603075490581280343">"అప్లై చేయి"</string> + <string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"ఈ నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string> <string name="inline_stop_button" msgid="2453460935438696090">"నోటిఫికేషన్లను ఆపివేయి"</string> <string name="inline_deliver_silently_button" msgid="2714314213321223286">"నిశ్శబ్దంగా బట్వాడా చేయండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 7705be92c5e0..1a9395a9b1fb 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"เริ่มบันทึกเลยไหม"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนที่ปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string> + <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนซึ่งปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"บันทึกเสียง"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"เสียงจากอุปกรณ์"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"เสียงจากอุปกรณ์ เช่น เพลง การโทร และเสียงเรียกเข้า"</string> @@ -337,7 +337,7 @@ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"ขณะนี้หน้าจอล็อกอยู่ในแนวนอน"</string> <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"ขณะนี้หน้าจอล็อกอยู่ในแนวตั้ง"</string> <string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string> - <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาหน้าจอ"</string> + <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาจอภาพ"</string> <string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string> <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"แตะไอคอนค้างไว้เพื่อดูตัวเลือกอื่นๆ"</string> <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string> @@ -509,7 +509,7 @@ <string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"ใหม่"</string> - <string name="notification_section_header_gentle" msgid="6804099527336337197">"เงียบ"</string> + <string name="notification_section_header_gentle" msgid="6804099527336337197">"ปิดเสียง"</string> <string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"การสนทนา"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ล้างการแจ้งเตือนแบบไม่มีเสียงทั้งหมด"</string> @@ -706,7 +706,7 @@ <string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"แจ้งเตือนต่อไป"</string> <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ปิดการแจ้งเตือน"</string> <string name="inline_keep_showing_app" msgid="4393429060390649757">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string> - <string name="notification_silence_title" msgid="8608090968400832335">"เงียบ"</string> + <string name="notification_silence_title" msgid="8608090968400832335">"ปิดเสียง"</string> <string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"บับเบิล"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"ไม่มีเสียงหรือการสั่น"</string> @@ -854,7 +854,7 @@ <string name="left_icon" msgid="5036278531966897006">"ไอคอนทางซ้าย"</string> <string name="right_icon" msgid="1103955040645237425">"ไอคอนทางขวา"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"กดค้างแล้วลากเพื่อเพิ่มการ์ด"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดการ์ดค้างไว้แล้วลากเพื่อจัดเรียงใหม่"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดค้างแล้วลากเพื่อจัดเรียงการ์ดใหม่"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ลากมาที่นี่เพื่อนำออก"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"คุณต้องมีการ์ดอย่างน้อย <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> รายการ"</string> <string name="qs_edit" msgid="5583565172803472437">"แก้ไข"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 88e3fb7f967b..2d37a0ae1739 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"可能會根據手機設定發出鈴聲或震動"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機設定發出鈴聲或震動。「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會預設以對話氣泡顯示。"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"為此內容建立浮動捷徑以保持注意力。"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"在對話部分的頂部以浮動對話氣泡顯示,並在上鎖畫面顯示個人檔案相片"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"以浮動對話泡顯示在對話部分的頂部,並在上鎖畫面顯示個人檔案相片"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"設定"</string> <string name="notification_priority_title" msgid="2079708866333537093">"重要"</string> <string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index ad5e370b9c66..14345873f2cf 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -710,7 +710,7 @@ <string name="notification_alert_title" msgid="3656229781017543655">"預設"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"泡泡"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"不震動或發出聲音"</string> - <string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並顯示在對話部分的下方"</string> + <string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並調整排序到其他對話下方"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"根據手機的設定響鈴或震動"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"利用浮動式捷徑快速存取這項內容。"</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0d40a9fa26d4..5b7bee653154 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1350,15 +1350,19 @@ <dimen name="controls_management_side_padding">16dp</dimen> <dimen name="controls_management_titles_margin">16dp</dimen> <dimen name="controls_management_footer_side_margin">8dp</dimen> + <dimen name="controls_management_footer_top_margin">@dimen/controls_management_footer_side_margin</dimen> <dimen name="controls_management_list_margin">16dp</dimen> + <dimen name="controls_management_indicator_top_margin">@dimen/controls_management_list_margin</dimen> <dimen name="controls_management_apps_list_margin">64dp</dimen> <dimen name="controls_management_editing_list_margin">48dp</dimen> <dimen name="controls_management_editing_divider_margin">24dp</dimen> <dimen name="controls_management_apps_extra_side_margin">8dp</dimen> <dimen name="controls_management_zone_top_margin">32dp</dimen> + <dimen name="controls_management_favorites_top_margin">@dimen/controls_management_zone_top_margin</dimen> <dimen name="controls_management_status_side_margin">16dp</dimen> <dimen name="controls_management_page_indicator_height">24dp</dimen> <dimen name="controls_management_checkbox_size">25dp</dimen> + <dimen name="controls_management_footer_height">72dp</dimen> <dimen name="controls_title_size">24sp</dimen> <dimen name="controls_subtitle_size">16sp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java index 5f8815665d88..2deeb1230f09 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java @@ -39,15 +39,21 @@ import javax.inject.Singleton; */ @Singleton public class ForegroundServiceController { - public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW}; + public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA, + AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION}; private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>(); private final Object mMutex = new Object(); + private final NotificationEntryManager mEntryManager; private final Handler mMainHandler; @Inject - public ForegroundServiceController(AppOpsController appOpsController, - @Main Handler mainHandler) { + public ForegroundServiceController(NotificationEntryManager entryManager, + AppOpsController appOpsController, @Main Handler mainHandler) { + mEntryManager = entryManager; mMainHandler = mainHandler; appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> { mMainHandler.post(() -> { @@ -81,6 +87,19 @@ public class ForegroundServiceController { } /** + * Returns the keys for notifications from this package using the standard template, + * if they exist. + */ + @Nullable + public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) { + synchronized (mMutex) { + final ForegroundServicesUserState services = mUserServices.get(userId); + if (services == null) return null; + return services.getStandardLayoutKeys(pkg); + } + } + + /** * Gets active app ops for this user and package */ @Nullable @@ -121,6 +140,31 @@ public class ForegroundServiceController { userServices.removeOp(packageName, appOpCode); } } + + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // AppOpsCoordinator + // Update appOps if there are associated pending or visible notifications + final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName); + if (notificationKeys != null) { + boolean changed = false; + for (String key : notificationKeys) { + final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key); + if (entry != null + && uid == entry.getSbn().getUid() + && packageName.equals(entry.getSbn().getPackageName())) { + synchronized (entry.mActiveAppOps) { + if (active) { + changed |= entry.mActiveAppOps.add(appOpCode); + } else { + changed |= entry.mActiveAppOps.remove(appOpCode); + } + } + } + } + if (changed) { + mEntryManager.updateNotifications("appOpChanged pkg=" + packageName); + } + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java index 1515272569d6..bb445832da93 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java @@ -172,8 +172,24 @@ public class ForegroundServiceNotificationListener { sbn.getPackageName(), sbn.getKey()); } } + tagAppOps(entry); return true; }, true /* create if not found */); } + + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // AppOpsCoordinator + private void tagAppOps(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); + ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( + sbn.getUserId(), + sbn.getPackageName()); + synchronized (entry.mActiveAppOps) { + entry.mActiveAppOps.clear(); + if (activeOps != null) { + entry.mActiveAppOps.addAll(activeOps); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 64b35caeeef2..2b9514f6d23f 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,8 +16,13 @@ package com.android.systemui.appops; +import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; + import android.app.AppOpsManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.location.LocationManager; import android.media.AudioManager; @@ -35,6 +40,7 @@ import androidx.annotation.WorkerThread; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; @@ -54,7 +60,7 @@ import javax.inject.Singleton; * NotificationPresenter to be displayed to the user. */ @Singleton -public class AppOpsControllerImpl implements AppOpsController, +public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController, AppOpsManager.OnOpActiveChangedInternalListener, AppOpsManager.OnOpNotedListener, Dumpable { @@ -65,6 +71,7 @@ public class AppOpsControllerImpl implements AppOpsController, private static final String TAG = "AppOpsControllerImpl"; private static final boolean DEBUG = false; + private final BroadcastDispatcher mDispatcher; private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private final LocationManager mLocationManager; @@ -79,6 +86,7 @@ public class AppOpsControllerImpl implements AppOpsController, private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); private final PermissionFlagsCache mFlagsCache; private boolean mListening; + private boolean mMicMuted; @GuardedBy("mActiveItems") private final List<AppOpItem> mActiveItems = new ArrayList<>(); @@ -104,8 +112,10 @@ public class AppOpsControllerImpl implements AppOpsController, @Background Looper bgLooper, DumpManager dumpManager, PermissionFlagsCache cache, - AudioManager audioManager + AudioManager audioManager, + BroadcastDispatcher dispatcher ) { + mDispatcher = dispatcher; mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mFlagsCache = cache; mBGHandler = new H(bgLooper); @@ -114,6 +124,7 @@ public class AppOpsControllerImpl implements AppOpsController, mCallbacksByCode.put(OPS[i], new ArraySet<>()); } mAudioManager = audioManager; + mMicMuted = audioManager.isMicrophoneMute(); mLocationManager = context.getSystemService(LocationManager.class); dumpManager.registerDumpable(TAG, this); } @@ -132,6 +143,8 @@ public class AppOpsControllerImpl implements AppOpsController, mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); + mDispatcher.registerReceiverWithHandler(this, + new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler); } else { mAppOps.stopWatchingActive(this); @@ -139,6 +152,7 @@ public class AppOpsControllerImpl implements AppOpsController, mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); mBGHandler.removeCallbacksAndMessages(null); // null removes all + mDispatcher.unregisterReceiver(this); synchronized (mActiveItems) { mActiveItems.clear(); mRecordingsByUid.clear(); @@ -466,6 +480,9 @@ public class AppOpsControllerImpl implements AppOpsController, } private boolean isAnyRecordingPausedLocked(int uid) { + if (mMicMuted) { + return true; + } List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid); if (configs == null) return false; int configsNum = configs.size(); @@ -520,6 +537,12 @@ public class AppOpsControllerImpl implements AppOpsController, } }; + @Override + public void onReceive(Context context, Intent intent) { + mMicMuted = mAudioManager.isMicrophoneMute(); + updateRecordingPausedStatus(); + } + protected class H extends Handler { H(Looper looper) { super(looper); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java index c1dd8c36ff6f..48a9b91721f5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java @@ -30,10 +30,6 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger * @param e UI event */ public void log(Bubble b, UiEventEnum e) { - if (b.getInstanceId() == null) { - // Added from persistence -- TODO log this with specific event? - return; - } logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId()); } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 31830b94e8e4..40662536e57e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -263,6 +263,7 @@ internal class ControlHolder( val context = itemView.context val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) + icon.imageTintList = null ci.customIcon?.let { icon.setImageIcon(it) } ?: run { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5f75c96be128..5a525974f3cb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -104,6 +104,7 @@ class ControlsUiControllerImpl @Inject constructor ( private var hidden = true private lateinit var dismissGlobalActions: Runnable private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow) + private var retainCache = false private val collator = Collator.getInstance(context.resources.configuration.locales[0]) private val localeComparator = compareBy<SelectionItem, CharSequence>(collator) { @@ -149,6 +150,7 @@ class ControlsUiControllerImpl @Inject constructor ( this.parent = parent this.dismissGlobalActions = dismissGlobalActions hidden = false + retainCache = false allStructures = controlsController.get().getFavorites() selectedStructure = loadPreference(allStructures) @@ -235,6 +237,8 @@ class ControlsUiControllerImpl @Inject constructor ( } putIntentExtras(i, si) startActivity(context, i) + + retainCache = true } private fun putIntentExtras(intent: Intent, si: StructureInfo) { @@ -497,7 +501,7 @@ class ControlsUiControllerImpl @Inject constructor ( controlsListingController.get().removeCallback(listingCallback) - RenderInfo.clearCache() + if (!retainCache) RenderInfo.clearCache() } override fun onRefreshState(componentName: ComponentName, controls: List<Control>) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 1e99a7b733e2..e2361d8f415a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -42,6 +42,7 @@ import android.hardware.SensorPrivacyManager; import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.media.MediaRouter2Manager; +import android.media.session.MediaSessionManager; import android.net.ConnectivityManager; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; @@ -219,6 +220,11 @@ public class SystemServicesModule { } @Provides + static MediaSessionManager provideMediaSessionManager(Context context) { + return context.getSystemService(MediaSessionManager.class); + } + + @Provides @Singleton static NetworkScoreManager provideNetworkScoreManager(Context context) { return context.getSystemService(NetworkScoreManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index ae7d82ac4a5e..253a35c55698 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -430,8 +430,12 @@ public class DozeMachine { /** Give the Part a chance to clean itself up. */ default void destroy() {} - /** Alerts that the screenstate is being changed. */ - default void onScreenState(int state) {} + /** + * Alerts that the screenstate is being changed. + * Note: This may be called from within a call to transitionTo, so local DozeState may not + * be accurate nor match with the new displayState. + */ + default void onScreenState(int displayState) {} } /** A wrapper interface for {@link android.service.dreams.DreamService} */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 64cfb4bcd058..a11997b6b845 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -69,7 +69,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi * --ei brightness_bucket 1} */ private int mDebugBrightnessBucket = -1; - private DozeMachine.State mState; @VisibleForTesting public DozeScreenBrightness(Context context, DozeMachine.Service service, @@ -109,7 +108,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { - mState = newState; switch (newState) { case INITIALIZED: case DOZE: @@ -127,10 +125,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Override public void onScreenState(int state) { - if (!mScreenOff - && (mState == DozeMachine.State.DOZE_AOD - || mState == DozeMachine.State.DOZE_AOD_DOCKED) - && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND)) { + if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) { setLightSensorEnabled(true); } else { setLightSensorEnabled(false); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 0554dc80cba5..ff25439a5f9f 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -19,6 +19,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS; import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; @@ -548,7 +549,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (!mDeviceProvisioned && !action.showBeforeProvisioning()) { return false; } - return true; + return action.shouldShow(); } /** @@ -961,6 +962,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @VisibleForTesting class ScreenshotAction extends SinglePressAction implements LongPressAction { + final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons"; + public ScreenshotAction() { super(R.drawable.ic_screenshot, R.string.global_action_screenshot); } @@ -993,6 +996,19 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } @Override + public boolean shouldShow() { + // Include screenshot in power menu for legacy nav because it is not accessible + // through Recents in that mode + return is2ButtonNavigationEnabled(); + } + + boolean is2ButtonNavigationEnabled() { + return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger( + com.android.internal.R.integer.config_navBarInteractionMode); + } + + + @Override public boolean onLongPress() { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) { mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS); @@ -1615,6 +1631,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, * @return */ CharSequence getMessage(); + + default boolean shouldShow() { + return true; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 865b11f7b738..3aa37a2a927d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -43,7 +43,7 @@ class MediaCarouselController @Inject constructor( private val mediaHostStatesManager: MediaHostStatesManager, private val activityStarter: ActivityStarter, @Main executor: DelayableExecutor, - mediaManager: MediaDataFilter, + private val mediaManager: MediaDataManager, configurationController: ConfigurationController, falsingManager: FalsingManager ) { @@ -110,6 +110,7 @@ class MediaCarouselController @Inject constructor( private val pageIndicator: PageIndicator private val visualStabilityCallback: VisualStabilityManager.Callback private var needsReordering: Boolean = false + private var keysNeedRemoval = mutableSetOf<String>() private var isRtl: Boolean = false set(value) { if (value != field) { @@ -161,6 +162,10 @@ class MediaCarouselController @Inject constructor( needsReordering = false reorderAllPlayers() } + + keysNeedRemoval.forEach { removePlayer(it) } + keysNeedRemoval.clear() + // Let's reset our scroll position mediaCarouselScrollHandler.scrollToStart() } @@ -168,13 +173,19 @@ class MediaCarouselController @Inject constructor( true /* persistent */) mediaManager.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (!data.active && !Utils.useMediaResumption(context)) { - // This view is inactive, let's remove this! This happens e.g when dismissing / - // timing out a view. We still have the data around because resumption could - // be on, but we should save the resources and release this. - onMediaDataRemoved(key) + addOrUpdatePlayer(key, oldKey, data) + val canRemove = data.isPlaying?.let { !it } ?: data.isClearable + if (canRemove && !Utils.useMediaResumption(context)) { + // This view isn't playing, let's remove this! This happens e.g when + // dismissing/timing out a view. We still have the data around because + // resumption could be on, but we should save the resources and release this. + if (visualStabilityManager.isReorderingAllowed) { + onMediaDataRemoved(key) + } else { + keysNeedRemoval.add(key) + } } else { - addOrUpdatePlayer(key, oldKey, data) + keysNeedRemoval.remove(key) } } @@ -236,12 +247,12 @@ class MediaCarouselController @Inject constructor( var newPlayer = mediaControlPanelFactory.get() newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions - MediaPlayerData.addMediaPlayer(key, data, newPlayer) val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newPlayer.view?.player?.setLayoutParams(lp) newPlayer.bind(data) newPlayer.setListening(currentlyExpanded) + MediaPlayerData.addMediaPlayer(key, data, newPlayer) updatePlayerToState(newPlayer, noAnimation = true) reorderAllPlayers() } else { @@ -263,7 +274,7 @@ class MediaCarouselController @Inject constructor( } } - private fun removePlayer(key: String) { + private fun removePlayer(key: String, dismissMediaData: Boolean = true) { val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) @@ -271,11 +282,17 @@ class MediaCarouselController @Inject constructor( removed.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() + + if (dismissMediaData) { + // Inform the media manager of a potentially late dismissal + mediaManager.dismissMediaData(key, 0L) + } } } private fun recreatePlayers() { MediaPlayerData.mediaData().forEach { (key, data) -> + removePlayer(key, dismissMediaData = false) addOrUpdatePlayer(key = key, oldKey = null, data = data) } } @@ -478,12 +495,11 @@ class MediaCarouselController @Inject constructor( internal object MediaPlayerData { private data class MediaSortKey( val data: MediaData, - val updateTime: Long = 0, - val isPlaying: Boolean = false + val updateTime: Long = 0 ) private val comparator = - compareByDescending<MediaSortKey> { it.isPlaying } + compareByDescending<MediaSortKey> { it.data.isPlaying } .thenByDescending { it.data.isLocalSession } .thenByDescending { !it.data.resumption } .thenByDescending { it.updateTime } @@ -493,7 +509,7 @@ internal object MediaPlayerData { fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) { removeMediaPlayer(key) - val sortKey = MediaSortKey(data, System.currentTimeMillis(), player.isPlaying()) + val sortKey = MediaSortKey(data, System.currentTimeMillis()) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index d6a02687c905..40a879abde34 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -94,7 +94,17 @@ data class MediaData( * Notification key for cancelling a media player after a timeout (when not using resumption.) */ val notificationKey: String? = null, - var hasCheckedForResume: Boolean = false + var hasCheckedForResume: Boolean = false, + + /** + * If apps do not report PlaybackState, set as null to imply 'undetermined' + */ + val isPlaying: Boolean? = null, + + /** + * Set from the notification and used as fallback when PlaybackState cannot be determined + */ + val isClearable: Boolean = true ) /** State of a media action. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index d0642ccf9714..aa3699e9a22b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -17,65 +17,48 @@ package com.android.systemui.media import javax.inject.Inject -import javax.inject.Singleton /** - * Combines updates from [MediaDataManager] with [MediaDeviceManager]. + * Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */ -@Singleton -class MediaDataCombineLatest @Inject constructor( - private val dataSource: MediaDataManager, - private val deviceSource: MediaDeviceManager -) { +class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, + MediaDeviceManager.Listener { + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf() - init { - dataSource.addListener(object : MediaDataManager.Listener { - override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (oldKey != null && oldKey != key && entries.contains(oldKey)) { - entries[key] = data to entries.remove(oldKey)?.second - update(key, oldKey) - } else { - entries[key] = data to entries[key]?.second - update(key, key) - } - } - override fun onMediaDataRemoved(key: String) { - remove(key) - } - }) - deviceSource.addListener(object : MediaDeviceManager.Listener { - override fun onMediaDeviceChanged( - key: String, - oldKey: String?, - data: MediaDeviceData? - ) { - if (oldKey != null && oldKey != key && entries.contains(oldKey)) { - entries[key] = entries.remove(oldKey)?.first to data - update(key, oldKey) - } else { - entries[key] = entries[key]?.first to data - update(key, key) - } - } - override fun onKeyRemoved(key: String) { - remove(key) - } - }) + override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = data to entries.remove(oldKey)?.second + update(key, oldKey) + } else { + entries[key] = data to entries[key]?.second + update(key, key) + } } - /** - * Get a map of all non-null data entries - */ - fun getData(): Map<String, MediaData> { - return entries.filter { - (key, pair) -> pair.first != null && pair.second != null - }.mapValues { - (key, pair) -> pair.first!!.copy(device = pair.second) + override fun onMediaDataRemoved(key: String) { + remove(key) + } + + override fun onMediaDeviceChanged( + key: String, + oldKey: String?, + data: MediaDeviceData? + ) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = entries.remove(oldKey)?.first to data + update(key, oldKey) + } else { + entries[key] = entries[key]?.first to data + update(key, key) } } + override fun onKeyRemoved(key: String) { + remove(key) + } + /** * Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index 662831e4a445..1f580a953d09 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -24,32 +24,32 @@ import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "MediaDataFilter" +private const val DEBUG = true /** * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user * switches (removing entries for the previous user, adding back entries for the current user) * - * This is added downstream of [MediaDataManager] since we may still need to handle callbacks from - * background users (e.g. timeouts) that UI classes should ignore. - * Instead, UI classes should listen to this so they can stay in sync with the current user. + * This is added at the end of the pipeline since we may still need to handle callbacks from + * background users (e.g. timeouts). */ -@Singleton class MediaDataFilter @Inject constructor( - private val dataSource: MediaDataCombineLatest, private val broadcastDispatcher: BroadcastDispatcher, private val mediaResumeListener: MediaResumeListener, - private val mediaDataManager: MediaDataManager, private val lockscreenUserManager: NotificationLockscreenUserManager, @Main private val executor: Executor ) : MediaDataManager.Listener { private val userTracker: CurrentUserTracker - private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + internal val listeners: Set<MediaDataManager.Listener> + get() = _listeners.toSet() + internal lateinit var mediaDataManager: MediaDataManager - // The filtered mediaEntries, which will be a subset of all mediaEntries in MediaDataManager - private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + private val allEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + // The filtered userEntries, which will be a subset of all userEntries in MediaDataManager + private val userEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() init { userTracker = object : CurrentUserTracker(broadcastDispatcher) { @@ -59,31 +59,34 @@ class MediaDataFilter @Inject constructor( } } userTracker.startTracking() - dataSource.addListener(this) } override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { + if (oldKey != null && oldKey != key) { + allEntries.remove(oldKey) + } + allEntries.put(key, data) + if (!lockscreenUserManager.isCurrentProfile(data.userId)) { return } - if (oldKey != null) { - mediaEntries.remove(oldKey) + if (oldKey != null && oldKey != key) { + userEntries.remove(oldKey) } - mediaEntries.put(key, data) + userEntries.put(key, data) // Notify listeners - val listenersCopy = listeners.toSet() - listenersCopy.forEach { + listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) } } override fun onMediaDataRemoved(key: String) { - mediaEntries.remove(key)?.let { + allEntries.remove(key) + userEntries.remove(key)?.let { // Only notify listeners if something actually changed - val listenersCopy = listeners.toSet() - listenersCopy.forEach { + listeners.forEach { it.onMediaDataRemoved(key) } } @@ -92,22 +95,22 @@ class MediaDataFilter @Inject constructor( @VisibleForTesting internal fun handleUserSwitched(id: Int) { // If the user changes, remove all current MediaData objects and inform listeners - val listenersCopy = listeners.toSet() - val keyCopy = mediaEntries.keys.toMutableList() + val listenersCopy = listeners + val keyCopy = userEntries.keys.toMutableList() // Clear the list first, to make sure callbacks from listeners if we have any entries // are up to date - mediaEntries.clear() + userEntries.clear() keyCopy.forEach { - Log.d(TAG, "Removing $it after user change") + if (DEBUG) Log.d(TAG, "Removing $it after user change") listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) } } - dataSource.getData().forEach { (key, data) -> + allEntries.forEach { (key, data) -> if (lockscreenUserManager.isCurrentProfile(data.userId)) { - Log.d(TAG, "Re-adding $key after user change") - mediaEntries.put(key, data) + if (DEBUG) Log.d(TAG, "Re-adding $key after user change") + userEntries.put(key, data) listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) } @@ -119,7 +122,8 @@ class MediaDataFilter @Inject constructor( * Invoked when the user has dismissed the media carousel */ fun onSwipeToDismiss() { - val mediaKeys = mediaEntries.keys.toSet() + if (DEBUG) Log.d(TAG, "Media carousel swiped away") + val mediaKeys = userEntries.keys.toSet() mediaKeys.forEach { mediaDataManager.setTimedOut(it, timedOut = true) } @@ -128,26 +132,20 @@ class MediaDataFilter @Inject constructor( /** * Are there any media notifications active? */ - fun hasActiveMedia() = mediaEntries.any { it.value.active } + fun hasActiveMedia() = userEntries.any { it.value.active } /** * Are there any media entries we should display? - * If resumption is enabled, this will include inactive players - * If resumption is disabled, we only want to show active players */ - fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) { - mediaEntries.isNotEmpty() - } else { - hasActiveMedia() - } + fun hasAnyMedia() = userEntries.isNotEmpty() /** * Add a listener for filtered [MediaData] changes */ - fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + fun addListener(listener: MediaDataManager.Listener) = _listeners.add(listener) /** * Remove a listener that was registered with addListener */ - fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) -}
\ No newline at end of file + fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener) +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 7e246c803254..bb11efeebf08 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -45,6 +45,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert @@ -56,8 +57,6 @@ import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton -import kotlin.collections.ArrayList -import kotlin.collections.LinkedHashMap // URI fields to try loading album art from private val ART_URIS = arrayOf( @@ -67,6 +66,7 @@ private val ART_URIS = arrayOf( ) private const val TAG = "MediaDataManager" +private const val DEBUG = true private const val DEFAULT_LUMINOSITY = 0.25f private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f @@ -99,23 +99,23 @@ class MediaDataManager( dumpManager: DumpManager, mediaTimeoutListener: MediaTimeoutListener, mediaResumeListener: MediaResumeListener, + mediaSessionBasedFilter: MediaSessionBasedFilter, + mediaDeviceManager: MediaDeviceManager, + mediaDataCombineLatest: MediaDataCombineLatest, + private val mediaDataFilter: MediaDataFilter, private var useMediaResumption: Boolean, private val useQsMediaPlayer: Boolean ) : Dumpable { - private val listeners: MutableSet<Listener> = mutableSetOf() + // Internal listeners are part of the internal pipeline. External listeners (those registered + // with [MediaDeviceManager.addListener]) receive events after they have propagated through + // the internal pipeline. + // Another way to think of the distinction between internal and external listeners is the + // following. Internal listeners are listeners that MediaDataManager depends on, and external + // listeners are listeners that depend on MediaDataManager. + // TODO(b/159539991#comment5): Move internal listeners to separate package. + private val internalListeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() - internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context) - set(value) { - // Update list - appsBlockedFromResume.clear() - appsBlockedFromResume.addAll(value) - - // Remove any existing resume players that are now blocked - appsBlockedFromResume.forEach { - removeAllForPackage(it) - } - } @Inject constructor( @@ -126,9 +126,14 @@ class MediaDataManager( dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, mediaTimeoutListener: MediaTimeoutListener, - mediaResumeListener: MediaResumeListener + mediaResumeListener: MediaResumeListener, + mediaSessionBasedFilter: MediaSessionBasedFilter, + mediaDeviceManager: MediaDeviceManager, + mediaDataCombineLatest: MediaDataCombineLatest, + mediaDataFilter: MediaDataFilter ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory, broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener, + mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context)) private val appChangeReceiver = object : BroadcastReceiver() { @@ -151,12 +156,26 @@ class MediaDataManager( init { dumpManager.registerDumpable(TAG, this) + + // Initialize the internal processing pipeline. The listeners at the front of the pipeline + // are set as internal listeners so that they receive events. From there, events are + // propagated through the pipeline. The end of the pipeline is currently mediaDataFilter, + // so it is responsible for dispatching events to external listeners. To achieve this, + // external listeners that are registered with [MediaDataManager.addListener] are actually + // registered as listeners to mediaDataFilter. + addInternalListener(mediaTimeoutListener) + addInternalListener(mediaResumeListener) + addInternalListener(mediaSessionBasedFilter) + mediaSessionBasedFilter.addListener(mediaDeviceManager) + mediaSessionBasedFilter.addListener(mediaDataCombineLatest) + mediaDeviceManager.addListener(mediaDataCombineLatest) + mediaDataCombineLatest.addListener(mediaDataFilter) + + // Set up links back into the pipeline for listeners that need to send events upstream. mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean -> setTimedOut(token, timedOut) } - addListener(mediaTimeoutListener) - mediaResumeListener.setManager(this) - addListener(mediaResumeListener) + mediaDataFilter.mediaDataManager = this val suspendFilter = IntentFilter(Intent.ACTION_PACKAGES_SUSPENDED) broadcastDispatcher.registerReceiver(appChangeReceiver, suspendFilter, null, UserHandle.ALL) @@ -194,10 +213,9 @@ class MediaDataManager( private fun removeAllForPackage(packageName: String) { Assert.isMainThread() - val listenersCopy = listeners.toSet() val toRemove = mediaEntries.filter { it.value.packageName == packageName } toRemove.forEach { - removeEntry(it.key, listenersCopy) + removeEntry(it.key) } } @@ -257,15 +275,48 @@ class MediaDataManager( /** * Add a listener for changes in this class */ - fun addListener(listener: Listener) = listeners.add(listener) + fun addListener(listener: Listener) { + // mediaDataFilter is the current end of the internal pipeline. Register external + // listeners as listeners to it. + mediaDataFilter.addListener(listener) + } /** * Remove a listener for changes in this class */ - fun removeListener(listener: Listener) = listeners.remove(listener) + fun removeListener(listener: Listener) { + // Since mediaDataFilter is the current end of the internal pipelie, external listeners + // have been registered to it. So, they need to be removed from it too. + mediaDataFilter.removeListener(listener) + } /** - * Called whenever the player has been paused or stopped for a while. + * Add a listener for internal events. + */ + private fun addInternalListener(listener: Listener) = internalListeners.add(listener) + + /** + * Notify internal listeners of loaded event. + * + * External listeners registered with [addListener] will be notified after the event propagates + * through the internal listener pipeline. + */ + private fun notifyMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + internalListeners.forEach { it.onMediaDataLoaded(key, oldKey, info) } + } + + /** + * Notify internal listeners of removed event. + * + * External listeners registered with [addListener] will be notified after the event propagates + * through the internal listener pipeline. + */ + private fun notifyMediaDataRemoved(key: String) { + internalListeners.forEach { it.onMediaDataRemoved(key) } + } + + /** + * Called whenever the player has been paused or stopped for a while, or swiped from QQS. * This will make the player not active anymore, hiding it from QQS and Keyguard. * @see MediaData.active */ @@ -275,20 +326,28 @@ class MediaDataManager( return } it.active = !timedOut + if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut") onMediaDataLoaded(token, token, it) } } - private fun removeEntry(key: String, listenersCopy: Set<Listener>) { + private fun removeEntry(key: String) { mediaEntries.remove(key) - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } + notifyMediaDataRemoved(key) } fun dismissMediaData(key: String, delay: Long) { - val listenersCopy = listeners.toSet() - foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay) + backgroundExecutor.execute { + mediaEntries[key]?.let { mediaData -> + if (mediaData.isLocalSession) { + mediaData.token?.let { + val mediaController = mediaControllerFactory.create(it) + mediaController.transportControls.stop() + } + } + } + } + foregroundExecutor.executeDelayed({ removeEntry(key) }, delay) } private fun loadMediaDataInBgForResumption( @@ -307,7 +366,9 @@ class MediaDataManager( return } - Log.d(TAG, "adding track for $userId from browser: $desc") + if (DEBUG) { + Log.d(TAG, "adding track for $userId from browser: $desc") + } // Album art var artworkBitmap = desc.iconBitmap @@ -408,7 +469,7 @@ class MediaDataManager( if (actions != null) { for ((index, action) in actions.withIndex()) { if (action.getIcon() == null) { - Log.i(TAG, "No icon for action $index ${action.title}") + if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}") actionsToShowCollapsed.remove(index) continue } @@ -433,6 +494,7 @@ class MediaDataManager( val isLocalSession = mediaController.playbackInfo?.playbackType == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true + val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null foregroundExecutor.execute { val resumeAction: Runnable? = mediaEntries[key]?.resumeAction @@ -442,7 +504,8 @@ class MediaDataManager( smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null, active, resumeAction = resumeAction, isLocalSession = isLocalSession, - notificationKey = key, hasCheckedForResume = hasCheckedForResume)) + notificationKey = key, hasCheckedForResume = hasCheckedForResume, + isPlaying = isPlaying, isClearable = sbn.isClearable())) } } @@ -455,7 +518,7 @@ class MediaDataManager( if (!TextUtils.isEmpty(uriString)) { val albumArt = loadBitmapFromUri(Uri.parse(uriString)) if (albumArt != null) { - Log.d(TAG, "loaded art from $uri") + if (DEBUG) Log.d(TAG, "loaded art from $uri") return albumArt } } @@ -534,19 +597,15 @@ class MediaDataManager( if (mediaEntries.containsKey(key)) { // Otherwise this was removed already mediaEntries.put(key, data) - val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataLoaded(key, oldKey, data) - } + notifyMediaDataLoaded(key, oldKey, data) } } fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) - if (useMediaResumption && removed?.resumeAction != null && - !isBlockedFromResume(removed?.packageName)) { - Log.d(TAG, "Not removing $key because resumable") + if (useMediaResumption && removed?.resumeAction != null) { + if (DEBUG) Log.d(TAG, "Not removing $key because resumable") // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = getResumeMediaAction(removed.resumeAction!!) val updated = removed.copy(token = null, actions = listOf(resumeAction), @@ -554,39 +613,22 @@ class MediaDataManager( val pkg = removed?.packageName val migrate = mediaEntries.put(pkg, updated) == null // Notify listeners of "new" controls when migrating or removed and update when not - val listenersCopy = listeners.toSet() if (migrate) { - listenersCopy.forEach { - it.onMediaDataLoaded(pkg, key, updated) - } + notifyMediaDataLoaded(pkg, key, updated) } else { // Since packageName is used for the key of the resumption controls, it is // possible that another notification has already been reused for the resumption // controls of this package. In this case, rather than renaming this player as // packageName, just remove it and then send a update to the existing resumption // controls. - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } - listenersCopy.forEach { - it.onMediaDataLoaded(pkg, pkg, updated) - } + notifyMediaDataRemoved(key) + notifyMediaDataLoaded(pkg, pkg, updated) } return } if (removed != null) { - val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } - } - } - - private fun isBlockedFromResume(packageName: String?): Boolean { - if (packageName == null) { - return true + notifyMediaDataRemoved(key) } - return appsBlockedFromResume.contains(packageName) } fun setMediaResumptionEnabled(isEnabled: Boolean) { @@ -598,17 +640,31 @@ class MediaDataManager( if (!useMediaResumption) { // Remove any existing resume controls - val listenersCopy = listeners.toSet() val filtered = mediaEntries.filter { !it.value.active } filtered.forEach { mediaEntries.remove(it.key) - listenersCopy.forEach { listener -> - listener.onMediaDataRemoved(it.key) - } + notifyMediaDataRemoved(it.key) } } } + /** + * Invoked when the user has dismissed the media carousel + */ + fun onSwipeToDismiss() = mediaDataFilter.onSwipeToDismiss() + + /** + * Are there any media notifications active? + */ + fun hasActiveMedia() = mediaDataFilter.hasActiveMedia() + + /** + * Are there any media entries we should display? + * If resumption is enabled, this will include inactive players + * If resumption is disabled, we only want to show active players + */ + fun hasAnyMedia() = mediaDataFilter.hasAnyMedia() + interface Listener { /** @@ -628,10 +684,10 @@ class MediaDataManager( override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { pw.apply { - println("listeners: $listeners") + println("internalListeners: $internalListeners") + println("externalListeners: ${mediaDataFilter.listeners}") println("mediaEntries: $mediaEntries") println("useMediaResumption: $useMediaResumption") - println("appsBlockedFromResume: $appsBlockedFromResume") } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 143f8496e7aa..102a4842e3c3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -19,8 +19,12 @@ package com.android.systemui.media import android.content.Context import android.media.MediaRouter2Manager import android.media.session.MediaController +import androidx.annotation.AnyThread +import androidx.annotation.MainThread +import androidx.annotation.WorkerThread import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager @@ -28,25 +32,23 @@ import java.io.FileDescriptor import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton /** * Provides information about the route (ie. device) where playback is occurring. */ -@Singleton class MediaDeviceManager @Inject constructor( private val context: Context, private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, @Main private val fgExecutor: Executor, - private val mediaDataManager: MediaDataManager, - private val dumpManager: DumpManager + @Background private val bgExecutor: Executor, + dumpManager: DumpManager ) : MediaDataManager.Listener, Dumpable { + private val listeners: MutableSet<Listener> = mutableSetOf() - private val entries: MutableMap<String, Token> = mutableMapOf() + private val entries: MutableMap<String, Entry> = mutableMapOf() init { - mediaDataManager.addListener(this) dumpManager.registerDumpable(javaClass.name, this) } @@ -71,7 +73,7 @@ class MediaDeviceManager @Inject constructor( val controller = data.token?.let { MediaController(context, it) } - entry = Token(key, oldKey, controller, + entry = Entry(key, oldKey, controller, localMediaManagerFactory.create(data.packageName)) entries[key] = entry entry.start() @@ -99,6 +101,7 @@ class MediaDeviceManager @Inject constructor( } } + @MainThread private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) { val enabled = device != null val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name) @@ -114,12 +117,13 @@ class MediaDeviceManager @Inject constructor( fun onKeyRemoved(key: String) } - private inner class Token( + private inner class Entry( val key: String, val oldKey: String?, val controller: MediaController?, val localMediaManager: LocalMediaManager ) : LocalMediaManager.DeviceCallback { + val token get() = controller?.sessionToken private var started = false @@ -127,20 +131,27 @@ class MediaDeviceManager @Inject constructor( set(value) { if (!started || value != field) { field = value - processDevice(key, oldKey, value) + fgExecutor.execute { + processDevice(key, oldKey, value) + } } } - fun start() { + + @AnyThread + fun start() = bgExecutor.execute { localMediaManager.registerCallback(this) localMediaManager.startScan() updateCurrent() started = true } - fun stop() { + + @AnyThread + fun stop() = bgExecutor.execute { started = false localMediaManager.stopScan() localMediaManager.unregisterCallback(this) } + fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) @@ -152,14 +163,18 @@ class MediaDeviceManager @Inject constructor( println(" route=$route") } } - override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute { + + override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute { updateCurrent() } + override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) { - fgExecutor.execute { + bgExecutor.execute { updateCurrent() } } + + @WorkerThread private fun updateCurrent() { val device = localMediaManager.getCurrentConnectedDevice() controller?.let { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 3598719fcb3a..ce184aa23a57 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -14,7 +14,7 @@ import javax.inject.Inject class MediaHost @Inject constructor( private val state: MediaHostStateHolder, private val mediaHierarchyManager: MediaHierarchyManager, - private val mediaDataFilter: MediaDataFilter, + private val mediaDataManager: MediaDataManager, private val mediaHostStatesManager: MediaHostStatesManager ) : MediaHostState by state { lateinit var hostView: UniqueObjectHostView @@ -79,12 +79,12 @@ class MediaHost @Inject constructor( // be a delay until the views and the controllers are initialized, leaving us // with either a blank view or the controllers not yet initialized and the // measuring wrong - mediaDataFilter.addListener(listener) + mediaDataManager.addListener(listener) updateViewVisibility() } override fun onViewDetachedFromWindow(v: View?) { - mediaDataFilter.removeListener(listener) + mediaDataManager.removeListener(listener) } }) @@ -113,9 +113,9 @@ class MediaHost @Inject constructor( private fun updateViewVisibility() { visible = if (showsOnlyActiveMedia) { - mediaDataFilter.hasActiveMedia() + mediaDataManager.hasActiveMedia() } else { - mediaDataFilter.hasAnyMedia() + mediaDataManager.hasAnyMedia() } val newVisibility = if (visible) View.VISIBLE else View.GONE if (newVisibility != hostView.visibility) { @@ -289,4 +289,4 @@ interface MediaHostState { * Get a copy of this view state, deepcopying all appropriate members */ fun copy(): MediaHostState -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index c41712c4cf10..1ac3034ea7c1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -52,7 +52,6 @@ class MediaResumeListener @Inject constructor( private var useMediaResumption: Boolean = Utils.useMediaResumption(context) private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue() - private var blockedApps: MutableSet<String> = Utils.getBlockedMediaApps(context) private lateinit var mediaDataManager: MediaDataManager @@ -115,18 +114,8 @@ class MediaResumeListener @Inject constructor( mediaDataManager.setMediaResumptionEnabled(useMediaResumption) } }, Settings.Secure.MEDIA_CONTROLS_RESUME) - - // Listen to changes in which apps are allowed to persist - tunerService.addTunable(object : TunerService.Tunable { - override fun onTuningChanged(key: String?, newValue: String?) { - blockedApps = Utils.getBlockedMediaApps(context) - mediaDataManager.appsBlockedFromResume = blockedApps - } - }, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED) } - fun isResumptionEnabled() = useMediaResumption - private fun loadSavedComponents() { // Make sure list is empty (if we switched users) resumeComponents.clear() @@ -153,10 +142,8 @@ class MediaResumeListener @Inject constructor( } resumeComponents.forEach { - if (!blockedApps.contains(it.packageName)) { - val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it) - browser.findRecentMedia() - } + val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it) + browser.findRecentMedia() } } @@ -165,8 +152,7 @@ class MediaResumeListener @Inject constructor( // If this had been started from a resume state, disconnect now that it's live mediaBrowser?.disconnect() // If we don't have a resume action, check if we haven't already - if (data.resumeAction == null && !data.hasCheckedForResume && - !blockedApps.contains(data.packageName)) { + if (data.resumeAction == null && !data.hasCheckedForResume) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt new file mode 100644 index 000000000000..f695622b943a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -0,0 +1,171 @@ +/* + * 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.media + +import android.content.ComponentName +import android.content.Context +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.util.Log +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins +import java.util.concurrent.Executor +import javax.inject.Inject + +private const val TAG = "MediaSessionBasedFilter" + +/** + * Filters media loaded events for local media sessions while an app is casting. + * + * When an app is casting there can be one remote media sessions and potentially more local media + * sessions. In this situation, there should only be a media object for the remote session. To + * achieve this, update events for the local session need to be filtered. + */ +class MediaSessionBasedFilter @Inject constructor( + context: Context, + private val sessionManager: MediaSessionManager, + @Main private val foregroundExecutor: Executor, + @Background private val backgroundExecutor: Executor +) : MediaDataManager.Listener { + + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + + // Keep track of MediaControllers for a given package to check if an app is casting and it + // filter loaded events for local sessions. + private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> = + LinkedHashMap() + + // Keep track of the key used for the session tokens. This information is used to know when to + // dispatch a removed event so that a media object for a local session will be removed. + private val keyedTokens: MutableMap<String, MutableSet<MediaSession.Token>> = mutableMapOf() + + // Keep track of which media session tokens have associated notifications. + private val tokensWithNotifications: MutableSet<MediaSession.Token> = mutableSetOf() + + private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener { + override fun onActiveSessionsChanged(controllers: List<MediaController>) { + handleControllersChanged(controllers) + } + } + + init { + backgroundExecutor.execute { + val name = ComponentName(context, NotificationListenerWithPlugins::class.java) + sessionManager.addOnActiveSessionsChangedListener(sessionListener, name) + handleControllersChanged(sessionManager.getActiveSessions(name)) + } + } + + /** + * Add a listener for filtered [MediaData] changes + */ + fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + + /** + * Remove a listener that was registered with addListener + */ + fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) + + /** + * May filter loaded events by not passing them along to listeners. + * + * If an app has only one session with playback type PLAYBACK_TYPE_REMOTE, then assuming that + * the app is casting. Sometimes apps will send redundant updates to a local session with + * playback type PLAYBACK_TYPE_LOCAL. These updates should be filtered to improve the usability + * of the media controls. + */ + override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + backgroundExecutor.execute { + info.token?.let { + tokensWithNotifications.add(it) + } + val isMigration = oldKey != null && key != oldKey + if (isMigration) { + keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) } + } + if (info.token != null) { + keyedTokens.get(key)?.let { + tokens -> + tokens.add(info.token) + } ?: run { + val tokens = mutableSetOf(info.token) + keyedTokens.put(key, tokens) + } + } + // Determine if an app is casting by checking if it has a session with playback type + // PLAYBACK_TYPE_REMOTE. + val remoteControllers = packageControllers.get(info.packageName)?.filter { + it.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE + } + // Limiting search to only apps with a single remote session. + val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null + if (isMigration || remote == null || remote.sessionToken == info.token || + !tokensWithNotifications.contains(remote.sessionToken)) { + // Not filtering in this case. Passing the event along to listeners. + dispatchMediaDataLoaded(key, oldKey, info) + } else { + // Filtering this event because the app is casting and the loaded events is for a + // local session. + Log.d(TAG, "filtering key=$key local=${info.token} remote=${remote?.sessionToken}") + // If the local session uses a different notification key, then lets go a step + // farther and dismiss the media data so that media controls for the local session + // don't hang around while casting. + if (!keyedTokens.get(key)!!.contains(remote.sessionToken)) { + dispatchMediaDataRemoved(key) + } + } + } + } + + override fun onMediaDataRemoved(key: String) { + // Queue on background thread to ensure ordering of loaded and removed events is maintained. + backgroundExecutor.execute { + keyedTokens.remove(key) + dispatchMediaDataRemoved(key) + } + } + + private fun dispatchMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + foregroundExecutor.execute { + listeners.toSet().forEach { it.onMediaDataLoaded(key, oldKey, info) } + } + } + + private fun dispatchMediaDataRemoved(key: String) { + foregroundExecutor.execute { + listeners.toSet().forEach { it.onMediaDataRemoved(key) } + } + } + + private fun handleControllersChanged(controllers: List<MediaController>) { + packageControllers.clear() + controllers.forEach { + controller -> + packageControllers.get(controller.packageName)?.let { + tokens -> + tokens.add(controller) + } ?: run { + val tokens = mutableListOf(controller) + packageControllers.put(controller.packageName, tokens) + } + } + tokensWithNotifications.retainAll(controllers.map { it.sessionToken }) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java index 5e2cd9c111b2..970203578e73 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -17,6 +17,7 @@ package com.android.systemui.pip; import android.app.TaskInfo; +import android.content.pm.PackageManager; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -31,27 +32,49 @@ import javax.inject.Singleton; @Singleton public class PipUiEventLogger { + private static final int INVALID_PACKAGE_UID = -1; + private final UiEventLogger mUiEventLogger; + private final PackageManager mPackageManager; - private TaskInfo mTaskInfo; + private String mPackageName; + private int mPackageUid = INVALID_PACKAGE_UID; @Inject - public PipUiEventLogger(UiEventLogger uiEventLogger) { + public PipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) { mUiEventLogger = uiEventLogger; + mPackageManager = packageManager; } public void setTaskInfo(TaskInfo taskInfo) { - mTaskInfo = taskInfo; + if (taskInfo == null) { + mPackageName = null; + mPackageUid = INVALID_PACKAGE_UID; + } else { + mPackageName = taskInfo.topActivity.getPackageName(); + mPackageUid = getUid(mPackageName, taskInfo.userId); + } } /** * Sends log via UiEvent, reference go/uievent for how to debug locally */ public void log(PipUiEventEnum event) { - if (mTaskInfo == null) { + if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) { return; } - mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName()); + mUiEventLogger.log(event, mPackageUid, mPackageName); + } + + private int getUid(String packageName, int userId) { + int uid = INVALID_PACKAGE_UID; + try { + uid = mPackageManager.getApplicationInfoAsUser( + packageName, 0 /* ApplicationInfoFlags */, userId).uid; + } catch (PackageManager.NameNotFoundException e) { + // do nothing. + } + return uid; } /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index bc03ca617dea..59597ac29e09 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -117,7 +117,8 @@ public class CellularTile extends QSTileImpl<SignalState> { return; } String carrierName = mController.getMobileDataNetworkName(); - if (TextUtils.isEmpty(carrierName)) { + boolean isInService = mController.isMobileDataNetworkInService(); + if (TextUtils.isEmpty(carrierName) || !isInService) { carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier); } AlertDialog dialog = new Builder(mContext) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index acc7f81eb722..8ec3db59117d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -41,6 +41,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import java.io.IOException; import java.util.concurrent.Executor; @@ -70,7 +71,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE"; private final RecordingController mController; - + private final KeyguardDismissUtil mKeyguardDismissUtil; private ScreenRecordingAudioSource mAudioSource; private boolean mShowTaps; private boolean mOriginalShowTaps; @@ -83,12 +84,13 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, UiEventLogger uiEventLogger, NotificationManager notificationManager, - CurrentUserContextTracker userContextTracker) { + CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { mController = controller; mLongExecutor = executor; mUiEventLogger = uiEventLogger; mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; + mKeyguardDismissUtil = keyguardDismissUtil; } /** @@ -168,16 +170,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis Intent shareIntent = new Intent(Intent.ACTION_SEND) .setType("video/mp4") .putExtra(Intent.EXTRA_STREAM, shareUri); - String shareLabel = getResources().getString(R.string.screenrecord_share_label); + mKeyguardDismissUtil.executeWhenUnlocked(() -> { + String shareLabel = getResources().getString(R.string.screenrecord_share_label); + startActivity(Intent.createChooser(shareIntent, shareLabel) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + // Remove notification + mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); + return false; + }, false); // Close quick shade sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - - // Remove notification - mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); - - startActivity(Intent.createChooser(shareIntent, shareLabel) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); break; } return Service.START_STICKY; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 6b8afffa065a..5bee9a762f6f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -486,6 +486,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } } + row.showAppOpsIcons(entry.mActiveAppOps); row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 84108b196d72..4b244bb18975 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -76,9 +76,14 @@ public class AppOpsCoordinator implements Coordinator { // extend the lifetime of foreground notification services to show for at least 5 seconds mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender); + // listen for new notifications to add appOps + mNotifPipeline.addCollectionListener(mNotifCollectionListener); + // filter out foreground service notifications that aren't necessary anymore mNotifPipeline.addPreGroupFilter(mNotifFilter); + // when appOps change, update any relevant notifications to update appOps for + mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged); } /** @@ -169,4 +174,82 @@ public class AppOpsCoordinator implements Coordinator { } } }; + + /** + * Adds appOps to incoming and updating notifications + */ + private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() { + @Override + public void onEntryAdded(NotificationEntry entry) { + tagAppOps(entry); + } + + @Override + public void onEntryUpdated(NotificationEntry entry) { + tagAppOps(entry); + } + + private void tagAppOps(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); + // note: requires that the ForegroundServiceController is updating their appOps first + ArraySet<Integer> activeOps = + mForegroundServiceController.getAppOps( + sbn.getUser().getIdentifier(), + sbn.getPackageName()); + + entry.mActiveAppOps.clear(); + if (activeOps != null) { + entry.mActiveAppOps.addAll(activeOps); + } + } + }; + + private void onAppOpsChanged(int code, int uid, String packageName, boolean active) { + mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active)); + } + + /** + * Update the appOp for the posted notification associated with the current foreground service + * + * @param code code for appOp to add/remove + * @param uid of user the notification is sent to + * @param packageName package that created the notification + * @param active whether the appOpCode is active or not + */ + private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) { + Assert.isMainThread(); + + int userId = UserHandle.getUserId(uid); + + // Update appOps of the app's posted notifications with standard layouts + final ArraySet<String> notifKeys = + mForegroundServiceController.getStandardLayoutKeys(userId, packageName); + if (notifKeys != null) { + boolean changed = false; + for (int i = 0; i < notifKeys.size(); i++) { + final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i)); + if (entry != null + && uid == entry.getSbn().getUid() + && packageName.equals(entry.getSbn().getPackageName())) { + if (active) { + changed |= entry.mActiveAppOps.add(code); + } else { + changed |= entry.mActiveAppOps.remove(code); + } + } + } + if (changed) { + mNotifFilter.invalidateList(); + } + } + } + + private NotificationEntry findNotificationEntryWithKey(String key) { + for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) { + if (entry.getKey().equals(key)) { + return entry; + } + } + return null; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java new file mode 100644 index 000000000000..28c53dc6d9b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java @@ -0,0 +1,214 @@ +/* + * 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.notification.row; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.service.notification.StatusBarNotification; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.R; + +/** + * The guts of a notification revealed when performing a long press. + */ +public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent { + private static final String TAG = "AppOpsGuts"; + + private PackageManager mPm; + + private String mPkg; + private String mAppName; + private int mAppUid; + private StatusBarNotification mSbn; + private ArraySet<Integer> mAppOps; + private MetricsLogger mMetricsLogger; + private OnSettingsClickListener mOnSettingsClickListener; + private NotificationGuts mGutsContainer; + private UiEventLogger mUiEventLogger; + + private OnClickListener mOnOk = v -> { + mGutsContainer.closeControls(v, false); + }; + + public AppOpsInfo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public interface OnSettingsClickListener { + void onClick(View v, String pkg, int uid, ArraySet<Integer> ops); + } + + public void bindGuts(final PackageManager pm, + final OnSettingsClickListener onSettingsClick, + final StatusBarNotification sbn, + final UiEventLogger uiEventLogger, + ArraySet<Integer> activeOps) { + mPkg = sbn.getPackageName(); + mSbn = sbn; + mPm = pm; + mAppName = mPkg; + mOnSettingsClickListener = onSettingsClick; + mAppOps = activeOps; + mUiEventLogger = uiEventLogger; + + bindHeader(); + bindPrompt(); + bindButtons(); + + logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN); + mMetricsLogger = new MetricsLogger(); + mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true); + } + + private void bindHeader() { + // Package name + Drawable pkgicon = null; + ApplicationInfo info; + try { + info = mPm.getApplicationInfo(mPkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + mAppUid = mSbn.getUid(); + mAppName = String.valueOf(mPm.getApplicationLabel(info)); + pkgicon = mPm.getApplicationIcon(info); + } + } catch (PackageManager.NameNotFoundException e) { + // app is gone, just show package name and generic icon + pkgicon = mPm.getDefaultActivityIcon(); + } + ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); + ((TextView) findViewById(R.id.pkgname)).setText(mAppName); + } + + private void bindPrompt() { + final TextView prompt = findViewById(R.id.prompt); + prompt.setText(getPrompt()); + } + + private void bindButtons() { + View settings = findViewById(R.id.settings); + settings.setOnClickListener((View view) -> { + mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps); + }); + TextView ok = findViewById(R.id.ok); + ok.setOnClickListener(mOnOk); + ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate()); + } + + private String getPrompt() { + if (mAppOps == null || mAppOps.size() == 0) { + return ""; + } else if (mAppOps.size() == 1) { + if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { + return mContext.getString(R.string.appops_camera); + } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { + return mContext.getString(R.string.appops_microphone); + } else { + return mContext.getString(R.string.appops_overlay); + } + } else if (mAppOps.size() == 2) { + if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { + if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { + return mContext.getString(R.string.appops_camera_mic); + } else { + return mContext.getString(R.string.appops_camera_overlay); + } + } else { + return mContext.getString(R.string.appops_mic_overlay); + } + } else { + return mContext.getString(R.string.appops_camera_mic_overlay); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mGutsContainer != null && + event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + if (mGutsContainer.isExposed()) { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_opened_accessibility, mAppName)); + } else { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_closed_accessibility, mAppName)); + } + } + } + + @Override + public void setGutsParent(NotificationGuts guts) { + mGutsContainer = guts; + } + + @Override + public boolean willBeRemoved() { + return false; + } + + @Override + public boolean shouldBeSaved() { + return false; + } + + @Override + public boolean needsFalsingProtection() { + return false; + } + + @Override + public View getContentView() { + return this; + } + + @Override + public boolean handleCloseControls(boolean save, boolean force) { + logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_CLOSE); + if (mMetricsLogger != null) { + mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false); + } + return false; + } + + @Override + public int getActualHeight() { + return getHeight(); + } + + private void logUiEvent(NotificationAppOpsEvent event) { + if (mSbn != null) { + mUiEventLogger.logWithInstanceId(event, + mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 8ead7bf2322f..94e12e82f850 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -239,6 +239,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mShowNoBackground; private ExpandableNotificationRow mNotificationParent; private OnExpandClickListener mOnExpandClickListener; + private View.OnClickListener mOnAppOpsClickListener; // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. @@ -1142,6 +1143,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView items.add(NotificationMenuRow.createPartialConversationItem(mContext)); items.add(NotificationMenuRow.createInfoItem(mContext)); items.add(NotificationMenuRow.createSnoozeItem(mContext)); + items.add(NotificationMenuRow.createAppOpsItem(mContext)); mMenuRow.setMenuItems(items); } if (existed) { @@ -1607,6 +1609,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView RowContentBindStage rowContentBindStage, OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, + OnAppOpsClickListener onAppOpsClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier) { @@ -1626,6 +1629,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mRowContentBindStage = rowContentBindStage; mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; + setAppOpsOnClickListener(onAppOpsClickListener); mFalsingManager = falsingManager; mStatusbarStateController = statusBarStateController; mPeopleNotificationIdentifier = peopleNotificationIdentifier; @@ -1682,6 +1686,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView requestLayout(); } + public void showAppOpsIcons(ArraySet<Integer> activeOps) { + if (mIsSummaryWithChildren) { + mChildrenContainer.showAppOpsIcons(activeOps); + } + mPrivateLayout.showAppOpsIcons(activeOps); + mPublicLayout.showAppOpsIcons(activeOps); + } + /** Sets the last time the notification being displayed audibly alerted the user. */ public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) { if (NotificationUtils.useNewInterruptionModel(mContext)) { @@ -1710,6 +1722,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently); } + public View.OnClickListener getAppOpsOnClickListener() { + return mOnAppOpsClickListener; + } + + void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) { + mOnAppOpsClickListener = v -> { + createMenu(); + NotificationMenuRowPlugin provider = getProvider(); + if (provider == null) { + return; + } + MenuItem menuItem = provider.getAppOpsMenuItem(mContext); + if (menuItem != null) { + l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem); + } + }; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index b132caf56265..7a6109d2ce78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -63,6 +63,7 @@ public class ExpandableNotificationRowController { private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; + private final ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private final NotificationGutsManager mNotificationGutsManager; private Runnable mOnDismissRunnable; private final FalsingManager mFalsingManager; @@ -100,6 +101,7 @@ public class ExpandableNotificationRowController { mStatusBarStateController = statusBarStateController; mNotificationGutsManager = notificationGutsManager; mOnDismissRunnable = onDismissRunnable; + mOnAppOpsClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; @@ -120,6 +122,7 @@ public class ExpandableNotificationRowController { mRowContentBindStage, mOnExpandClickListener, mMediaManager, + mOnAppOpsClickListener, mFalsingManager, mStatusBarStateController, mPeopleNotificationIdentifier diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 41a9b187129c..1f5b063b0aa2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1567,6 +1567,18 @@ public class NotificationContentView extends FrameLayout { return header; } + public void showAppOpsIcons(ArraySet<Integer> activeOps) { + if (mContractedChild != null) { + mContractedWrapper.showAppOpsIcons(activeOps); + } + if (mExpandedChild != null) { + mExpandedWrapper.showAppOpsIcons(activeOps); + } + if (mHeadsUpChild != null) { + mHeadsUpWrapper.showAppOpsIcons(activeOps); + } + } + /** Sets whether the notification being displayed audibly alerted the user. */ public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { if (mContractedChild != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index f543db74d91a..25a0ea515ea3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -42,6 +42,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -539,12 +541,21 @@ public class NotificationConversationInfo extends LinearLayout implements && Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0) == 1; + Drawable person = mIconFactory.getBaseIconDrawable(mShortcutInfo); + if (person == null) { + person = mContext.getDrawable(R.drawable.ic_person).mutate(); + TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent}); + int colorAccent = ta.getColor(0, 0); + ta.recycle(); + person.setTint(colorAccent); + } + PriorityOnboardingDialogController controller = mBuilderProvider.get() .setContext(mUserContext) .setView(onboardingView) .setIgnoresDnd(ignoreDnd) .setShowsAsBubble(showAsBubble) - .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo)) + .setIcon(person) .setBadge(mIconFactory.getAppBadge( mPackageName, UserHandle.getUserId(mSbn.getUid()))) .setOnSettingsClick(mOnConversationSettingsClickListener) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 3eed18a90836..24883f51a984 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -264,6 +264,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx try { if (gutsView instanceof NotificationSnooze) { initializeSnoozeView(row, (NotificationSnooze) gutsView); + } else if (gutsView instanceof AppOpsInfo) { + initializeAppOpsInfo(row, (AppOpsInfo) gutsView); } else if (gutsView instanceof NotificationInfo) { initializeNotificationInfo(row, (NotificationInfo) gutsView); } else if (gutsView instanceof NotificationConversationInfo) { @@ -301,6 +303,36 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } /** + * Sets up the {@link AppOpsInfo} inside the notification row's guts. + * + * @param row view to set up the guts for + * @param appOpsInfoView view to set up/bind within {@code row} + */ + private void initializeAppOpsInfo( + final ExpandableNotificationRow row, + AppOpsInfo appOpsInfoView) { + NotificationGuts guts = row.getGuts(); + StatusBarNotification sbn = row.getEntry().getSbn(); + UserHandle userHandle = sbn.getUser(); + PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, + userHandle.getIdentifier()); + + AppOpsInfo.OnSettingsClickListener onSettingsClick = + (View v, String pkg, int uid, ArraySet<Integer> ops) -> { + mUiEventLogger.logWithInstanceId( + NotificationAppOpsEvent.NOTIFICATION_APP_OPS_SETTINGS_CLICK, + sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId()); + mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS); + guts.resetFalsingCheck(); + startAppOpsSettingsActivity(pkg, uid, ops, row); + }; + if (!row.getEntry().mActiveAppOps.isEmpty()) { + appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, mUiEventLogger, + row.getEntry().mActiveAppOps); + } + } + + /** * Sets up the {@link NotificationInfo} inside the notification row's guts. * @param row view to set up the guts for * @param notificationInfoView view to set up/bind within {@code row} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index a167925a6358..5e1e3b255867 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -76,6 +76,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private Context mContext; private FrameLayout mMenuContainer; private NotificationMenuItem mInfoItem; + private MenuItem mAppOpsItem; private MenuItem mSnoozeItem; private ArrayList<MenuItem> mLeftMenuItems; private ArrayList<MenuItem> mRightMenuItems; @@ -137,6 +138,11 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } @Override + public MenuItem getAppOpsMenuItem(Context context) { + return mAppOpsItem; + } + + @Override public MenuItem getSnoozeMenuItem(Context context) { return mSnoozeItem; } @@ -258,6 +264,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl // Only show snooze for non-foreground notifications, and if the setting is on mSnoozeItem = createSnoozeItem(mContext); } + mAppOpsItem = createAppOpsItem(mContext); NotificationEntry entry = mParent.getEntry(); int personNotifType = mPeopleNotificationIdentifier .getPeopleNotificationType(entry.getSbn(), entry.getRanking()); @@ -273,6 +280,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl mRightMenuItems.add(mSnoozeItem); } mRightMenuItems.add(mInfoItem); + mRightMenuItems.add(mAppOpsItem); mLeftMenuItems.addAll(mRightMenuItems); populateMenuViews(); @@ -680,6 +688,14 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl R.drawable.ic_settings); } + static MenuItem createAppOpsItem(Context context) { + AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate( + R.layout.app_ops_info, null, false); + MenuItem info = new NotificationMenuItem(context, null, appOpsContent, + -1 /*don't show in slow swipe menu */); + return info; + } + private void addMenuView(MenuItem item, ViewGroup parent) { View menuView = item.getMenuView(); if (menuView != null) { 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 86fc352f6fe9..c747a7c300b7 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 @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row.wrapper; import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y; +import android.app.AppOpsManager; import android.app.Notification; import android.content.Context; import android.util.ArraySet; @@ -63,6 +64,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private TextView mHeaderText; private TextView mAppNameText; private ImageView mWorkProfileImage; + private View mCameraIcon; + private View mMicIcon; + private View mOverlayIcon; + private View mAppOps; private View mAudiblyAlertedIcon; private FrameLayout mIconContainer; @@ -103,6 +108,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } }, TRANSFORMING_VIEW_TITLE); resolveHeaderViews(); + addAppOpsOnClickListener(row); } protected void resolveHeaderViews() { @@ -113,6 +119,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); + mCameraIcon = mView.findViewById(com.android.internal.R.id.camera); + mMicIcon = mView.findViewById(com.android.internal.R.id.mic); + mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay); + mAppOps = mView.findViewById(com.android.internal.R.id.app_ops); mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon); if (mNotificationHeader != null) { mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd); @@ -120,6 +130,38 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } } + private void addAppOpsOnClickListener(ExpandableNotificationRow row) { + View.OnClickListener listener = row.getAppOpsOnClickListener(); + if (mNotificationHeader != null) { + mNotificationHeader.setAppOpsOnClickListener(listener); + } + if (mAppOps != null) { + mAppOps.setOnClickListener(listener); + } + } + + /** + * Shows or hides 'app op in use' icons based on app usage. + */ + @Override + public void showAppOpsIcons(ArraySet<Integer> appOps) { + if (appOps == null) { + return; + } + if (mOverlayIcon != null) { + mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) + ? View.VISIBLE : View.GONE); + } + if (mCameraIcon != null) { + mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA) + ? View.VISIBLE : View.GONE); + } + if (mMicIcon != null) { + mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO) + ? View.VISIBLE : View.GONE); + } + } + @Override public void onContentUpdated(ExpandableNotificationRow row) { super.onContentUpdated(row); @@ -243,6 +285,15 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); } + if (mCameraIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mCameraIcon); + } + if (mMicIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mMicIcon); + } + if (mOverlayIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon); + } if (mAudiblyAlertedIcon != null) { mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 4bf279444462..30080e3d8cc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -96,6 +96,14 @@ public abstract class NotificationViewWrapper implements TransformableView { public void onContentUpdated(ExpandableNotificationRow row) { } + /** + * Show a set of app opp icons in the layout. + * + * @param appOps which app ops to show + */ + public void showAppOpsIcons(ArraySet<Integer> appOps) { + } + public void onReinflated() { if (shouldClearBackgroundOnReapply()) { mBackgroundColor = 0; 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 5edcde1cd3c7..99691b710cc2 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 @@ -1302,6 +1302,20 @@ public class NotificationChildrenContainer extends ViewGroup { mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader); } + /** + * Show a set of app opp icons in the layout. + * + * @param appOps which app ops to show + */ + public void showAppOpsIcons(ArraySet<Integer> appOps) { + if (mNotificationHeaderWrapper != null) { + mNotificationHeaderWrapper.showAppOpsIcons(appOps); + } + if (mNotificationHeaderWrapperLowPriority != null) { + mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps); + } + } + public void setRecentlyAudiblyAlerted(boolean audiblyAlertedRecently) { if (mNotificationHeaderWrapper != null) { mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 304fe0090e77..00a932cb1e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -231,7 +231,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa } } - Dependency.get(ProtoTracer.class).add(this); mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT, ViewConfiguration.getLongPressTimeout()); @@ -286,6 +285,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa */ public void onNavBarAttached() { mIsAttached = true; + Dependency.get(ProtoTracer.class).add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); updateIsEnabled(); startTracking(); @@ -296,6 +296,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa */ public void onNavBarDetached() { mIsAttached = false; + Dependency.get(ProtoTracer.class).remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); updateIsEnabled(); stopTracking(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 93d4a5e93cb5..5bb3c836586c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -113,7 +113,8 @@ public class NavigationBarView extends FrameLayout implements int mNavigationIconHints = 0; private int mNavBarMode; - private final Region mActiveRegion = new Region(); + private final Region mTmpRegion = new Region(); + private final int[] mTmpPosition = new int[2]; private Rect mTmpBounds = new Rect(); private KeyButtonDrawable mBackIcon; @@ -252,24 +253,14 @@ public class NavigationBarView extends FrameLayout implements private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> { // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully // gestural mode, the entire nav bar should be touchable. - if (!mEdgeBackGestureHandler.isHandlingGestures() || mImeVisible) { + if (!mEdgeBackGestureHandler.isHandlingGestures()) { info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); return; } info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - ButtonDispatcher imeSwitchButton = getImeSwitchButton(); - if (imeSwitchButton.getVisibility() == VISIBLE) { - // If the IME is not up, but the ime switch button is visible, then make sure that - // button is touchable - int[] loc = new int[2]; - View buttonView = imeSwitchButton.getCurrentView(); - buttonView.getLocationInWindow(loc); - info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(), - loc[1] + buttonView.getHeight()); - return; - } - info.touchableRegion.setEmpty(); + info.touchableRegion.set(getButtonLocations(false /* includeFloatingRotationButton */, + false /* inScreen */)); }; private final Consumer<Boolean> mRotationButtonListener = (visible) -> { @@ -947,30 +938,44 @@ public class NavigationBarView extends FrameLayout implements * Notifies the overview service of the active touch regions. */ public void notifyActiveTouchRegions() { - mActiveRegion.setEmpty(); - updateButtonLocation(getBackButton()); - updateButtonLocation(getHomeButton()); - updateButtonLocation(getRecentsButton()); - updateButtonLocation(getImeSwitchButton()); - updateButtonLocation(getAccessibilityButton()); - if (mFloatingRotationButton.isVisible()) { - View floatingRotationView = mFloatingRotationButton.getCurrentView(); - floatingRotationView.getBoundsOnScreen(mTmpBounds); - mActiveRegion.op(mTmpBounds, Op.UNION); + mOverviewProxyService.onActiveNavBarRegionChanges( + getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */)); + } + + private Region getButtonLocations(boolean includeFloatingRotationButton, + boolean inScreenSpace) { + mTmpRegion.setEmpty(); + updateButtonLocation(getBackButton(), inScreenSpace); + updateButtonLocation(getHomeButton(), inScreenSpace); + updateButtonLocation(getRecentsButton(), inScreenSpace); + updateButtonLocation(getImeSwitchButton(), inScreenSpace); + updateButtonLocation(getAccessibilityButton(), inScreenSpace); + if (includeFloatingRotationButton && mFloatingRotationButton.isVisible()) { + updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace); } else { - updateButtonLocation(getRotateSuggestionButton()); + updateButtonLocation(getRotateSuggestionButton(), inScreenSpace); } - mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion); + return mTmpRegion; } - private void updateButtonLocation(ButtonDispatcher button) { + private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace) { View view = button.getCurrentView(); if (view == null || !button.isVisible()) { return; } + updateButtonLocation(view, inScreenSpace); + } - view.getBoundsOnScreen(mTmpBounds); - mActiveRegion.op(mTmpBounds, Op.UNION); + private void updateButtonLocation(View view, boolean inScreenSpace) { + if (inScreenSpace) { + view.getBoundsOnScreen(mTmpBounds); + } else { + view.getLocationInWindow(mTmpPosition); + mTmpBounds.set(mTmpPosition[0], mTmpPosition[1], + mTmpPosition[0] + view.getWidth(), + mTmpPosition[1] + view.getHeight()); + } + mTmpRegion.op(mTmpBounds, Op.UNION); } private void updateOrientationViews() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java index 23d03a4b225a..e1ff9a236b02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java @@ -172,6 +172,24 @@ public class KeyButtonDrawable extends Drawable { } @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (changed) { + // End any existing animations when the visibility changes + jumpToCurrentState(); + } + return changed; + } + + @Override + public void jumpToCurrentState() { + super.jumpToCurrentState(); + if (mAnimatedDrawable != null) { + mAnimatedDrawable.jumpToCurrentState(); + } + } + + @Override public void setAlpha(int alpha) { mState.mAlpha = alpha; mIconPaint.setAlpha(alpha); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java index 2d8784dc41bd..9e1485dba8bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java @@ -225,6 +225,16 @@ public class KeyButtonRipple extends Drawable { } @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (changed) { + // End any existing animations when the visibility changes + jumpToCurrentState(); + } + return changed; + } + + @Override public void jumpToCurrentState() { endAnimations("jumpToCurrentState", false /* cancel */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index cf83603997c0..eb2d9bce6c4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -418,6 +418,10 @@ public class MobileSignalController extends SignalController< return (mServiceState != null && mServiceState.isEmergencyOnly()); } + public boolean isInService() { + return Utils.isInService(mServiceState); + } + private boolean isRoaming() { // During a carrier change, roaming indications need to be supressed. if (isCarrierNetworkChangeActive()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 95a97729936b..1dbb228f58b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -37,6 +37,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D DataUsageController getMobileDataController(); DataSaverController getDataSaverController(); String getMobileDataNetworkName(); + boolean isMobileDataNetworkInService(); int getNumberSubscriptions(); boolean hasVoiceCallingFeature(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index f41a27cf4c64..5b3c3a38c568 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -445,6 +445,12 @@ public class NetworkControllerImpl extends BroadcastReceiver } @Override + public boolean isMobileDataNetworkInService() { + MobileSignalController controller = getDataController(); + return controller != null && controller.isInService(); + } + + @Override public int getNumberSubscriptions() { return mMobileSignalControllers.size(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 2512257fdc19..9ad2aa257aa0 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -63,8 +63,7 @@ public class TunerServiceImpl extends TunerService { private static final String[] RESET_BLACKLIST = new String[] { QSTileHost.TILES_SETTING, Settings.Secure.DOZE_ALWAYS_ON, - Settings.Secure.MEDIA_CONTROLS_RESUME, - Secure.MEDIA_CONTROLS_RESUME_BLOCKED + Settings.Secure.MEDIA_CONTROLS_RESUME }; private final Observer mObserver = new Observer(); diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 21f7a85b3277..9e3fcc401ffa 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -21,15 +21,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.provider.Settings; -import android.text.TextUtils; import android.view.View; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.function.Consumer; public class Utils { @@ -144,21 +141,4 @@ public class Utils { Settings.Secure.MEDIA_CONTROLS_RESUME, 1); return useQsMediaPlayer(context) && flag > 0; } - - /** - * Get the set of apps for which the user has manually disabled resumption. - */ - public static Set<String> getBlockedMediaApps(Context context) { - String list = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED); - if (TextUtils.isEmpty(list)) { - return new HashSet<>(); - } - String[] names = list.split(":"); - Set<String> apps = new HashSet<>(names.length); - for (String s : names) { - apps.add(s); - } - return apps; - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index e967a5d607eb..60f0cd9da5f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -82,7 +82,8 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { allowTestableLooperAsMainThread(); MockitoAnnotations.initMocks(this); - mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler); + mFsc = new ForegroundServiceController( + mEntryManager, mAppOpsController, mMainHandler); mListener = new ForegroundServiceNotificationListener( mContext, mFsc, mEntryManager, mNotifPipeline, mock(ForegroundServiceLifetimeExtender.class), mClock); @@ -114,6 +115,85 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } @Test + public void testAppOps_appOpChangedBeforeNotificationExists() { + // GIVEN app op exists, but notification doesn't exist in NEM yet + NotificationEntry entry = createFgEntry(); + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // WHEN the notification is added + mEntryListener.onPendingEntryAdded(entry); + + // THEN the app op is added to the entry + Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + } + + @Test + public void testAppOps_appOpAddedToForegroundNotif() { + // GIVEN a notification associated with a foreground service + NotificationEntry entry = addFgEntry(); + when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); + + // WHEN we are notified of a new app op for this notification + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + + // THEN the app op is added to the entry + Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // THEN notification views are updated since the notification is visible + verify(mEntryManager, times(1)).updateNotifications(anyString()); + } + + @Test + public void testAppOpsAlreadyAdded() { + // GIVEN a foreground service associated notification that already has the correct app op + NotificationEntry entry = addFgEntry(); + entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA); + when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); + + // WHEN we are notified of the same app op for this notification + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + + // THEN the app op still exists in the notification entry + Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // THEN notification views aren't updated since nothing changed + verify(mEntryManager, never()).updateNotifications(anyString()); + } + + @Test + public void testAppOps_appOpNotAddedToUnrelatedNotif() { + // GIVEN no notification entries correspond to the newly updated appOp + NotificationEntry entry = addFgEntry(); + when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null); + + // WHEN a new app op is detected + mFsc.onAppOpChanged( + AppOpsManager.OP_CAMERA, + entry.getSbn().getUid(), + entry.getSbn().getPackageName(), + true); + + // THEN we won't see appOps on the entry + Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + + // THEN notification views aren't updated since nothing changed + verify(mEntryManager, never()).updateNotifications(anyString()); + } + + @Test public void testAppOpsCRUD() { // no crash on remove that doesn't exist mFsc.onAppOpChanged(9, 1000, "pkg1", false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 8f082c15df36..ade329011b7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -47,6 +47,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import org.junit.Before; @@ -82,6 +83,8 @@ public class AppOpsControllerTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock(stubOnly = true) private AudioManager mAudioManager; + @Mock() + private BroadcastDispatcher mDispatcher; @Mock(stubOnly = true) private AudioManager.AudioRecordingCallback mRecordingCallback; @Mock(stubOnly = true) @@ -120,7 +123,8 @@ public class AppOpsControllerTest extends SysuiTestCase { mTestableLooper.getLooper(), mDumpManager, mFlagsCache, - mAudioManager + mAudioManager, + mDispatcher ); } @@ -128,12 +132,14 @@ public class AppOpsControllerTest extends SysuiTestCase { public void testOnlyListenForFewOps() { mController.setListening(true); verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController); + verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any()); } @Test public void testStopListening() { mController.setListening(false); verify(mAppOpsManager, times(1)).stopWatchingActive(mController); + verify(mDispatcher, times(1)).unregisterReceiver(mController); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 3ef60274cd76..c82bb9bbd37c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -103,8 +103,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testAod_usesLightSensor() { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); mSensor.sendSensorEvent(3); @@ -114,8 +112,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testAod_usesDebugValue() throws Exception { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.onScreenState(Display.STATE_DOZE); Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS); intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1); @@ -166,14 +163,13 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void testDozingAfterPulsing_pausesLightSensor() throws Exception { + public void testScreenOffAfterPulsing_pausesLightSensor() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING); mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE); mScreen.transitionTo(DOZE_PULSE_DONE, DOZE); - mScreen.onScreenState(Display.STATE_DOZE); mSensor.sendSensorEvent(1); @@ -181,6 +177,17 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.onScreenState(Display.STATE_DOZE); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mSensor.sendSensorEvent(1); + + assertEquals(1, mServiceFake.screenBrightness); + } + + @Test public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, @@ -191,12 +198,15 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); + mScreen.onScreenState(Display.STATE_DOZE); + mScreen.onScreenState(Display.STATE_OFF); } @Test public void testNoBrightnessDeliveredAfterFinish() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.onScreenState(Display.STATE_DOZE); mScreen.transitionTo(DOZE_AOD, FINISH); mSensor.sendSensorEvent(1); diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index ac8c6710e041..5e2e7c075043 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -49,6 +49,7 @@ import android.testing.TestableLooper; import android.util.FeatureFlagUtils; import android.view.IWindowManager; import android.view.View; +import android.view.WindowManagerPolicyConstants; import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -241,6 +242,28 @@ public class GlobalActionsDialogTest extends SysuiTestCase { verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS); } + @Test + public void testShouldShowScreenshot() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.integer.config_navBarInteractionMode, + WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON); + + GlobalActionsDialog.ScreenshotAction screenshotAction = + mGlobalActionsDialog.makeScreenshotActionForTesting(); + assertThat(screenshotAction.shouldShow()).isTrue(); + } + + @Test + public void testShouldNotShowScreenshot() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.integer.config_navBarInteractionMode, + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON); + + GlobalActionsDialog.ScreenshotAction screenshotAction = + mGlobalActionsDialog.makeScreenshotActionForTesting(); + assertThat(screenshotAction.shouldShow()).isFalse(); + } + private void verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent event) { mTestableLooper.processAllMessages(); verify(mUiEventLogger, times(1)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index 2e794a40d238..609b8474d134 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -34,19 +33,23 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.ArrayList; -import java.util.Map; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class MediaDataCombineLatestTest extends SysuiTestCase { + @Rule public MockitoRule mockito = MockitoJUnit.rule(); + private static final String KEY = "TEST_KEY"; private static final String OLD_KEY = "TEST_KEY_OLD"; private static final String APP = "APP"; @@ -59,39 +62,26 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { private MediaDataCombineLatest mManager; - @Mock private MediaDataManager mDataSource; - @Mock private MediaDeviceManager mDeviceSource; @Mock private MediaDataManager.Listener mListener; - private MediaDataManager.Listener mDataListener; - private MediaDeviceManager.Listener mDeviceListener; - private MediaData mMediaData; private MediaDeviceData mDeviceData; @Before public void setUp() { - mDataSource = mock(MediaDataManager.class); - mDeviceSource = mock(MediaDeviceManager.class); - mListener = mock(MediaDataManager.Listener.class); - - mManager = new MediaDataCombineLatest(mDataSource, mDeviceSource); - - mDataListener = captureDataListener(); - mDeviceListener = captureDeviceListener(); - + mManager = new MediaDataCombineLatest(); mManager.addListener(mListener); mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true, - false, KEY, false); + false, KEY, false, false, false); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } @Test public void eventNotEmittedWithoutDevice() { // WHEN data source emits an event without device data - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -99,7 +89,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutMedia() { // WHEN device source emits an event without media data - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -107,9 +97,9 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterDeviceFirst() { // GIVEN that a device event has already been received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -119,9 +109,9 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterMediaFirst() { // GIVEN that media event has already been received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // WHEN device event is received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -131,11 +121,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyMediaFirst() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); @@ -145,11 +135,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyDeviceFirst() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); @@ -159,12 +149,12 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyMediaAfter() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); reset(mListener); // WHEN a second key migration event is received for media - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); // THEN the key has already been migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); @@ -174,12 +164,12 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyDeviceAfter() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); reset(mListener); // WHEN a second key migration event is received for the device - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the key has already be migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); @@ -189,60 +179,34 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemoved() { // WHEN media data is removed without first receiving device or data - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDataRemoved(KEY); // THEN a removed event isn't emitted verify(mListener, never()).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataRemovedAfterMediaEvent() { - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataRemovedAfterDeviceEvent() { - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed - mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); + mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); // THEN the listener gets a load event with the correct keys ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture()); } - - @Test - public void getDataIncludesDevice() { - // GIVEN that device and media events have been received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - - // THEN the result of getData includes device info - Map<String, MediaData> results = mManager.getData(); - assertThat(results.get(KEY)).isNotNull(); - assertThat(results.get(KEY).getDevice()).isEqualTo(mDeviceData); - } - - private MediaDataManager.Listener captureDataListener() { - ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass( - MediaDataManager.Listener.class); - verify(mDataSource).addListener(captor.capture()); - return captor.getValue(); - } - - private MediaDeviceManager.Listener captureDeviceListener() { - ArgumentCaptor<MediaDeviceManager.Listener> captor = ArgumentCaptor.forClass( - MediaDeviceManager.Listener.class); - verify(mDeviceSource).addListener(captor.capture()); - return captor.getValue(); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index afb64a7649b4..36b6527167f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -32,6 +32,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.concurrent.Executor @@ -56,8 +57,6 @@ private fun <T> any(): T = Mockito.any() class MediaDataFilterTest : SysuiTestCase() { @Mock - private lateinit var combineLatest: MediaDataCombineLatest - @Mock private lateinit var listener: MediaDataManager.Listener @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher @@ -78,8 +77,9 @@ class MediaDataFilterTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - mediaDataFilter = MediaDataFilter(combineLatest, broadcastDispatcher, mediaResumeListener, - mediaDataManager, lockscreenUserManager, executor) + mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener, + lockscreenUserManager, executor) + mediaDataFilter.mediaDataManager = mediaDataManager mediaDataFilter.addListener(listener) // Start all tests as main user @@ -152,8 +152,9 @@ class MediaDataFilterTest : SysuiTestCase() { @Test fun testOnUserSwitched_addsNewUserControls() { // GIVEN that we had some media for both users - val dataMap = mapOf(KEY to dataMain, KEY_ALT to dataGuest) - `when`(combineLatest.getData()).thenReturn(dataMap) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) + mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest) + reset(listener) // and we switch to guest user setUser(USER_GUEST) @@ -213,4 +214,4 @@ class MediaDataFilterTest : SysuiTestCase() { verify(mediaDataManager).setTimedOut(eq(KEY), eq(true)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 568dd6aafc3d..b47ee2921380 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -15,6 +15,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -23,9 +24,13 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever @@ -47,6 +52,7 @@ private fun <T> anyObject(): T { @RunWith(AndroidTestingRunner::class) class MediaDataManagerTest : SysuiTestCase() { + @JvmField @Rule val mockito = MockitoJUnit.rule() @Mock lateinit var mediaControllerFactory: MediaControllerFactory @Mock lateinit var controller: MediaController lateinit var session: MediaSession @@ -57,19 +63,36 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock lateinit var broadcastDispatcher: BroadcastDispatcher @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener @Mock lateinit var mediaResumeListener: MediaResumeListener + @Mock lateinit var mediaSessionBasedFilter: MediaSessionBasedFilter + @Mock lateinit var mediaDeviceManager: MediaDeviceManager + @Mock lateinit var mediaDataCombineLatest: MediaDataCombineLatest + @Mock lateinit var mediaDataFilter: MediaDataFilter + @Mock lateinit var listener: MediaDataManager.Listener @Mock lateinit var pendingIntent: PendingIntent - @JvmField @Rule val mockito = MockitoJUnit.rule() lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification + @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> @Before fun setup() { foregroundExecutor = FakeExecutor(FakeSystemClock()) backgroundExecutor = FakeExecutor(FakeSystemClock()) - mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor, - mediaControllerFactory, broadcastDispatcher, dumpManager, - mediaTimeoutListener, mediaResumeListener, useMediaResumption = true, - useQsMediaPlayer = true) + mediaDataManager = MediaDataManager( + context = context, + backgroundExecutor = backgroundExecutor, + foregroundExecutor = foregroundExecutor, + mediaControllerFactory = mediaControllerFactory, + broadcastDispatcher = broadcastDispatcher, + dumpManager = dumpManager, + mediaTimeoutListener = mediaTimeoutListener, + mediaResumeListener = mediaResumeListener, + mediaSessionBasedFilter = mediaSessionBasedFilter, + mediaDeviceManager = mediaDeviceManager, + mediaDataCombineLatest = mediaDataCombineLatest, + mediaDataFilter = mediaDataFilter, + useMediaResumption = true, + useQsMediaPlayer = true + ) session = MediaSession(context, "MediaDataManagerTestSession") mediaNotification = SbnBuilder().run { setPkg(PACKAGE_NAME) @@ -84,6 +107,12 @@ class MediaDataManagerTest : SysuiTestCase() { putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) } whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller) + + // This is an ugly hack for now. The mediaSessionBasedFilter is one of the internal + // listeners in the internal processing pipeline. It receives events, but ince it is a + // mock, it doesn't pass those events along the chain to the external listeners. So, just + // treat mediaSessionBasedFilter as a listener for testing. + listener = mediaSessionBasedFilter } @After @@ -113,8 +142,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_callsListener() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject()) @@ -122,136 +149,74 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_conservesActiveFlag() { - val listener = TestListener() whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - assertThat(listener.data!!.active).isTrue() + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value!!.active).isTrue() } @Test fun testOnNotificationRemoved_callsListener() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.onNotificationRemoved(KEY) - verify(listener).onMediaDataRemoved(eq(KEY)) } @Test fun testOnNotificationRemoved_withResumption() { // GIVEN that the manager has a notification with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed mediaDataManager.onNotificationRemoved(KEY) // THEN the media data indicates that it is for resumption - assertThat(listener.data!!.resumption).isTrue() - // AND the new key is the package name - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) - assertThat(listener.removedKey).isNull() + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() } @Test fun testOnNotificationRemoved_twoWithResumption() { // GIVEN that the manager has two notifications with resume actions - val listener = TestListener() - mediaDataManager.addListener(listener) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onNotificationAdded(KEY_2, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() val resumableData = data.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, resumableData) mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData) + reset(listener) // WHEN the first is removed mediaDataManager.onNotificationRemoved(KEY) // THEN the data is for resumption and the key is migrated to the package name - assertThat(listener.data!!.resumption).isTrue() - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) - assertThat(listener.removedKey).isNull() + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + verify(listener, never()).onMediaDataRemoved(eq(KEY)) // WHEN the second is removed mediaDataManager.onNotificationRemoved(KEY_2) // THEN the data is for resumption and the second key is removed - assertThat(listener.data!!.resumption).isTrue() - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.removedKey!!).isEqualTo(KEY_2) - } - - @Test - fun testAppBlockedFromResumption() { - // GIVEN that the manager has a notification with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! - assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) - - // and the manager should block the package from creating resume controls - val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") - mediaDataManager.appsBlockedFromResume = blocked - - // WHEN the notification is removed - mediaDataManager.onNotificationRemoved(KEY) - - // THEN the media data is removed - assertThat(listener.removedKey!!).isEqualTo(KEY) - } - - @Test - fun testAppUnblockedFromResumption() { - // GIVEN that an app was blocked from resuming - val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") - mediaDataManager.appsBlockedFromResume = blocked - - // and GIVEN that the manager has a notification from that app with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! - assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) - - // WHEN the app is unblocked - mediaDataManager.appsBlockedFromResume = mutableSetOf("com.example.app") - - // and the notification is removed - mediaDataManager.onNotificationRemoved(KEY) - - // THEN the entry will stay as a resume control - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(PACKAGE_NAME), + capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + verify(listener).onMediaDataRemoved(eq(KEY_2)) } @Test fun testAddResumptionControls() { - val listener = TestListener() - mediaDataManager.addListener(listener) // WHEN resumption controls are added` val desc = MediaDescription.Builder().run { setTitle(SESSION_TITLE) @@ -262,7 +227,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN the media data indicates that it is for resumption - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isTrue() assertThat(data.song).isEqualTo(SESSION_TITLE) assertThat(data.app).isEqualTo(APP_NAME) @@ -271,8 +237,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testDismissMedia_listenerCalled() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.dismissMediaData(KEY, 0L) @@ -282,26 +246,4 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } - - /** - * Simple implementation of [MediaDataManager.Listener] for the test. - * - * Giving up on trying to get a mock Listener and ArgumentCaptor to work. - */ - private class TestListener : MediaDataManager.Listener { - var data: MediaData? = null - var key: String? = null - var oldKey: String? = null - var removedKey: String? = null - - override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - this.key = key - this.oldKey = oldKey - this.data = data - } - - override fun onMediaDataRemoved(key: String) { - removedKey = key - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index 3c6e19f0ec6f..fdb432cc097c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -68,11 +68,11 @@ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value public class MediaDeviceManagerTest : SysuiTestCase() { private lateinit var manager: MediaDeviceManager - @Mock private lateinit var mediaDataManager: MediaDataManager @Mock private lateinit var lmmFactory: LocalMediaManagerFactory @Mock private lateinit var lmm: LocalMediaManager @Mock private lateinit var mr2: MediaRouter2Manager - private lateinit var fakeExecutor: FakeExecutor + private lateinit var fakeFgExecutor: FakeExecutor + private lateinit var fakeBgExecutor: FakeExecutor @Mock private lateinit var dumpster: DumpManager @Mock private lateinit var listener: MediaDeviceManager.Listener @Mock private lateinit var device: MediaDevice @@ -87,8 +87,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Before fun setUp() { - fakeExecutor = FakeExecutor(FakeSystemClock()) - manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager, + fakeFgExecutor = FakeExecutor(FakeSystemClock()) + fakeBgExecutor = FakeExecutor(FakeSystemClock()) + manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor, dumpster) manager.addListener(listener) @@ -144,13 +145,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun loadAndRemoveMediaData() { manager.onMediaDataLoaded(KEY, null, mediaData) manager.onMediaDataRemoved(KEY) + fakeBgExecutor.runAllReady() verify(lmm).unregisterCallback(any()) } @Test fun loadMediaDataWithNullToken() { manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null)) - fakeExecutor.runAllReady() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() assertThat(data.name).isEqualTo(DEVICE_NAME) @@ -163,6 +166,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { reset(listener) // WHEN data is loaded with a new key manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the listener for the old key should removed. verify(lmm).unregisterCallback(any()) // AND a new device event emitted @@ -186,6 +191,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun unknownOldKey() { val oldKey = "unknown" manager.onMediaDataLoaded(KEY, oldKey, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any()) } @@ -193,13 +200,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun updateToSessionTokenWithNullRoute() { // GIVEN that media data has been loaded with a null token manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null)) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) // WHEN media data is loaded with a different token // AND that token results in a null route - reset(listener) whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device should be disabled - fakeExecutor.runAllReady() val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() assertThat(data.name).isNull() @@ -210,7 +220,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun deviceEventOnAddNotification() { // WHEN a notification is added manager.onMediaDataLoaded(KEY, null, mediaData) - val deviceCallback = captureCallback() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the update is dispatched to the listener val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() @@ -230,10 +241,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun deviceListUpdate() { manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() val deviceCallback = captureCallback() // WHEN the device list changes deviceCallback.onDeviceListUpdate(mutableListOf(device)) - assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) // THEN the update is dispatched to the listener val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() @@ -244,10 +257,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun selectedDeviceStateChanged() { manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() val deviceCallback = captureCallback() // WHEN the selected device changes state deviceCallback.onSelectedDeviceStateChanged(device, 1) - assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1) + assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) // THEN the update is dispatched to the listener val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() @@ -270,6 +285,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) // WHEN a notification is added manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device is disabled val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() @@ -281,13 +298,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() { // GIVEN a notif is added manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() reset(listener) // AND MR2Manager returns null for routing session whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) // WHEN the selected device changes state val deviceCallback = captureCallback() deviceCallback.onSelectedDeviceStateChanged(device, 1) - fakeExecutor.runAllReady() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device is disabled val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() @@ -299,13 +319,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() { // GIVEN a notif is added manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() reset(listener) // GIVEN that MR2Manager returns null for routing session whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) // WHEN the selected device changes state val deviceCallback = captureCallback() deviceCallback.onDeviceListUpdate(mutableListOf(device)) - fakeExecutor.runAllReady() + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() // THEN the device is disabled val data = captureDeviceData(KEY) assertThat(data.enabled).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt index 118cffc2d5b8..00b003d290ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt @@ -24,7 +24,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -33,6 +32,8 @@ public class MediaPlayerDataTest : SysuiTestCase() { companion object { val LOCAL = true val RESUMPTION = true + val PLAYING = true + val UNDETERMINED = null } @Before @@ -43,15 +44,13 @@ public class MediaPlayerDataTest : SysuiTestCase() { @Test fun addPlayingThenRemote() { val playerIsPlaying = mock(MediaControlPanel::class.java) - whenever(playerIsPlaying.isPlaying).thenReturn(true) - val dataIsPlaying = createMediaData(LOCAL, !RESUMPTION) + val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION) val playerIsRemote = mock(MediaControlPanel::class.java) - whenever(playerIsRemote.isPlaying).thenReturn(false) - val dataIsRemote = createMediaData(!LOCAL, !RESUMPTION) + val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION) - MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote) + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) val players = MediaPlayerData.players() assertThat(players).hasSize(2) @@ -61,18 +60,16 @@ public class MediaPlayerDataTest : SysuiTestCase() { @Test fun switchPlayersPlaying() { val playerIsPlaying1 = mock(MediaControlPanel::class.java) - whenever(playerIsPlaying1.isPlaying).thenReturn(true) - val dataIsPlaying1 = createMediaData(LOCAL, !RESUMPTION) + var dataIsPlaying1 = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION) val playerIsPlaying2 = mock(MediaControlPanel::class.java) - whenever(playerIsPlaying2.isPlaying).thenReturn(false) - val dataIsPlaying2 = createMediaData(LOCAL, !RESUMPTION) + var dataIsPlaying2 = createMediaData("app2", !PLAYING, LOCAL, !RESUMPTION) MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) - whenever(playerIsPlaying1.isPlaying).thenReturn(false) - whenever(playerIsPlaying2.isPlaying).thenReturn(true) + dataIsPlaying1 = createMediaData("app1", !PLAYING, LOCAL, !RESUMPTION) + dataIsPlaying2 = createMediaData("app2", PLAYING, LOCAL, !RESUMPTION) MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) @@ -85,38 +82,43 @@ public class MediaPlayerDataTest : SysuiTestCase() { @Test fun fullOrderTest() { val playerIsPlaying = mock(MediaControlPanel::class.java) - whenever(playerIsPlaying.isPlaying).thenReturn(true) - val dataIsPlaying = createMediaData(LOCAL, !RESUMPTION) + val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION) val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java) - whenever(playerIsPlayingAndRemote.isPlaying).thenReturn(true) - val dataIsPlayingAndRemote = createMediaData(!LOCAL, !RESUMPTION) + val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION) val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java) - whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false) - val dataIsStoppedAndLocal = createMediaData(LOCAL, !RESUMPTION) + val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION) val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java) - whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false) - val dataIsStoppedAndRemote = createMediaData(!LOCAL, !RESUMPTION) + val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION) val playerCanResume = mock(MediaControlPanel::class.java) - whenever(playerCanResume.isPlaying).thenReturn(false) - val dataCanResume = createMediaData(LOCAL, RESUMPTION) + val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION) + + val playerUndetermined = mock(MediaControlPanel::class.java) + val dataUndetermined = createMediaData("app6", UNDETERMINED, LOCAL, RESUMPTION) MediaPlayerData.addMediaPlayer("3", dataIsStoppedAndLocal, playerIsStoppedAndLocal) MediaPlayerData.addMediaPlayer("5", dataIsStoppedAndRemote, playerIsStoppedAndRemote) MediaPlayerData.addMediaPlayer("4", dataCanResume, playerCanResume) MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) MediaPlayerData.addMediaPlayer("2", dataIsPlayingAndRemote, playerIsPlayingAndRemote) + MediaPlayerData.addMediaPlayer("6", dataUndetermined, playerUndetermined) val players = MediaPlayerData.players() - assertThat(players).hasSize(5) + assertThat(players).hasSize(6) assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote, - playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote).inOrder() + playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote, + playerUndetermined).inOrder() } - private fun createMediaData(isLocalSession: Boolean, resumption: Boolean) = - MediaData(0, false, 0, null, null, null, null, null, emptyList(), emptyList<Int>(), "", - null, null, null, true, null, isLocalSession, resumption, null, false) + private fun createMediaData( + app: String, + isPlaying: Boolean?, + isLocalSession: Boolean, + resumption: Boolean + ) = + MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "", + null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt new file mode 100644 index 000000000000..2d90cc4f6712 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt @@ -0,0 +1,422 @@ +/* + * 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.media + +import android.graphics.Color +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.time.FakeSystemClock + +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever + +private const val PACKAGE = "PKG" +private const val KEY = "TEST_KEY" +private const val NOTIF_KEY = "TEST_KEY" +private const val SESSION_ARTIST = "SESSION_ARTIST" +private const val SESSION_TITLE = "SESSION_TITLE" +private const val APP_NAME = "APP_NAME" +private const val USER_ID = 0 + +private val info = MediaData( + userId = USER_ID, + initialized = true, + backgroundColor = Color.DKGRAY, + app = APP_NAME, + appIcon = null, + artist = SESSION_ARTIST, + song = SESSION_TITLE, + artwork = null, + actions = emptyList(), + actionsToShowInCompact = emptyList(), + packageName = PACKAGE, + token = null, + clickIntent = null, + device = null, + active = true, + resumeAction = null, + resumption = false, + notificationKey = NOTIF_KEY, + hasCheckedForResume = false +) + +private fun <T> eq(value: T): T = Mockito.eq(value) ?: value + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class MediaSessionBasedFilterTest : SysuiTestCase() { + + @JvmField @Rule val mockito = MockitoJUnit.rule() + + // Unit to be tested + private lateinit var filter: MediaSessionBasedFilter + + private lateinit var sessionListener: MediaSessionManager.OnActiveSessionsChangedListener + @Mock private lateinit var mediaListener: MediaDataManager.Listener + + // MediaSessionBasedFilter dependencies + @Mock private lateinit var mediaSessionManager: MediaSessionManager + private lateinit var fgExecutor: FakeExecutor + private lateinit var bgExecutor: FakeExecutor + + @Mock private lateinit var controller1: MediaController + @Mock private lateinit var controller2: MediaController + @Mock private lateinit var controller3: MediaController + @Mock private lateinit var controller4: MediaController + + private lateinit var token1: MediaSession.Token + private lateinit var token2: MediaSession.Token + private lateinit var token3: MediaSession.Token + private lateinit var token4: MediaSession.Token + + @Mock private lateinit var remotePlaybackInfo: PlaybackInfo + @Mock private lateinit var localPlaybackInfo: PlaybackInfo + + private lateinit var session1: MediaSession + private lateinit var session2: MediaSession + private lateinit var session3: MediaSession + private lateinit var session4: MediaSession + + private lateinit var mediaData1: MediaData + private lateinit var mediaData2: MediaData + private lateinit var mediaData3: MediaData + private lateinit var mediaData4: MediaData + + @Before + fun setUp() { + fgExecutor = FakeExecutor(FakeSystemClock()) + bgExecutor = FakeExecutor(FakeSystemClock()) + filter = MediaSessionBasedFilter(context, mediaSessionManager, fgExecutor, bgExecutor) + + // Configure mocks. + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(emptyList()) + + session1 = MediaSession(context, "MediaSessionBasedFilter1") + session2 = MediaSession(context, "MediaSessionBasedFilter2") + session3 = MediaSession(context, "MediaSessionBasedFilter3") + session4 = MediaSession(context, "MediaSessionBasedFilter4") + + token1 = session1.sessionToken + token2 = session2.sessionToken + token3 = session3.sessionToken + token4 = session4.sessionToken + + whenever(controller1.getSessionToken()).thenReturn(token1) + whenever(controller2.getSessionToken()).thenReturn(token2) + whenever(controller3.getSessionToken()).thenReturn(token3) + whenever(controller4.getSessionToken()).thenReturn(token4) + + whenever(controller1.getPackageName()).thenReturn(PACKAGE) + whenever(controller2.getPackageName()).thenReturn(PACKAGE) + whenever(controller3.getPackageName()).thenReturn(PACKAGE) + whenever(controller4.getPackageName()).thenReturn(PACKAGE) + + mediaData1 = info.copy(token = token1) + mediaData2 = info.copy(token = token2) + mediaData3 = info.copy(token = token3) + mediaData4 = info.copy(token = token4) + + whenever(remotePlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(localPlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL) + + whenever(controller1.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller2.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller3.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller4.getPlaybackInfo()).thenReturn(localPlaybackInfo) + + // Capture listener + bgExecutor.runAllReady() + val listenerCaptor = ArgumentCaptor.forClass( + MediaSessionManager.OnActiveSessionsChangedListener::class.java) + verify(mediaSessionManager).addOnActiveSessionsChangedListener( + listenerCaptor.capture(), any()) + sessionListener = listenerCaptor.value + + filter.addListener(mediaListener) + } + + @After + fun tearDown() { + session1.release() + session2.release() + session3.release() + session4.release() + } + + @Test + fun noMediaSession_loadedEventNotFiltered() { + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun noMediaSession_removedEventNotFiltered() { + filter.onMediaDataRemoved(KEY) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + verify(mediaListener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun matchingMediaSession_loadedEventNotFiltered() { + // GIVEN an active session + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun matchingMediaSession_removedEventNotFiltered() { + // GIVEN an active session + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a removed event is received + filter.onMediaDataRemoved(KEY) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun remoteSession_loadedEventNotFiltered() { + // GIVEN a remote session + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matche the session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun remoteAndLocalSessions_localLoadedEventFiltered() { + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) + } + + @Test + fun remoteAndLocalSessions_remoteSessionWithoutNotification() { + // GIVEN remote and local sessions + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered because there isn't a notification for the remote + // session. + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() { + // GIVEN remote and local sessions + val key1 = "KEY_1" + val key2 = "KEY_2" + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key1, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + // AND there should be a removed event for key2 + verify(mediaListener).onMediaDataRemoved(eq(key2)) + } + + @Test + fun remoteAndLocalHaveDifferentKeys_remoteSessionWithoutNotification() { + // GIVEN remote and local sessions + val key1 = "KEY_1" + val key2 = "KEY_2" + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key1, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + } + + @Test + fun multipleRemoteSessions_loadedEventNotFiltered() { + // GIVEN two remote sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) + } + + @Test + fun multipleOtherSessions_loadedEventNotFiltered() { + // GIVEN multiple active sessions from other packages + val controllers = listOf(controller1, controller2, controller3, controller4) + whenever(controller1.getPackageName()).thenReturn("PKG_1") + whenever(controller2.getPackageName()).thenReturn("PKG_2") + whenever(controller3.getPackageName()).thenReturn("PKG_3") + whenever(controller4.getPackageName()).thenReturn("PKG_4") + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun doNotFilterDuringKeyMigration() { + val key1 = "KEY_1" + val key2 = "KEY_2" + // GIVEN a loaded event + filter.onMediaDataLoaded(key1, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session but it is a key migration + filter.onMediaDataLoaded(key2, key1, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is fired + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2)) + } + + @Test + fun filterAfterKeyMigration() { + val key1 = "KEY_1" + val key2 = "KEY_2" + // GIVEN a loaded event + filter.onMediaDataLoaded(key1, null, mediaData1) + filter.onMediaDataLoaded(key1, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // GIVEN that the keys have been migrated + filter.onMediaDataLoaded(key2, key1, mediaData1) + filter.onMediaDataLoaded(key2, key1, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key2, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is fired + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index e98b6b69ee76..4c9e141c45cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -32,7 +32,9 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import org.junit.Before; import org.junit.Test; @@ -61,6 +63,12 @@ public class RecordingServiceTest extends SysuiTestCase { private Executor mExecutor; @Mock private CurrentUserContextTracker mUserContextTracker; + private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() { + public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, + boolean requiresShadeOpen) { + action.onDismiss(); + } + }; private RecordingService mRecordingService; @@ -68,7 +76,7 @@ public class RecordingServiceTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger, - mNotificationManager, mUserContextTracker)); + mNotificationManager, mUserContextTracker, mKeyguardDismissUtil)); // Return actual context info doReturn(mContext).when(mRecordingService).getApplicationContext(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 80fa8cc7d931..92a2c8738344 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -211,6 +211,19 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { } @Test + public void testUpdateNotificationViews_appOps() throws Exception { + NotificationEntry entry0 = createEntry(); + entry0.setRow(spy(entry0.getRow())); + when(mEntryManager.getVisibleNotifications()).thenReturn( + Lists.newArrayList(entry0)); + mListContainer.addContainerView(entry0.getRow()); + + mViewHierarchyManager.updateNotificationViews(); + + verify(entry0.getRow(), times(1)).showAppOpsIcons(any()); + } + + @Test public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() { // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews() mMadeReentrantCall = false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index ae39035e8666..314b19140e7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -72,6 +72,8 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { private Notification mNotification; private AppOpsCoordinator mAppOpsCoordinator; private NotifFilter mForegroundFilter; + private NotifCollectionListener mNotifCollectionListener; + private AppOpsController.Callback mAppOpsCallback; private NotifLifetimeExtender mForegroundNotifLifetimeExtender; private FakeSystemClock mClock = new FakeSystemClock(); @@ -108,6 +110,18 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { lifetimeExtenderCaptor.capture()); mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue(); + // capture notifCollectionListener + ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor = + ArgumentCaptor.forClass(NotifCollectionListener.class); + verify(mNotifPipeline, times(1)).addCollectionListener( + notifCollectionCaptor.capture()); + mNotifCollectionListener = notifCollectionCaptor.getValue(); + + // capture app ops callback + ArgumentCaptor<AppOpsController.Callback> appOpsCaptor = + ArgumentCaptor.forClass(AppOpsController.Callback.class); + verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture()); + mAppOpsCallback = appOpsCaptor.getValue(); } @Test @@ -201,4 +215,134 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { assertFalse(mForegroundNotifLifetimeExtender .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK)); } + + @Test + public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() { + // GIVEN three current notifications, two with the same key but from different users + NotificationEntry entry1 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(1) + .build(); + NotificationEntry entry2 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + NotificationEntry entry3_diffUser = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID + 1)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser)); + + // GIVEN that only entry2 has a standard layout + when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG)) + .thenReturn(new ArraySet<>(List.of(entry2.getKey()))); + + // WHEN a new app ops code comes in + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); + mExecutor.runAllReady(); + + // THEN entry2's app ops are updated, but no one else's are + assertEquals( + new ArraySet<>(), + entry1.mActiveAppOps); + assertEquals( + new ArraySet<>(List.of(47)), + entry2.mActiveAppOps); + assertEquals( + new ArraySet<>(), + entry3_diffUser.mActiveAppOps); + } + + @Test + public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() { + // GIVEN three notifications with standard layouts + NotificationEntry entry1 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(1) + .build(); + NotificationEntry entry2 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + NotificationEntry entry3 = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(3) + .build(); + when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3)); + when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG)) + .thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(), + entry3.getKey()))); + + // WHEN a new app ops code comes in + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); + mExecutor.runAllReady(); + + // THEN all entries get updated + assertEquals( + new ArraySet<>(List.of(47)), + entry1.mActiveAppOps); + assertEquals( + new ArraySet<>(List.of(47)), + entry2.mActiveAppOps); + assertEquals( + new ArraySet<>(List.of(47)), + entry3.mActiveAppOps); + } + + @Test + public void testAppOpsAreRemoved() { + // GIVEN One notification which is associated with app ops + NotificationEntry entry = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry)); + when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG)) + .thenReturn(new ArraySet<>(List.of(entry.getKey()))); + + // GIVEN that the notification's app ops are already [47, 33] + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); + mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true); + mExecutor.runAllReady(); + assertEquals( + new ArraySet<>(List.of(47, 33)), + entry.mActiveAppOps); + + // WHEN one of the app ops is removed + mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false); + mExecutor.runAllReady(); + + // THEN the entry's active app ops are updated as well + assertEquals( + new ArraySet<>(List.of(33)), + entry.mActiveAppOps); + } + + @Test + public void testNullAppOps() { + // GIVEN one notification with app ops + NotificationEntry entry = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .setPkg(TEST_PKG) + .setId(2) + .build(); + entry.mActiveAppOps.clear(); + entry.mActiveAppOps.addAll(List.of(47, 33)); + + // WHEN the notification is updated and the foreground service controller returns null for + // this notification + when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(), + entry.getSbn().getPackageName())).thenReturn(null); + mNotifCollectionListener.onEntryUpdated(entry); + + // THEN the entry's active app ops is updated to empty + assertTrue(entry.mActiveAppOps.isEmpty()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java new file mode 100644 index 000000000000..43d8b50bcf72 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java @@ -0,0 +1,230 @@ +/* + * 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.notification.row; + +import static android.app.AppOpsManager.OP_CAMERA; +import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.UiThreadTest; +import android.util.ArraySet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@UiThreadTest +public class AppOpsInfoTest extends SysuiTestCase { + private static final String TEST_PACKAGE_NAME = "test_package"; + private static final int TEST_UID = 1; + + private AppOpsInfo mAppOpsInfo; + private final PackageManager mMockPackageManager = mock(PackageManager.class); + private final NotificationGuts mGutsParent = mock(NotificationGuts.class); + private StatusBarNotification mSbn; + private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); + + @Before + public void setUp() throws Exception { + // Inflate the layout + final LayoutInflater layoutInflater = LayoutInflater.from(mContext); + mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null); + mAppOpsInfo.setGutsParent(mGutsParent); + + // PackageManager must return a packageInfo and applicationInfo. + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt())) + .thenReturn(packageInfo); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = TEST_UID; // non-zero + when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn( + applicationInfo); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + new Notification(), UserHandle.CURRENT, null, 0); + } + + @Test + public void testBindNotification_SetsTextApplicationName() { + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); + final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname); + assertTrue(textView.getText().toString().contains("App Name")); + } + + @Test + public void testBindNotification_SetsPackageIcon() { + final Drawable iconDrawable = mock(Drawable.class); + when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) + .thenReturn(iconDrawable); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); + final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon); + assertEquals(iconDrawable, iconView.getDrawable()); + } + + @Test + public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + final CountDownLatch latch = new CountDownLatch(1); + mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid, + ArraySet<Integer> ops) -> { + assertEquals(TEST_PACKAGE_NAME, pkg); + assertEquals(expectedOps, ops); + assertEquals(TEST_UID, uid); + latch.countDown(); + }, mSbn, mUiEventLogger, expectedOps); + + final View settingsButton = mAppOpsInfo.findViewById(R.id.settings); + settingsButton.performClick(); + // Verify that listener was triggered. + assertEquals(0, latch.getCount()); + } + + @Test + public void testBindNotification_LogsOpen() throws Exception { + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); + assertEquals(1, mUiEventLogger.numLogs()); + assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(), + mUiEventLogger.eventId(0)); + } + + @Test + public void testOk() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + final CountDownLatch latch = new CountDownLatch(1); + mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid, + ArraySet<Integer> ops) -> { + assertEquals(TEST_PACKAGE_NAME, pkg); + assertEquals(expectedOps, ops); + assertEquals(TEST_UID, uid); + latch.countDown(); + }, mSbn, mUiEventLogger, expectedOps); + + final View okButton = mAppOpsInfo.findViewById(R.id.ok); + okButton.performClick(); + assertEquals(1, latch.getCount()); + verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean()); + } + + @Test + public void testPrompt_camera() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is using the camera.", prompt.getText()); + } + + @Test + public void testPrompt_mic() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_RECORD_AUDIO); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is using the microphone.", prompt.getText()); + } + + @Test + public void testPrompt_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen.", prompt.getText()); + } + + @Test + public void testPrompt_camera_mic() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + expectedOps.add(OP_RECORD_AUDIO); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is using the microphone and camera.", prompt.getText()); + } + + @Test + public void testPrompt_camera_mic_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + expectedOps.add(OP_RECORD_AUDIO); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen and using" + + " the microphone and camera.", prompt.getText()); + } + + @Test + public void testPrompt_camera_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_CAMERA); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen and using" + + " the camera.", prompt.getText()); + } + + @Test + public void testPrompt_mic_overlay() { + ArraySet<Integer> expectedOps = new ArraySet<>(); + expectedOps.add(OP_RECORD_AUDIO); + expectedOps.add(OP_SYSTEM_ALERT_WINDOW); + mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); + TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); + assertEquals("This app is displaying over other apps on your screen and using" + + " the microphone.", prompt.getText()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 4758d2318889..2684cc29aa93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -35,10 +35,12 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AppOpsManager; import android.app.NotificationChannel; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.util.ArraySet; import android.view.View; import androidx.test.filters.SmallTest; @@ -211,6 +213,46 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test + public void testShowAppOps_noHeader() { + // public notification is custom layout - no header + mGroupRow.setSensitive(true, true); + mGroupRow.setAppOpsOnClickListener(null); + mGroupRow.showAppOpsIcons(null); + } + + @Test + public void testShowAppOpsIcons_header() { + NotificationContentView publicLayout = mock(NotificationContentView.class); + mGroupRow.setPublicLayout(publicLayout); + NotificationContentView privateLayout = mock(NotificationContentView.class); + mGroupRow.setPrivateLayout(privateLayout); + NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); + when(mockContainer.getNotificationChildCount()).thenReturn(1); + mGroupRow.setChildrenContainer(mockContainer); + + ArraySet<Integer> ops = new ArraySet<>(); + ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS); + mGroupRow.showAppOpsIcons(ops); + + verify(mockContainer, times(1)).showAppOpsIcons(ops); + verify(privateLayout, times(1)).showAppOpsIcons(ops); + verify(publicLayout, times(1)).showAppOpsIcons(ops); + + } + + @Test + public void testAppOpsOnClick() { + ExpandableNotificationRow.OnAppOpsClickListener l = mock( + ExpandableNotificationRow.OnAppOpsClickListener.class); + View view = mock(View.class); + + mGroupRow.setAppOpsOnClickListener(l); + + mGroupRow.getAppOpsOnClickListener().onClick(view); + verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any()); + } + + @Test public void testHeadsUpAnimatingAwayListener() { mGroupRow.setHeadsUpAnimatingAway(true); Assert.assertEquals(true, mHeadsUpAnimatingAway); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index b02f2746ce7a..ed4f8b330e23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -76,6 +76,32 @@ public class NotificationContentViewTest extends SysuiTestCase { @Test @UiThreadTest + public void testShowAppOpsIcons() { + View mockContracted = mock(NotificationHeaderView.class); + when(mockContracted.findViewById(com.android.internal.R.id.mic)) + .thenReturn(mockContracted); + View mockExpanded = mock(NotificationHeaderView.class); + when(mockExpanded.findViewById(com.android.internal.R.id.mic)) + .thenReturn(mockExpanded); + View mockHeadsUp = mock(NotificationHeaderView.class); + when(mockHeadsUp.findViewById(com.android.internal.R.id.mic)) + .thenReturn(mockHeadsUp); + + mView.setContractedChild(mockContracted); + mView.setExpandedChild(mockExpanded); + mView.setHeadsUpChild(mockHeadsUp); + + ArraySet<Integer> ops = new ArraySet<>(); + ops.add(AppOpsManager.OP_RECORD_AUDIO); + mView.showAppOpsIcons(ops); + + verify(mockContracted, times(1)).setVisibility(View.VISIBLE); + verify(mockExpanded, times(1)).setVisibility(View.VISIBLE); + verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE); + } + + @Test + @UiThreadTest public void testExpandButtonFocusIsCalled() { View mockContractedEB = mock(NotificationExpandButton.class); View mockContracted = mock(NotificationHeaderView.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index d2ff2ad8a684..0c6409b38d21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -421,6 +421,7 @@ public class NotificationTestHelper { mBindStage, mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), + mock(ExpandableNotificationRow.OnAppOpsClickListener.class), mock(FalsingManager.class), mStatusBarStateController, mPeopleNotificationIdentifier); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 6fffcff41a4f..56598f7a5bfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -3,6 +3,8 @@ package com.android.systemui.statusbar.policy; import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -10,6 +12,7 @@ import static org.mockito.Mockito.when; import android.net.NetworkCapabilities; import android.os.Looper; import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -259,6 +262,25 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { assertDataNetworkNameEquals(newDataName); } + @Test + public void testIsDataInService_true() { + setupDefaultSignal(); + assertTrue(mNetworkController.isMobileDataNetworkInService()); + } + + @Test + public void testIsDataInService_noSignal_false() { + assertFalse(mNetworkController.isMobileDataNetworkInService()); + } + + @Test + public void testIsDataInService_notInService_false() { + setupDefaultSignal(); + setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE); + setDataRegState(ServiceState.STATE_OUT_OF_SERVICE); + assertFalse(mNetworkController.isMobileDataNetworkInService()); + } + private void testDataActivity(int direction, boolean in, boolean out) { updateDataActivity(direction); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java index d5ba381bfcac..e7acfae24f30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java @@ -95,6 +95,11 @@ public class FakeNetworkController extends BaseLeakChecker<SignalCallback> } @Override + public boolean isMobileDataNetworkInService() { + return false; + } + + @Override public int getNumberSubscriptions() { return 0; } diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java index fbe8c04bd59c..38983488654e 100644 --- a/services/core/java/android/os/UserManagerInternal.java +++ b/services/core/java/android/os/UserManagerInternal.java @@ -227,6 +227,14 @@ public abstract class UserManagerInternal { public abstract @NonNull List<UserInfo> getUsers(boolean excludeDying); /** + * Internal implementation of getUsers does not check permissions. + * This improves performance for calls from inside system server which already have permissions + * checked. + */ + public abstract @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + boolean excludePreCreated); + + /** * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group * and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled. * diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 966038986791..8112bb854b71 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import android.app.ActivityManager; import android.app.ActivityThread; +import android.app.ApplicationExitInfo; import android.os.Debug; import android.os.Handler; import android.os.Message; @@ -132,11 +133,15 @@ public final class CachedAppOptimizer { static final int REPORT_UNFREEZE_MSG = 4; //TODO:change this static definition into a configurable flag. - static final int FREEZE_TIMEOUT_MS = 500; + static final int FREEZE_TIMEOUT_MS = 10000; static final int DO_FREEZE = 1; static final int REPORT_UNFREEZE = 2; + // Bitfield values for sync/async transactions reveived by frozen processes + static final int SYNC_RECEIVED_WHILE_FROZEN = 1; + static final int ASYNC_RECEIVED_WHILE_FROZEN = 2; + /** * This thread must be moved to the system background cpuset. * If that doesn't happen, it's probably going to draw a lot of power. @@ -494,6 +499,15 @@ public final class CachedAppOptimizer { private static native void freezeBinder(int pid, boolean freeze); /** + * Retrieves binder freeze info about a process. + * @param pid the pid for which binder freeze info is to be retrieved. + * + * @throws RuntimeException if the operation could not complete successfully. + * @return a bit field reporting the binder freeze info for the process. + */ + private static native int getBinderFreezeInfo(int pid); + + /** * Determines whether the freezer is supported by this system */ public static boolean isFreezerSupported() { @@ -729,6 +743,37 @@ public final class CachedAppOptimizer { return; } + boolean processKilled = false; + + try { + int freezeInfo = getBinderFreezeInfo(app.pid); + + if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) { + Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " " + + " received sync transactions while frozen, killing"); + app.kill("Sync transaction while in frozen state", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + processKilled = true; + } + + if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) { + Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " " + + " received async transactions while frozen"); + } + } catch (Exception e) { + Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " " + + app.processName + ". Killing it. Exception: " + e); + app.kill("Unable to query binder frozen stats", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + processKilled = true; + } + + if (processKilled) { + return; + } + long freezeTime = app.freezeUnfreezeTime; try { @@ -745,8 +790,12 @@ public final class CachedAppOptimizer { try { freezeBinder(app.pid, false); } catch (RuntimeException e) { - // TODO: it might be preferable to kill the target pid in this case - Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName); + Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName + + ". Killing it"); + app.kill("Unable to unfreeze", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + return; } if (DEBUG_FREEZER) { diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java index b36e3c7b9e35..50d58f02baa7 100644 --- a/services/core/java/com/android/server/am/PendingTempWhitelists.java +++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java @@ -32,13 +32,13 @@ final class PendingTempWhitelists { void put(int uid, ActivityManagerService.PendingTempWhitelist value) { mPendingTempWhitelist.put(uid, value); - mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag); + mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag); } void removeAt(int index) { final int uid = mPendingTempWhitelist.keyAt(index); mPendingTempWhitelist.removeAt(index); - mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid); + mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid); } ActivityManagerService.PendingTempWhitelist get(int uid) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 22b0aebca6c4..074d3fe10816 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -224,12 +224,35 @@ import java.util.concurrent.atomic.AtomicBoolean; if (!addSpeakerphoneClient(cb, pid, on)) { return false; } + if (on) { + // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for startBluetoothScoForClient_Sync(). + mBtHelper.stopBluetoothScoForPid(pid); + } final boolean wasOn = isSpeakerphoneOn(); updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } + /** + * Turns speakerphone off for a given pid and update speakerphone state. + * @param pid + */ + @GuardedBy("mDeviceStateLock") + private void setSpeakerphoneOffForPid(int pid) { + SpeakerphoneClient client = getSpeakerphoneClientForPid(pid); + if (client == null) { + return; + } + client.unregisterDeathRecipient(); + mSpeakerphoneClients.remove(client); + final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(") + .append(pid).append(")").toString(); + updateSpeakerphoneOn(eventSource); + } + @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { @@ -488,6 +511,10 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { synchronized (mDeviceStateLock) { + // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for setSpeakerphoneOn(true). + setSpeakerphoneOffForPid(Binder.getCallingPid()); mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); } } @@ -1381,6 +1408,16 @@ import java.util.concurrent.atomic.AtomicBoolean; return false; } + @GuardedBy("mDeviceStateLock") + private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) { + for (SpeakerphoneClient cl : mSpeakerphoneClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // List of clients requesting speakerPhone ON @GuardedBy("mDeviceStateLock") private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients = diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java index 9ebd75bd0f64..af0e978726e3 100644 --- a/services/core/java/com/android/server/audio/AudioEventLogger.java +++ b/services/core/java/com/android/server/audio/AudioEventLogger.java @@ -60,7 +60,36 @@ public class AudioEventLogger { * @return the same instance of the event */ public Event printLog(String tag) { - Log.i(tag, eventToString()); + return printLog(ALOGI, tag); + } + + public static final int ALOGI = 0; + public static final int ALOGE = 1; + public static final int ALOGW = 2; + public static final int ALOGV = 3; + + /** + * Same as {@link #printLog(String)} with a log type + * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV} + * @param tag + * @return + */ + public Event printLog(int type, String tag) { + switch (type) { + case ALOGI: + Log.i(tag, eventToString()); + break; + case ALOGE: + Log.e(tag, eventToString()); + break; + case ALOGW: + Log.w(tag, eventToString()); + break; + case ALOGV: + default: + Log.v(tag, eventToString()); + break; + } return this; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index fe619690ae34..75c27603d785 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -26,6 +26,10 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; +import static com.android.server.audio.AudioEventLogger.Event.ALOGE; +import static com.android.server.audio.AudioEventLogger.Event.ALOGI; +import static com.android.server.audio.AudioEventLogger.Event.ALOGW; + import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; @@ -284,6 +288,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29; private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30; private static final int MSG_CHECK_MODE_FOR_UID = 31; + private static final int MSG_REINIT_VOLUMES = 32; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -673,6 +678,7 @@ public class AudioService extends IAudioService.Stub public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()")); mContext = context; mContentResolver = context.getContentResolver(); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); @@ -892,6 +898,9 @@ public class AudioService extends IAudioService.Stub mPrescaleAbsoluteVolume[i] = preScale[i]; } } + + // check on volume initialization + checkVolumeRangeInitialization("AudioService()"); } public void systemReady() { @@ -1019,11 +1028,15 @@ public class AudioService extends IAudioService.Stub if (!mSystemReady || (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) { Log.e(TAG, "Audioserver died."); + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "onAudioServerDied() audioserver died")); sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0, null, 500); return; } - Log.e(TAG, "Audioserver started."); + Log.i(TAG, "Audioserver started."); + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "onAudioServerDied() audioserver started")); updateAudioHalPids(); @@ -1058,14 +1071,7 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.setForceUse_Async(AudioSystem.FOR_SYSTEM, forSys, "onAudioServerDied"); // Restore stream volumes - int numStreamTypes = AudioSystem.getNumStreamTypes(); - for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - VolumeStreamState streamState = mStreamStates[streamType]; - AudioSystem.initStreamVolume( - streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10); - - streamState.applyAllVolumes(); - } + onReinitVolumes("after audioserver restart"); // Restore audio volume groups restoreVolumeGroups(); @@ -1163,6 +1169,72 @@ public class AudioService extends IAudioService.Stub setMicMuteFromSwitchInput(); } + private void onReinitVolumes(@NonNull String caller) { + final int numStreamTypes = AudioSystem.getNumStreamTypes(); + // keep track of any error during stream volume initialization + int status = AudioSystem.AUDIO_STATUS_OK; + for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { + VolumeStreamState streamState = mStreamStates[streamType]; + final int res = AudioSystem.initStreamVolume( + streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10); + if (res != AudioSystem.AUDIO_STATUS_OK) { + status = res; + Log.e(TAG, "Failed to initStreamVolume (" + res + ") for stream " + streamType); + // stream volume initialization failed, no need to try the others, it will be + // attempted again when MSG_REINIT_VOLUMES is handled + break; + } + streamState.applyAllVolumes(); + } + + // did it work? check based on status + if (status != AudioSystem.AUDIO_STATUS_OK) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume failed with " + status + " will retry") + .printLog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + return; + } + + // did it work? check based on min/max values of some basic streams + if (!checkVolumeRangeInitialization(caller)) { + return; + } + + // success + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume succeeded").printLog(ALOGI, TAG)); + } + + /** + * Check volume ranges were properly initialized + * @return true if volume ranges were successfully initialized + */ + private boolean checkVolumeRangeInitialization(String caller) { + boolean success = true; + final int[] basicStreams = { AudioSystem.STREAM_ALARM, AudioSystem.STREAM_RING, + AudioSystem.STREAM_MUSIC, AudioSystem.STREAM_VOICE_CALL, + AudioSystem.STREAM_ACCESSIBILITY }; + for (int streamType : basicStreams) { + final AudioAttributes aa = new AudioAttributes.Builder() + .setInternalLegacyStreamType(streamType).build(); + if (AudioSystem.getMaxVolumeIndexForAttributes(aa) < 0 + || AudioSystem.getMinVolumeIndexForAttributes(aa) < 0) { + success = false; + break; + } + } + if (!success) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume succeeded but invalid mix/max levels, will retry") + .printLog(ALOGW, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } + return success; + } + private void onDispatchAudioServerStateChange(boolean state) { synchronized (mAudioServerStateListeners) { for (AsdProxy asdp : mAudioServerStateListeners.values()) { @@ -5579,7 +5651,15 @@ public class AudioService extends IAudioService.Stub mIndexMin = MIN_STREAM_VOLUME[streamType] * 10; mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex() mIndexMax = MAX_STREAM_VOLUME[streamType] * 10; - AudioSystem.initStreamVolume(streamType, mIndexMin / 10, mIndexMax / 10); + final int status = AudioSystem.initStreamVolume( + streamType, mIndexMin / 10, mIndexMax / 10); + if (status != AudioSystem.AUDIO_STATUS_OK) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "VSS() stream:" + streamType + " initStreamVolume=" + status) + .printLog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } readSettings(); mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION); @@ -6433,6 +6513,10 @@ public class AudioService extends IAudioService.Stub mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; + + case MSG_REINIT_VOLUMES: + onReinitVolumes((String) msg.obj); + break; } } } @@ -7363,12 +7447,16 @@ public class AudioService extends IAudioService.Stub //========================================================================================== // AudioService logging and dumpsys //========================================================================================== + static final int LOG_NB_EVENTS_LIFECYCLE = 20; static final int LOG_NB_EVENTS_PHONE_STATE = 20; static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30; static final int LOG_NB_EVENTS_FORCE_USE = 20; static final int LOG_NB_EVENTS_VOLUME = 40; static final int LOG_NB_EVENTS_DYN_POLICY = 10; + static final AudioEventLogger sLifecycleLogger = new AudioEventLogger(LOG_NB_EVENTS_LIFECYCLE, + "audio services lifecycle"); + final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE, "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))"); @@ -7445,6 +7533,7 @@ public class AudioService extends IAudioService.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + sLifecycleLogger.dump(pw); if (mAudioHandler != null) { pw.println("\nMessage handler (watch for unhandled messages):"); mAudioHandler.dump(new PrintWriterPrinter(pw), " "); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index b4c41b274dbe..7616557ac80f 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -432,19 +432,36 @@ public class BtHelper { // and this must be done on behalf of system server to make sure permissions are granted. final long ident = Binder.clearCallingIdentity(); if (client != null) { - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, - SCO_MODE_VIRTUAL_CALL); - // If a disconnection is pending, the client will be removed whne clearAllScoClients() - // is called form receiveBtEvent() - if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ - && mScoAudioState != SCO_STATE_DEACTIVATING) { - client.remove(false /*stop */, true /*unregister*/); - } + stopAndRemoveClient(client, eventSource); } Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void stopBluetoothScoForPid(int pid) { + ScoClient client = getScoClientForPid(pid); + if (client == null) { + return; + } + final String eventSource = new StringBuilder("stopBluetoothScoForPid(") + .append(pid).append(")").toString(); + stopAndRemoveClient(client, eventSource); + } + + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + // @GuardedBy("BtHelper.this") + private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + // If a disconnection is pending, the client will be removed when clearAllScoClients() + // is called form receiveBtEvent() + if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATING) { + client.remove(false /*stop */, true /*unregister*/); + } + } /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { if (mHearingAid == null) { @@ -974,6 +991,16 @@ public class BtHelper { return null; } + @GuardedBy("BtHelper.this") + private ScoClient getScoClientForPid(int pid) { + for (ScoClient cl : mScoClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 2672f848f192..cf03c607a368 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -948,7 +948,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem if (debug) { Slog.d(mTag, "Eagerly recreating service for user " + userId); } - getServiceForUserLocked(userId); + updateCachedServiceLocked(userId); } } onServicePackageRestartedLocked(userId); diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 9adde24a99e0..5c9350a8452c 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -2064,10 +2064,12 @@ public class LocationManagerService extends ILocationManager.Stub { } } + @Nullable @Override - public boolean getCurrentLocation(LocationRequest locationRequest, - ICancellationSignal remoteCancellationSignal, ILocationListener listener, - String packageName, String featureId, String listenerId) { + public ICancellationSignal getCurrentLocation(LocationRequest locationRequest, + ILocationListener listener, String packageName, String featureId, String listenerId) { + ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport(); + // side effect of validating locationRequest and packageName Location lastLocation = getLastLocation(locationRequest, packageName, featureId); if (lastLocation != null) { @@ -2077,17 +2079,17 @@ public class LocationManagerService extends ILocationManager.Stub { if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { try { listener.onLocationChanged(lastLocation); - return true; + return remoteCancellationSignal; } catch (RemoteException e) { Log.w(TAG, e); - return false; + return null; } } if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) { if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) { // not allowed to request new locations, so we can't return anything - return false; + return null; } } } @@ -2095,11 +2097,8 @@ public class LocationManagerService extends ILocationManager.Stub { requestLocationUpdates(locationRequest, listener, null, packageName, featureId, listenerId); CancellationSignal cancellationSignal = CancellationSignal.fromTransport( remoteCancellationSignal); - if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener( - () -> removeUpdates(listener, null)); - } - return true; + cancellationSignal.setOnCancelListener(() -> removeUpdates(listener, null)); + return remoteCancellationSignal; } @Override diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 90370ddd21dd..69b02ceb2411 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2620,7 +2620,6 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId); final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid( getGateKeeperService(), credentialHash, credential, userId); - onAuthTokenKnownForUser(userId, auth); if (auth == null) { Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token"); return null; @@ -2643,6 +2642,7 @@ public class LockSettingsService extends ILockSettings.Stub { } fixateNewestUserKeyAuth(userId); setSyntheticPasswordHandleLocked(handle, userId); + onAuthTokenKnownForUser(userId, auth); return auth; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e42dd359dad1..5d0981da1906 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6245,6 +6245,8 @@ public class NotificationManagerService extends SystemService { for (NotificationRecord r : enqueued) { if (r.mUpdateTimeMs > mWhen) { // At least one enqueue was posted after the cancel, so we're invalid + Slog.i(TAG, "notification cancel ignored due to newer enqueued entry" + + "key=" + r.getSbn().getKey()); return; } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 069a00f03a1d..10f77144e022 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -35,6 +35,9 @@ import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedMainComponent; import android.content.pm.parsing.component.ParsedProvider; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.HandlerThread; import android.os.Process; import android.os.Trace; import android.os.UserHandle; @@ -48,6 +51,7 @@ import android.util.SparseBooleanArray; import android.util.SparseSetArray; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.FgThread; @@ -61,6 +65,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.Executor; /** * The entity responsible for filtering visibility between apps based on declarations in their @@ -96,6 +101,12 @@ public class AppsFilter { private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>(); /** + * Executor for running reasonably short background tasks such as building the initial + * visibility cache. + */ + private final Executor mBackgroundExecutor; + + /** * Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of * protected broadcast. This in turn invalidates all prior additions and require a very * computationally expensive recomputing. @@ -125,6 +136,8 @@ public class AppsFilter { private PackageParser.SigningDetails mSystemSigningDetails; private Set<String> mProtectedBroadcasts = new ArraySet<>(); + private final Object mCacheLock = new Object(); + /** * This structure maps uid -> uid and indicates whether access from the first should be * filtered to the second. It's essentially a cache of the @@ -132,6 +145,7 @@ public class AppsFilter { * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on * initial scam and is null until {@link #onSystemReady()} is called. */ + @GuardedBy("mCacheLock") private volatile SparseArray<SparseBooleanArray> mShouldFilterCache; @VisibleForTesting(visibility = PRIVATE) @@ -139,13 +153,15 @@ public class AppsFilter { FeatureConfig featureConfig, String[] forceQueryableWhitelist, boolean systemAppsQueryable, - @Nullable OverlayReferenceMapper.Provider overlayProvider) { + @Nullable OverlayReferenceMapper.Provider overlayProvider, + Executor backgroundExecutor) { mFeatureConfig = featureConfig; mForceQueryableByDevicePackageNames = forceQueryableWhitelist; mSystemAppsQueryable = systemAppsQueryable; mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/, overlayProvider); mStateProvider = stateProvider; + mBackgroundExecutor = backgroundExecutor; } /** @@ -337,8 +353,13 @@ public class AppsFilter { injector.getUserManagerInternal().getUserInfos()); } }; + HandlerThread appsFilterThread = new HandlerThread("appsFilter"); + appsFilterThread.start(); + Handler appsFilterHandler = new Handler(appsFilterThread.getLooper()); + Executor executor = new HandlerExecutor(appsFilterHandler); + AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig, - forcedQueryablePackageNames, forceSystemAppsQueryable, null); + forcedQueryablePackageNames, forceSystemAppsQueryable, null, executor); featureConfig.setAppsFilter(appsFilter); return appsFilter; } @@ -470,29 +491,26 @@ public class AppsFilter { if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); } - if (mShouldFilterCache != null) { - // update the cache in a one-off manner since we've got all the information we need. - SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid); - if (visibleUids == null) { - visibleUids = new SparseBooleanArray(); - mShouldFilterCache.put(recipientUid, visibleUids); + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + // update the cache in a one-off manner since we've got all the information we + // need. + SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid); + if (visibleUids == null) { + visibleUids = new SparseBooleanArray(); + mShouldFilterCache.put(recipientUid, visibleUids); + } + visibleUids.put(visibleUid, false); } - visibleUids.put(visibleUid, false); } } } public void onSystemReady() { - mStateProvider.runWithState(new StateProvider.CurrentStateCallback() { - @Override - public void currentState(ArrayMap<String, PackageSetting> settings, - UserInfo[] users) { - mShouldFilterCache = new SparseArray<>(users.length * settings.size()); - } - }); - mFeatureConfig.onSystemReady(); mOverlayReferenceMapper.rebuildIfDeferred(); - updateEntireShouldFilterCache(); + mFeatureConfig.onSystemReady(); + + updateEntireShouldFilterCacheAsync(); } /** @@ -510,10 +528,12 @@ public class AppsFilter { } mStateProvider.runWithState((settings, users) -> { addPackageInternal(newPkgSetting, settings); - if (mShouldFilterCache != null) { - updateShouldFilterCacheForPackage( - null, newPkgSetting, settings, users, settings.size()); - } // else, rebuild entire cache when system is ready + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting, + settings, users, settings.size()); + } // else, rebuild entire cache when system is ready + } }); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -607,6 +627,7 @@ public class AppsFilter { mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/); } + @GuardedBy("mCacheLock") private void removeAppIdFromVisibilityCache(int appId) { if (mShouldFilterCache == null) { return; @@ -627,31 +648,95 @@ public class AppsFilter { private void updateEntireShouldFilterCache() { mStateProvider.runWithState((settings, users) -> { - mShouldFilterCache.clear(); - for (int i = settings.size() - 1; i >= 0; i--) { - updateShouldFilterCacheForPackage( - null /*skipPackage*/, settings.valueAt(i), settings, users, i); + SparseArray<SparseBooleanArray> cache = + updateEntireShouldFilterCacheInner(settings, users); + synchronized (mCacheLock) { + mShouldFilterCache = cache; } }); } - public void onUsersChanged() { - if (mShouldFilterCache != null) { - updateEntireShouldFilterCache(); + private SparseArray<SparseBooleanArray> updateEntireShouldFilterCacheInner( + ArrayMap<String, PackageSetting> settings, UserInfo[] users) { + SparseArray<SparseBooleanArray> cache = + new SparseArray<>(users.length * settings.size()); + for (int i = settings.size() - 1; i >= 0; i--) { + updateShouldFilterCacheForPackage(cache, + null /*skipPackage*/, settings.valueAt(i), settings, users, i); } + return cache; } - private void updateShouldFilterCacheForPackage(String packageName) { - mStateProvider.runWithState((settings, users) -> { - updateShouldFilterCacheForPackage(null /* skipPackage */, settings.get(packageName), - settings, users, settings.size() /*maxIndex*/); + private void updateEntireShouldFilterCacheAsync() { + mBackgroundExecutor.execute(() -> { + final ArrayMap<String, PackageSetting> settingsCopy = new ArrayMap<>(); + final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>(); + final UserInfo[][] usersRef = new UserInfo[1][]; + mStateProvider.runWithState((settings, users) -> { + packagesCache.ensureCapacity(settings.size()); + settingsCopy.putAll(settings); + usersRef[0] = users; + // store away the references to the immutable packages, since settings are retained + // during updates. + for (int i = 0, max = settings.size(); i < max; i++) { + final AndroidPackage pkg = settings.valueAt(i).pkg; + packagesCache.put(settings.keyAt(i), pkg); + } + }); + SparseArray<SparseBooleanArray> cache = + updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]); + boolean[] changed = new boolean[1]; + // We have a cache, let's make sure the world hasn't changed out from under us. + mStateProvider.runWithState((settings, users) -> { + if (settings.size() != settingsCopy.size()) { + changed[0] = true; + return; + } + for (int i = 0, max = settings.size(); i < max; i++) { + final AndroidPackage pkg = settings.valueAt(i).pkg; + if (!Objects.equals(pkg, packagesCache.get(settings.keyAt(i)))) { + changed[0] = true; + return; + } + } + }); + if (changed[0]) { + // Something has changed, just update the cache inline with the lock held + updateEntireShouldFilterCache(); + if (DEBUG_LOGGING) { + Slog.i(TAG, "Rebuilding cache with lock due to package change."); + } + } else { + synchronized (mCacheLock) { + mShouldFilterCache = cache; + } + } }); + } + public void onUsersChanged() { + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + updateEntireShouldFilterCache(); + } + } } - private void updateShouldFilterCacheForPackage(@Nullable String skipPackageName, - PackageSetting subjectSetting, ArrayMap<String, PackageSetting> allSettings, - UserInfo[] allUsers, int maxIndex) { + private void updateShouldFilterCacheForPackage(String packageName) { + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + mStateProvider.runWithState((settings, users) -> { + updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */, + settings.get(packageName), settings, users, + settings.size() /*maxIndex*/); + }); + } + } + } + + private void updateShouldFilterCacheForPackage(SparseArray<SparseBooleanArray> cache, + @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String, + PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) { for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) { PackageSetting otherSetting = allSettings.valueAt(i); if (subjectSetting.appId == otherSetting.appId) { @@ -668,17 +753,17 @@ public class AppsFilter { for (int ou = 0; ou < userCount; ou++) { int otherUser = allUsers[ou].id; int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId); - if (!mShouldFilterCache.contains(subjectUid)) { - mShouldFilterCache.put(subjectUid, new SparseBooleanArray(appxUidCount)); + if (!cache.contains(subjectUid)) { + cache.put(subjectUid, new SparseBooleanArray(appxUidCount)); } int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); - if (!mShouldFilterCache.contains(otherUid)) { - mShouldFilterCache.put(otherUid, new SparseBooleanArray(appxUidCount)); + if (!cache.contains(otherUid)) { + cache.put(otherUid, new SparseBooleanArray(appxUidCount)); } - mShouldFilterCache.get(subjectUid).put(otherUid, + cache.get(subjectUid).put(otherUid, shouldFilterApplicationInternal( subjectUid, subjectSetting, otherSetting, otherUser)); - mShouldFilterCache.get(otherUid).put(subjectUid, + cache.get(otherUid).put(subjectUid, shouldFilterApplicationInternal( otherUid, otherSetting, subjectSetting, subjectUser)); } @@ -712,7 +797,8 @@ public class AppsFilter { * This method recomputes all component / intent-based visibility and is intended to match the * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)} */ - private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) { + private void recomputeComponentVisibility( + ArrayMap<String, PackageSetting> existingSettings) { mQueriesViaComponent.clear(); for (int i = existingSettings.size() - 1; i >= 0; i--) { PackageSetting setting = existingSettings.valueAt(i); @@ -854,15 +940,17 @@ public class AppsFilter { } } - removeAppIdFromVisibilityCache(setting.appId); - if (mShouldFilterCache != null && setting.sharedUser != null) { - for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) { - PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i); - if (siblingSetting == setting) { - continue; + synchronized (mCacheLock) { + removeAppIdFromVisibilityCache(setting.appId); + if (mShouldFilterCache != null && setting.sharedUser != null) { + for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) { + PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i); + if (siblingSetting == setting) { + continue; + } + updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name, + siblingSetting, settings, users, settings.size()); } - updateShouldFilterCacheForPackage( - setting.name, siblingSetting, settings, users, settings.size()); } } }); @@ -888,26 +976,29 @@ public class AppsFilter { || callingAppId == targetPkgSetting.appId) { return false; } - if (mShouldFilterCache != null) { // use cache - SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid); - final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId); - if (shouldFilterTargets == null) { - Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + callingUid); - return true; - } - int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid); - if (indexOfTargetUid < 0) { - Slog.w(TAG, "Encountered calling -> target with no cached rules: " - + callingUid + " -> " + targetUid); - return true; - } - if (!shouldFilterTargets.valueAt(indexOfTargetUid)) { - return false; - } - } else { - if (!shouldFilterApplicationInternal( - callingUid, callingSetting, targetPkgSetting, userId)) { - return false; + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { // use cache + SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid); + final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId); + if (shouldFilterTargets == null) { + Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + + callingUid); + return true; + } + int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid); + if (indexOfTargetUid < 0) { + Slog.w(TAG, "Encountered calling -> target with no cached rules: " + + callingUid + " -> " + targetUid); + return true; + } + if (!shouldFilterTargets.valueAt(indexOfTargetUid)) { + return false; + } + } else { + if (!shouldFilterApplicationInternal( + callingUid, callingSetting, targetPkgSetting, userId)) { + return false; + } } } if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(callingAppId)) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5c3644e5b262..daca0b34ec2a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2991,7 +2991,10 @@ public class PackageManagerService extends IPackageManager.Stub t.traceEnd(); t.traceBegin("read user settings"); - mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false)); + mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers( + /* excludePartial= */ true, + /* excludeDying= */ false, + /* excludePreCreated= */ false)); t.traceEnd(); // Clean up orphaned packages for which the code path doesn't exist diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 8bbe9cc01ada..7b4e4d94baa2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2313,7 +2313,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isVendorApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isVendor(); } catch (RemoteException e) { return false; @@ -2322,7 +2323,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isProductApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isProduct(); } catch (RemoteException e) { return false; @@ -2331,7 +2333,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isSystemExtApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isSystemExt(); } catch (RemoteException e) { return false; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index a0feb94fd3fc..aeea2514d0a4 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1613,6 +1613,7 @@ public final class Settings { return; } str = new FileInputStream(userPackagesStateFile); + if (DEBUG_MU) Log.i(TAG, "Reading " + userPackagesStateFile); } final XmlPullParser parser = Xml.newPullParser(); parser.setInput(str, StandardCharsets.UTF_8.name()); @@ -2057,9 +2058,13 @@ public final class Settings { serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS); + if (DEBUG_MU) Log.i(TAG, "Writing " + userPackagesStateFile); for (final PackageSetting pkg : mPackages.values()) { final PackageUserState ustate = pkg.readUserState(userId); - if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled); + if (DEBUG_MU) { + Log.i(TAG, " pkg=" + pkg.name + ", installed=" + ustate.installed + + ", state=" + ustate.enabled); + } serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, pkg.name); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 5b1c0fdb8249..600357b7697f 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -743,7 +743,6 @@ public class StagingManager { PackageInstaller.SessionParams params = originalSession.params.copy(); params.isStaged = false; params.installFlags |= PackageManager.INSTALL_STAGED; - // TODO(b/129744602): use the userid from the original session. if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; params.installFlags |= PackageManager.INSTALL_DRY_RUN; @@ -753,7 +752,7 @@ public class StagingManager { try { int apkSessionId = mPi.createSession( params, originalSession.getInstallerPackageName(), - 0 /* UserHandle.SYSTEM */); + originalSession.userId); PackageInstallerSession apkSession = mPi.getSession(apkSessionId); apkSession.open(); for (int i = 0, size = apkFilePaths.size(); i < size; i++) { @@ -811,10 +810,9 @@ public class StagingManager { if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } - // TODO(b/129744602): use the userid from the original session. final int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), - 0 /* UserHandle.SYSTEM */); + session.userId); final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId); try { apkParentSession.open(); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 01a3d14e05df..f5d7d9eda1ba 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -173,6 +173,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PARTIAL = "partial"; private static final String ATTR_PRE_CREATED = "preCreated"; + private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; @@ -2896,6 +2897,9 @@ public class UserManagerService extends IUserManager.Stub { if (userInfo.preCreated) { serializer.attribute(null, ATTR_PRE_CREATED, "true"); } + if (userInfo.convertedFromPreCreated) { + serializer.attribute(null, ATTR_CONVERTED_FROM_PRE_CREATED, "true"); + } if (userInfo.guestToRemove) { serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); } @@ -3053,6 +3057,7 @@ public class UserManagerService extends IUserManager.Stub { int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID; boolean partial = false; boolean preCreated = false; + boolean converted = false; boolean guestToRemove = false; boolean persistSeedData = false; String seedAccountName = null; @@ -3104,6 +3109,10 @@ public class UserManagerService extends IUserManager.Stub { if ("true".equals(valueString)) { preCreated = true; } + valueString = parser.getAttributeValue(null, ATTR_CONVERTED_FROM_PRE_CREATED); + if ("true".equals(valueString)) { + converted = true; + } valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); if ("true".equals(valueString)) { guestToRemove = true; @@ -3161,6 +3170,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint; userInfo.partial = partial; userInfo.preCreated = preCreated; + userInfo.convertedFromPreCreated = converted; userInfo.guestToRemove = guestToRemove; userInfo.profileGroupId = profileGroupId; userInfo.profileBadge = profileBadge; @@ -3607,6 +3617,7 @@ public class UserManagerService extends IUserManager.Stub { preCreatedUser.name = name; preCreatedUser.flags = newFlags; preCreatedUser.preCreated = false; + preCreatedUser.convertedFromPreCreated = true; preCreatedUser.creationTime = getCreationTime(); synchronized (mPackagesLock) { @@ -4669,6 +4680,7 @@ public class UserManagerService extends IUserManager.Stub { running ? " (running)" : "", user.partial ? " (partial)" : "", user.preCreated ? " (pre-created)" : "", + user.convertedFromPreCreated ? " (converted)" : "", current ? " (current)" : ""); } else { // NOTE: the standard "list users" command is used by integration tests and @@ -4753,6 +4765,9 @@ public class UserManagerService extends IUserManager.Stub { if (userInfo.preCreated) { pw.print(" <pre-created>"); } + if (userInfo.convertedFromPreCreated) { + pw.print(" <converted>"); + } pw.println(); pw.print(" Type: "); pw.println(userInfo.userType); pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" ("); @@ -5102,8 +5117,14 @@ public class UserManagerService extends IUserManager.Stub { @Override public @NonNull List<UserInfo> getUsers(boolean excludeDying) { - return UserManagerService.this.getUsersInternal(/*excludePartial= */ true, - excludeDying, /* excludePreCreated= */ true); + return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); + } + + @Override + public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + boolean excludePreCreated) { + return UserManagerService.this.getUsersInternal(excludePartial, excludeDying, + excludePreCreated); } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 2533aa7c4302..66d8b5974261 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2603,13 +2603,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int N = pkg.getRequestedPermissions().size(); for (int i = 0; i < N; i++) { final String permName = pkg.getRequestedPermissions().get(i); + final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")"; final BasePermission bp = mSettings.getPermissionLocked(permName); final boolean appSupportsRuntimePermissions = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; String upgradedActivityRecognitionPermission = null; - if (DEBUG_INSTALL && bp != null) { - Log.i(TAG, "Package " + pkg.getPackageName() + Log.i(TAG, "Package " + friendlyName + " checking " + permName + ": " + bp); } @@ -2618,7 +2618,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { pkg.getPackageName())) { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Unknown permission " + permName - + " in package " + pkg.getPackageName()); + + " in package " + friendlyName); } } continue; @@ -2634,7 +2634,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { newImplicitPermissions.add(permName); if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName()); + Slog.i(TAG, permName + " is newly added for " + friendlyName); } } else { // Special case for Activity Recognition permission. Even if AR permission @@ -2656,8 +2656,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { newImplicitPermissions.add(permName); if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " - + pkg.getPackageName()); + Slog.i(TAG, permName + " is newly added for " + friendlyName); } break; } @@ -2671,7 +2670,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { // if (DEBUG_PERMISSIONS) { // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() -// + " for package " + pkg.getPackageName()); +// + " for package " + friendlyName); // } // continue; // } @@ -2679,7 +2678,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { if (DEBUG_PERMISSIONS) { Log.i(TAG, "Denying runtime-only permission " + bp.getName() - + " for package " + pkg.getPackageName()); + + " for package " + friendlyName); } continue; } @@ -2716,7 +2715,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Considering granting permission " + perm + " to package " - + pkg.getPackageName()); + + friendlyName); } if (grant != GRANT_DENIED) { @@ -3005,7 +3004,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { || packageOfInterest.equals(pkg.getPackageName())) { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() + + " to package " + friendlyName + " because it was previously installed without"); } } @@ -3020,7 +3019,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { changedInstallPermission = true; if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Un-granting permission " + perm - + " from package " + pkg.getPackageName() + + " from package " + friendlyName + " (protectionLevel=" + bp.getProtectionLevel() + " flags=0x" + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) @@ -3033,7 +3032,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { && (packageOfInterest == null || packageOfInterest.equals(pkg.getPackageName()))) { Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() + + " to package " + friendlyName + " (protectionLevel=" + bp.getProtectionLevel() + " flags=0x" + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java index 3f2b5c231dca..a713e5b13667 100644 --- a/services/core/java/com/android/server/vr/Vr2dDisplay.java +++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java @@ -295,6 +295,7 @@ class Vr2dDisplay { flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5339d86a7b78..1a13d0fc64c4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -70,7 +70,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS; import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY; @@ -1680,7 +1680,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (options != null) { final boolean useLockTask = options.getLockTaskMode(); if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) { - lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; + lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; } } return lockTaskLaunchMode; diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index cd508d0e1860..6d452c39eccc 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1700,8 +1700,9 @@ class ActivityStack extends Task { // If the most recent activity was noHistory but was only stopped rather // than stopped+finished because the device went to sleep, we need to make // sure to finish it as we're making a new activity topmost. - if (shouldSleepActivities() && mLastNoHistoryActivity != null && - !mLastNoHistoryActivity.finishing) { + if (shouldSleepActivities() && mLastNoHistoryActivity != null + && !mLastNoHistoryActivity.finishing + && mLastNoHistoryActivity != next) { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + mLastNoHistoryActivity + " on new resume"); mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */); @@ -1990,7 +1991,7 @@ class ActivityStack extends Task { return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea()); } - void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, + void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { Task rTask = r.getTask(); final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront(); @@ -3336,7 +3337,11 @@ class ActivityStack extends Task { // Do not sleep activities in this stack if we're marked as focused and the keyguard // is in the process of going away. if (isFocusedStackOnDisplay() - && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) { + && mStackSupervisor.getKeyguardController().isKeyguardGoingAway() + // Avoid resuming activities on secondary displays since we don't want bubble + // activities to be resumed while bubble is still collapsed. + // TODO(b/113840485): Having keyguard going away state for secondary displays. + && display.isDefaultDisplay) { return false; } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 6bfcf0c75b83..3dd82a6221c6 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -75,9 +75,9 @@ import static com.android.server.wm.RootWindowContainer.TAG_STATES; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -788,7 +788,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final LockTaskController lockTaskController = mService.getLockTaskController(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV - || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED + || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED && lockTaskController.getLockTaskModeState() == LOCK_TASK_MODE_LOCKED)) { lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index b869eb56f536..7af237b80cfa 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1481,9 +1481,10 @@ class ActivityStarter { // anyone interested in this piece of information. final ActivityStack homeStack = targetTask.getDisplayArea().getRootHomeTask(); final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null); + final ActivityRecord top = targetTask.getTopNonFinishingActivity(); + final boolean visible = top != null && top.isVisible(); mService.getTaskChangeNotificationController().notifyActivityRestartAttempt( - targetTask.getTaskInfo(), homeTaskVisible, clearedTask, - targetTask.getTopNonFinishingActivity().isVisible()); + targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible); } } @@ -1697,8 +1698,9 @@ class ActivityStarter { mRootWindowContainer.sendPowerHintForLaunchStartIfNeeded( false /* forceSend */, mStartActivity); - mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(), - newTask, mKeepCurTransition, mOptions); + mTargetStack.startActivityLocked(mStartActivity, + topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask, + mKeepCurTransition, mOptions); if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index a903bcd3d728..d4dd35f53cf0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -520,8 +520,8 @@ public abstract class ActivityTaskManagerInternal { public abstract void onActiveUidsCleared(); public abstract void onUidProcStateChanged(int uid, int procState); - public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag); - public abstract void onUidRemovedFromPendingTempWhitelist(int uid); + public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag); + public abstract void onUidRemovedFromPendingTempAllowlist(int uid); /** Handle app crash event in {@link android.app.IActivityController} if there is one. */ public abstract boolean handleAppCrashInActivityController(String processName, int pid, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 52d4b6fc78df..0542ef9b09a4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -382,7 +382,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private AppOpsManager mAppOpsManager; /** All active uids in the system. */ private final MirrorActiveUids mActiveUids = new MirrorActiveUids(); - private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); + private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); /** All processes we currently have running mapped by pid and uid */ @@ -1099,7 +1099,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int startActivityIntentSender(IApplicationThread caller, IIntentSender target, - IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, + IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) { enforceNotIsolatedCaller("startActivityIntentSender"); // Refuse possible leaked file descriptors @@ -1122,7 +1122,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesAllowedTime = 0; } } - return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null, + return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions); } @@ -3025,7 +3025,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // system or a specific app. // * System-initiated requests will only start the pinned mode (screen pinning) // * App-initiated requests - // - will put the device in fully locked mode (LockTask), if the app is whitelisted + // - will put the device in fully locked mode (LockTask), if the app is allowlisted // - will start the pinned mode, otherwise final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); @@ -3073,7 +3073,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "updateLockTaskPackages()"); } synchronized (mGlobalLock) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":" + Arrays.toString(packages)); getLockTaskController().updateLockTaskPackages(userId, packages); } @@ -5962,11 +5962,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** - * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on - * the whitelist + * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on + * the allowlist */ - String getPendingTempWhitelistTagForUidLocked(int uid) { - return mPendingTempWhitelist.get(uid); + String getPendingTempAllowlistTagForUidLocked(int uid) { + return mPendingTempAllowlist.get(uid); } void logAppTooSlow(WindowProcessController app, long startTime, String msg) { @@ -7295,16 +7295,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onUidAddedToPendingTempWhitelist(int uid, String tag) { + public void onUidAddedToPendingTempAllowlist(int uid, String tag) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.put(uid, tag); + mPendingTempAllowlist.put(uid, tag); } } @Override - public void onUidRemovedFromPendingTempWhitelist(int uid) { + public void onUidRemovedFromPendingTempAllowlist(int uid) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.remove(uid); + mPendingTempAllowlist.remove(uid); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index abfb9c99de6b..553aad1a43cd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3554,7 +3554,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return false; } return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this) - || mWmService.mForceDesktopModeOnExternalDisplays; + || forceDesktopMode(); + } + + boolean forceDesktopMode() { + return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate(); } private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { @@ -4786,7 +4790,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo boolean supportsSystemDecorations() { return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this) || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 - || mWmService.mForceDesktopModeOnExternalDisplays) + || forceDesktopMode()) // VR virtual display will be used to run and render 2D app within a VR experience. && mDisplayId != mWmService.mVr2dDisplayId // Do not show system decorations on untrusted virtual display. diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index dbc579940e14..4a40fc6e572c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,6 +29,8 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; @@ -248,7 +250,8 @@ public class DisplayPolicy { | View.STATUS_BAR_TRANSPARENT | View.NAVIGATION_BAR_TRANSPARENT; - private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR}; + private static final int[] SHOW_TYPES_FOR_SWIPE = + {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR}; private final WindowManagerService mService; @@ -334,6 +337,16 @@ public class DisplayPolicy { private WindowState mNavigationBarAlt = null; @WindowManagerPolicy.AltBarPosition private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN; + // Alternative climate bar for when flexible insets mapping is used to place a climate bar on + // the screen. + private WindowState mClimateBarAlt = null; + @WindowManagerPolicy.AltBarPosition + private int mClimateBarAltPosition = ALT_BAR_UNKNOWN; + // Alternative extra nav bar for when flexible insets mapping is used to place an extra nav bar + // on the screen. + private WindowState mExtraNavBarAlt = null; + @WindowManagerPolicy.AltBarPosition + private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN; /** See {@link #getNavigationBarFrameHeight} */ private int[] mNavigationBarFrameHeightForRotationDefault = new int[4]; @@ -670,6 +683,12 @@ public class DisplayPolicy { if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) { requestTransientBars(mNavigationBarAlt); } + if (mClimateBarAlt != null && mClimateBarAltPosition == pos) { + requestTransientBars(mClimateBarAlt); + } + if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) { + requestTransientBars(mExtraNavBarAlt); + } } void systemReady() { @@ -941,6 +960,12 @@ public class DisplayPolicy { if (mNavigationBarAlt == win) { mNavigationBarAltPosition = getAltBarPosition(attrs); } + if (mClimateBarAlt == win) { + mClimateBarAltPosition = getAltBarPosition(attrs); + } + if (mExtraNavBarAlt == win) { + mExtraNavBarAltPosition = getAltBarPosition(attrs); + } } /** @@ -1048,6 +1073,16 @@ public class DisplayPolicy { return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } break; + case ITYPE_CLIMATE_BAR: + if (mClimateBarAlt != null && mClimateBarAlt.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + break; + case ITYPE_EXTRA_NAVIGATION_BAR: + if (mExtraNavBarAlt != null && mExtraNavBarAlt.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + break; } } } @@ -1153,6 +1188,14 @@ public class DisplayPolicy { mNavigationBarController.setWindow(mNavigationBarAlt); mNavigationBarAltPosition = getAltBarPosition(attrs); break; + case ITYPE_CLIMATE_BAR: + mClimateBarAlt = win; + mClimateBarAltPosition = getAltBarPosition(attrs); + break; + case ITYPE_EXTRA_NAVIGATION_BAR: + mExtraNavBarAlt = win; + mExtraNavBarAltPosition = getAltBarPosition(attrs); + break; } mDisplayContent.setInsetProvider(insetsType, win, null); } @@ -1201,6 +1244,8 @@ public class DisplayPolicy { switch (insetsType) { case ITYPE_NAVIGATION_BAR: case ITYPE_STATUS_BAR: + case ITYPE_CLIMATE_BAR: + case ITYPE_EXTRA_NAVIGATION_BAR: case ITYPE_CAPTION_BAR: if (++count > 1) { throw new IllegalArgumentException( @@ -1232,6 +1277,12 @@ public class DisplayPolicy { if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(null); } + } else if (mClimateBarAlt == win) { + mClimateBarAlt = null; + mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null); + } else if (mExtraNavBarAlt == win) { + mExtraNavBarAlt = null; + mDisplayContent.setInsetProvider(ITYPE_EXTRA_NAVIGATION_BAR, null, null); } if (mLastFocusedWindow == win) { mLastFocusedWindow = null; @@ -1320,7 +1371,8 @@ public class DisplayPolicy { return R.anim.dock_left_enter; } } - } else if (win == mStatusBarAlt || win == mNavigationBarAlt) { + } else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt + || win == mExtraNavBarAlt) { if (win.getAttrs().windowAnimations != 0) { return ANIMATION_STYLEABLE; } @@ -3347,6 +3399,10 @@ public class DisplayPolicy { (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) + ? Type.statusBars() : 0) + | (requestedState.getSourceOrDefaultVisibility(ITYPE_EXTRA_NAVIGATION_BAR) + ? Type.navigationBars() : 0) + | (requestedState.getSourceOrDefaultVisibility(ITYPE_CLIMATE_BAR) ? Type.statusBars() : 0); if (swipeTarget == mNavigationBar @@ -4046,6 +4102,16 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mNavigationBarAltPosition="); pw.println(mNavigationBarAltPosition); } + if (mClimateBarAlt != null) { + pw.print(prefix); pw.print("mClimateBarAlt="); pw.println(mClimateBarAlt); + pw.print(prefix); pw.print("mClimateBarAltPosition="); + pw.println(mClimateBarAltPosition); + } + if (mExtraNavBarAlt != null) { + pw.print(prefix); pw.print("mExtraNavBarAlt="); pw.println(mExtraNavBarAlt); + pw.print(prefix); pw.print("mExtraNavBarAltPosition="); + pw.println(mExtraNavBarAltPosition); + } if (mFocusedWindow != null) { pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 37ecee8e3db3..1284e009ee3f 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -330,10 +330,8 @@ public class DisplayRotation { // It's also not likely to rotate a TV screen. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); - final boolean forceDesktopMode = - mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay; mDefaultFixedToUserRotation = - (isCar || isTv || mService.mIsPc || forceDesktopMode) + (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode()) // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true // $ adb shell wm size reset diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 470a02e5bd44..34b403b18d20 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -248,7 +248,7 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } - private int getWindowingModeLocked(Entry entry, int displayId) { + private int getWindowingModeLocked(Entry entry, DisplayContent dc) { int windowingMode = entry != null ? entry.mWindowingMode : WindowConfiguration.WINDOWING_MODE_UNDEFINED; // This display used to be in freeform, but we don't support freeform anymore, so fall @@ -259,10 +259,8 @@ class DisplayWindowSettings { } // No record is present so use default windowing mode policy. if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays - && displayId != Display.DEFAULT_DISPLAY; windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement - && (mService.mIsPc || forceDesktopMode) + && (mService.mIsPc || dc.forceDesktopMode()) ? WindowConfiguration.WINDOWING_MODE_FREEFORM : WindowConfiguration.WINDOWING_MODE_FULLSCREEN; } @@ -272,7 +270,7 @@ class DisplayWindowSettings { int getWindowingModeLocked(DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final Entry entry = getEntry(displayInfo); - return getWindowingModeLocked(entry, dc.getDisplayId()); + return getWindowingModeLocked(entry, dc); } void setWindowingModeLocked(DisplayContent dc, int mode) { @@ -382,7 +380,7 @@ class DisplayWindowSettings { final Entry entry = getOrCreateEntry(displayInfo); // Setting windowing mode first, because it may override overscan values later. - dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId())); + dc.setWindowingMode(getWindowingModeLocked(entry, dc)); dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, entry.mUserRotation, entry.mFixedToUserRotation); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 0216db471843..3663ee14ea99 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -219,6 +219,11 @@ final class InputMonitor { WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) { + if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); + } + if (mInputConsumers.containsKey(name)) { throw new IllegalStateException("Existing input consumer found with name: " + name + ", display: " + mDisplayId); @@ -248,6 +253,11 @@ final class InputMonitor { // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL; break; + case INPUT_CONSUMER_RECENTS_ANIMATION: + break; + default: + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); } addInputConsumer(name, consumer); } @@ -459,9 +469,6 @@ final class InputMonitor { mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); - if (mAddWallpaperInputConsumerHandle) { - mWallpaperInputConsumer.show(mInputTransaction, 0); - } if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 8e6a2656fce3..9fdfbd0a09da 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -94,7 +96,8 @@ class InsetsSourceProvider { new Point()); final int type = source.getType(); - if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR) { + if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR || type == ITYPE_CLIMATE_BAR + || type == ITYPE_EXTRA_NAVIGATION_BAR) { mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL; } else if (type == ITYPE_IME) { mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME; diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index c2c04d9f70ac..68405c40ce37 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -43,6 +45,7 @@ import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams.WindowType; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.protolog.common.ProtoLog; @@ -112,7 +115,7 @@ class InsetsStateController { } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { - final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type); + final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs); final WindowToken token = mDisplayContent.getWindowToken(attrs.token); final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; @@ -132,7 +135,9 @@ class InsetsStateController { return false; } - private static @InternalInsetsType int getInsetsTypeForWindowType(int type) { + private static @InternalInsetsType + int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) { + @WindowType int type = attrs.type; switch (type) { case TYPE_STATUS_BAR: return ITYPE_STATUS_BAR; @@ -140,9 +145,22 @@ class InsetsStateController { return ITYPE_NAVIGATION_BAR; case TYPE_INPUT_METHOD: return ITYPE_IME; - default: - return ITYPE_INVALID; } + + // If not one of the types above, check whether an internal inset mapping is specified. + if (attrs.providesInsetsTypes != null) { + for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { + switch (insetsType) { + case ITYPE_STATUS_BAR: + case ITYPE_NAVIGATION_BAR: + case ITYPE_CLIMATE_BAR: + case ITYPE_EXTRA_NAVIGATION_BAR: + return insetsType; + } + } + } + + return ITYPE_INVALID; } /** @see #getInsetsForDispatch */ @@ -155,14 +173,15 @@ class InsetsStateController { state.removeSource(type); // Navigation bar doesn't get influenced by anything else - if (type == ITYPE_NAVIGATION_BAR) { + if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) { state.removeSource(ITYPE_IME); state.removeSource(ITYPE_STATUS_BAR); + state.removeSource(ITYPE_CLIMATE_BAR); state.removeSource(ITYPE_CAPTION_BAR); } // Status bar doesn't get influenced by caption bar - if (type == ITYPE_STATUS_BAR) { + if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) { state.removeSource(ITYPE_CAPTION_BAR); } @@ -332,8 +351,12 @@ class InsetsStateController { @Nullable InsetsControlTarget fakeNavControlling) { onControlChanged(ITYPE_STATUS_BAR, statusControlling); onControlChanged(ITYPE_NAVIGATION_BAR, navControlling); + onControlChanged(ITYPE_CLIMATE_BAR, statusControlling); + onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling); onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling); onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling); + onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling); + onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling); notifyPendingInsetsControlChanged(); } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 892ee717e21f..c36dede013f4 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -33,11 +33,11 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTAS import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -264,12 +264,12 @@ public class LockTaskController { } /** - * @return whether the requested task is allowed to be locked (either whitelisted, or declares + * @return whether the requested task is allowed to be locked (either allowlisted, or declares * lockTaskMode="always" in the manifest). */ - boolean isTaskWhitelisted(Task task) { + boolean isTaskAllowlisted(Task task) { switch(task.mLockTaskAuth) { - case LOCK_TASK_AUTH_WHITELISTED: + case LOCK_TASK_AUTH_ALLOWLISTED: case LOCK_TASK_AUTH_LAUNCHABLE: case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return true; @@ -311,7 +311,7 @@ public class LockTaskController { private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) { // TODO: Double check what's going on here. If the task is already in lock task mode, it's - // likely whitelisted, so will return false below. + // likely allowlisted, so will return false below. if (isTaskLocked(task) && !isNewClearTask) { // If the task is already at the top and won't be cleared, then allow the operation return false; @@ -327,7 +327,7 @@ public class LockTaskController { return false; } - return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty()); + return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty()); } private boolean isRecentsAllowed(int userId) { @@ -356,7 +356,7 @@ public class LockTaskController { return false; default: } - return isPackageWhitelisted(userId, packageName); + return isPackageAllowlisted(userId, packageName); } private boolean isEmergencyCallTask(Task task) { @@ -556,7 +556,7 @@ public class LockTaskController { if (!isSystemCaller) { task.mLockTaskUid = callingUid; if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) { - // startLockTask() called by app, but app is not part of lock task whitelist. Show + // startLockTask() called by app, but app is not part of lock task allowlist. Show // app pinning request. We will come back here with isSystemCaller true. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user"); StatusBarManagerInternal statusBarManager = LocalServices.getService( @@ -649,8 +649,8 @@ public class LockTaskController { /** * Update packages that are allowed to be launched in lock task mode. - * @param userId Which user this whitelist is associated with - * @param packages The whitelist of packages allowed in lock task mode + * @param userId Which user this allowlist is associated with + * @param packages The allowlist of packages allowed in lock task mode * @see #mLockTaskPackages */ void updateLockTaskPackages(int userId, String[] packages) { @@ -659,19 +659,19 @@ public class LockTaskController { boolean taskChanged = false; for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task lockedTask = mLockTaskModeTasks.get(taskNdx); - final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean wasAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; lockedTask.setLockTaskAuth(); - final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean isAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || lockedTask.mUserId != userId - || !wasWhitelisted || isWhitelisted) { + || !wasAllowlisted || isAllowlisted) { continue; } - // Terminate locked tasks that have recently lost whitelist authorization. + // Terminate locked tasks that have recently lost allowlist authorization. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); removeLockedTask(lockedTask); @@ -697,17 +697,17 @@ public class LockTaskController { } } - boolean isPackageWhitelisted(int userId, String pkg) { + boolean isPackageAllowlisted(int userId, String pkg) { if (pkg == null) { return false; } - String[] whitelist; - whitelist = mLockTaskPackages.get(userId); - if (whitelist == null) { + String[] allowlist; + allowlist = mLockTaskPackages.get(userId); + if (allowlist == null) { return false; } - for (String whitelistedPkg : whitelist) { - if (pkg.equals(whitelistedPkg)) { + for (String allowlistedPkg : allowlist) { + if (pkg.equals(allowlistedPkg)) { return true; } } diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java index 0f92bc83a666..61b6e0b25961 100644 --- a/services/core/java/com/android/server/wm/PolicyControl.java +++ b/services/core/java/com/android/server/wm/PolicyControl.java @@ -196,40 +196,40 @@ class PolicyControl { private static final String ALL = "*"; private static final String APPS = "apps"; - private final ArraySet<String> mWhitelist; - private final ArraySet<String> mBlacklist; + private final ArraySet<String> mAllowlist; + private final ArraySet<String> mDenylist; - private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { - mWhitelist = whitelist; - mBlacklist = blacklist; + private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) { + mAllowlist = allowlist; + mDenylist = denylist; } boolean matches(LayoutParams attrs) { if (attrs == null) return false; boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; - if (isApp && mBlacklist.contains(APPS)) return false; - if (onBlacklist(attrs.packageName)) return false; - if (isApp && mWhitelist.contains(APPS)) return true; - return onWhitelist(attrs.packageName); + if (isApp && mDenylist.contains(APPS)) return false; + if (onDenylist(attrs.packageName)) return false; + if (isApp && mAllowlist.contains(APPS)) return true; + return onAllowlist(attrs.packageName); } boolean matches(String packageName) { - return !onBlacklist(packageName) && onWhitelist(packageName); + return !onDenylist(packageName) && onAllowlist(packageName); } - private boolean onBlacklist(String packageName) { - return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + private boolean onDenylist(String packageName) { + return mDenylist.contains(packageName) || mDenylist.contains(ALL); } - private boolean onWhitelist(String packageName) { - return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + private boolean onAllowlist(String packageName) { + return mAllowlist.contains(ALL) || mAllowlist.contains(packageName); } void dump(PrintWriter pw) { pw.print("Filter["); - dump("whitelist", mWhitelist, pw); pw.print(','); - dump("blacklist", mBlacklist, pw); pw.print(']'); + dump("allowlist", mAllowlist, pw); pw.print(','); + dump("denylist", mDenylist, pw); pw.print(']'); } private void dump(String name, ArraySet<String> set, PrintWriter pw) { @@ -253,18 +253,18 @@ class PolicyControl { // e.g. "com.package1", or "apps, com.android.keyguard" or "*" static Filter parse(String value) { if (value == null) return null; - ArraySet<String> whitelist = new ArraySet<String>(); - ArraySet<String> blacklist = new ArraySet<String>(); + ArraySet<String> allowlist = new ArraySet<String>(); + ArraySet<String> denylist = new ArraySet<String>(); for (String token : value.split(",")) { token = token.trim(); if (token.startsWith("-") && token.length() > 1) { token = token.substring(1); - blacklist.add(token); + denylist.add(token); } else { - whitelist.add(token); + allowlist.add(token); } } - return new Filter(whitelist, blacklist); + return new Filter(allowlist, denylist); } } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 851b533a550d..3fe75a4ab49e 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -655,7 +655,8 @@ class RecentTasks { } for (int i = mTasks.size() - 1; i >= 0; --i) { final Task task = mTasks.get(i); - if (task.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(task)) { + if (task.mUserId == userId + && !mService.getLockTaskController().isTaskAllowlisted(task)) { remove(task); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 049e46fb6cef..a65eca832f48 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2305,10 +2305,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent display = getChildAt(displayNdx); - if (display.shouldSleep()) { - continue; - } - boolean resumedOnDisplay = false; for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); @@ -2590,7 +2586,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mDisplayAccessUIDs.clear(); for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent displayContent = getChildAt(displayNdx); - // Only bother calculating the whitelist for private displays + // Only bother calculating the allowlist for private displays if (displayContent.isPrivate()) { mDisplayAccessUIDs.append( displayContent.mDisplayId, displayContent.getPresentUIDs()); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index b71ecbb8a72d..ede6708d5f8f 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -233,10 +233,10 @@ public class SafeActivityOptions { Slog.w(TAG, msg); throw new SecurityException(msg); } - // Check if someone tries to launch an unwhitelisted activity into LockTask mode. + // Check if someone tries to launch an unallowlisted activity into LockTask mode. final boolean lockTaskMode = options.getLockTaskMode(); if (aInfo != null && lockTaskMode - && !supervisor.mService.getLockTaskController().isPackageWhitelisted( + && !supervisor.mService.getLockTaskController().isPackageAllowlisted( UserHandle.getUserId(callingUid), aInfo.packageName)) { final String msg = "Permission Denial: starting " + getIntentString(intent) + " from " + callerApp + " (pid=" + callingPid diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 954b7a026bf5..3a750f2c9da3 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -40,7 +40,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -264,7 +264,7 @@ class Task extends WindowContainer<WindowContainer> { /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE = 2; /** Can enter lockTask without user approval. Can start over existing lockTask task. */ - final static int LOCK_TASK_AUTH_WHITELISTED = 3; + final static int LOCK_TASK_AUTH_ALLOWLISTED = 3; /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing * lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4; @@ -1377,7 +1377,7 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().addStackReferenceIfNeeded((ActivityStack) child); } - // Make sure the list of display UID whitelists is updated + // Make sure the list of display UID allowlists is updated // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); @@ -1605,7 +1605,7 @@ class Task extends WindowContainer<WindowContainer> { case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK"; case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE"; case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE"; - case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED"; + case LOCK_TASK_AUTH_ALLOWLISTED: return "LOCK_TASK_AUTH_ALLOWLISTED"; case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV"; default: return "unknown=" + mLockTaskAuth; } @@ -1625,8 +1625,8 @@ class Task extends WindowContainer<WindowContainer> { final LockTaskController lockTaskController = mAtmService.getLockTaskController(); switch (r.lockTaskLaunchMode) { case LOCK_TASK_LAUNCH_MODE_DEFAULT: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) - ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) + ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: @@ -1637,8 +1637,8 @@ class Task extends WindowContainer<WindowContainer> { mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV; break; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b7ac54f3470e..03834c348d4b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.MANAGE_APP_TOKENS; @@ -1476,9 +1477,14 @@ public class WindowManagerService extends IWindowManager.Stub rootType, attrs.token, attrs.packageName)) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); - token = new WindowToken(this, binder, type, false, displayContent, - session.mCanAddInternalSystemWindow, isRoundedCornerOverlay); + if (hasParent) { + // Use existing parent window token for child windows. + token = parentWindow.mToken; + } else { + final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); + token = new WindowToken(this, binder, type, false, displayContent, + session.mCanAddInternalSystemWindow, isRoundedCornerOverlay); + } } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { activity = token.asActivityRecord(); @@ -5869,6 +5875,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void createInputConsumer(IBinder token, String name, int displayId, InputChannel inputChannel) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { @@ -5880,6 +5891,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean destroyInputConsumer(String name, int displayId) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 95d4ba7c199a..678308af34ea 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -36,6 +36,9 @@ using android::base::StringPrintf; using android::base::WriteStringToFile; +#define SYNC_RECEIVED_WHILE_FROZEN (1) +#define ASYNC_RECEIVED_WHILE_FROZEN (2) + namespace android { // This performs per-process reclaim on all processes belonging to non-app UIDs. @@ -99,12 +102,37 @@ static void com_android_server_am_CachedAppOptimizer_freezeBinder( } } +static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env, + jobject clazz, jint pid) { + bool syncReceived = false, asyncReceived = false; + + int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived); + + if (error < 0) { + jniThrowException(env, "java/lang/RuntimeException", strerror(error)); + } + + jint retVal = 0; + + if(syncReceived) { + retVal |= SYNC_RECEIVED_WHILE_FROZEN;; + } + + if(asyncReceived) { + retVal |= ASYNC_RECEIVED_WHILE_FROZEN; + } + + return retVal; +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"enableFreezerInternal", "(Z)V", (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, - {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder} + {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, + {"getBinderFreezeInfo", "(I)I", + (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo} }; int register_android_server_am_CachedAppOptimizer(JNIEnv* env) diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java index 27a116c8043e..1586a33ba0e9 100644 --- a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java +++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java @@ -34,7 +34,8 @@ public final class SystemCaptionsManagerService extends context, com.android.internal.R.string.config_defaultSystemCaptionsManagerService), /*disallowProperty=*/ null, - /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER); + /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER + | PACKAGE_RESTART_POLICY_REFRESH_EAGER); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 37aedac8f28e..af5a539450ac 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -72,6 +72,7 @@ import java.util.Map; import java.util.Set; import java.util.function.IntFunction; import java.util.stream.Collectors; +import java.util.concurrent.Executor; @Presubmit @RunWith(JUnit4.class) @@ -91,6 +92,8 @@ public class AppsFilterTest { AppsFilter.FeatureConfig mFeatureConfigMock; @Mock AppsFilter.StateProvider mStateProvider; + @Mock + Executor mMockExecutor; private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>(); @@ -187,10 +190,15 @@ public class AppsFilterTest { doAnswer(invocation -> { ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST); - return null; + return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return new Object(); + }).when(mMockExecutor).execute(any(Runnable.class)); + when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true); when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer( (Answer<Boolean>) invocation -> @@ -201,7 +209,8 @@ public class AppsFilterTest { @Test public void testSystemReadyPropogates() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); appsFilter.onSystemReady(); verify(mFeatureConfigMock).onSystemReady(); } @@ -209,7 +218,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -225,7 +235,8 @@ public class AppsFilterTest { @Test public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); final Signature frameworkSignature = Mockito.mock(Signature.class); final PackageParser.SigningDetails frameworkSigningDetails = new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1); @@ -263,7 +274,8 @@ public class AppsFilterTest { @Test public void testQueriesProvider_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -280,7 +292,8 @@ public class AppsFilterTest { @Test public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -297,7 +310,8 @@ public class AppsFilterTest { @Test public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -315,7 +329,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingAction_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -331,7 +346,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -351,7 +367,8 @@ public class AppsFilterTest { @Test public void testNoQueries_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -367,7 +384,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -385,7 +403,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_NonSystemFilters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -402,7 +421,7 @@ public class AppsFilterTest { public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null); + false, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -420,7 +439,8 @@ public class AppsFilterTest { @Test public void testSystemSignedTarget_DoesntFilter() throws CertificateException { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); appsFilter.onSystemReady(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -449,7 +469,7 @@ public class AppsFilterTest { public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null); + false, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -467,7 +487,7 @@ public class AppsFilterTest { public void testSystemQueryable_DoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, - true /* system force queryable */, null); + true /* system force queryable */, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -484,7 +504,8 @@ public class AppsFilterTest { @Test public void testQueriesPackage_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -502,7 +523,8 @@ public class AppsFilterTest { when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))) .thenReturn(false); final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -518,7 +540,8 @@ public class AppsFilterTest { @Test public void testSystemUid_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -533,7 +556,8 @@ public class AppsFilterTest { @Test public void testSystemUidSecondaryUser_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -549,7 +573,8 @@ public class AppsFilterTest { @Test public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -563,7 +588,8 @@ public class AppsFilterTest { @Test public void testNoTargetPackage_filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -621,7 +647,8 @@ public class AppsFilterTest { } return Collections.emptyMap(); } - }); + }, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -693,7 +720,8 @@ public class AppsFilterTest { } return Collections.emptyMap(); } - }); + }, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -718,7 +746,8 @@ public class AppsFilterTest { @Test public void testInitiatingApp_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -734,7 +763,8 @@ public class AppsFilterTest { @Test public void testUninstalledInitiatingApp_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -750,7 +780,8 @@ public class AppsFilterTest { @Test public void testOriginatingApp_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -766,7 +797,8 @@ public class AppsFilterTest { @Test public void testInstallingApp_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -782,7 +814,8 @@ public class AppsFilterTest { @Test public void testInstrumentation_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -804,7 +837,8 @@ public class AppsFilterTest { @Test public void testWhoCanSee() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 66ca839081bc..0ccc02663dc5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -105,7 +105,7 @@ public class UserManagerServiceUserInfoTest { UserData read = mUserManagerService.readUserLP( data.info.id, new ByteArrayInputStream(bytes)); - assertUserInfoEquals(data.info, read.info); + assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false); } @Test @@ -123,7 +123,16 @@ public class UserManagerServiceUserInfoTest { UserInfo read = UserInfo.CREATOR.createFromParcel(in); in.recycle(); - assertUserInfoEquals(info, read); + assertUserInfoEquals(info, read, /* parcelCopy= */ true); + } + + @Test + public void testCopyConstructor() throws Exception { + UserInfo info = createUser(); + + UserInfo copy = new UserInfo(info); + + assertUserInfoEquals(info, copy, /* parcelCopy= */ false); } @Test @@ -227,10 +236,11 @@ public class UserManagerServiceUserInfoTest { user.partial = true; user.guestToRemove = true; user.preCreated = true; + user.convertedFromPreCreated = true; return user; } - private void assertUserInfoEquals(UserInfo one, UserInfo two) { + private void assertUserInfoEquals(UserInfo one, UserInfo two, boolean parcelCopy) { assertEquals("Id not preserved", one.id, two.id); assertEquals("Name not preserved", one.name, two.name); assertEquals("Icon path not preserved", one.iconPath, two.iconPath); @@ -238,11 +248,17 @@ public class UserManagerServiceUserInfoTest { assertEquals("UserType not preserved", one.userType, two.userType); assertEquals("profile group not preserved", one.profileGroupId, two.profileGroupId); - assertEquals("restricted profile parent not preseved", one.restrictedProfileParentId, + assertEquals("restricted profile parent not preserved", one.restrictedProfileParentId, two.restrictedProfileParentId); - assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge); - assertEquals("partial not preseved", one.partial, two.partial); - assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove); - assertEquals("preCreated not preseved", one.preCreated, two.preCreated); + assertEquals("profile badge not preserved", one.profileBadge, two.profileBadge); + assertEquals("partial not preserved", one.partial, two.partial); + assertEquals("guestToRemove not preserved", one.guestToRemove, two.guestToRemove); + assertEquals("preCreated not preserved", one.preCreated, two.preCreated); + if (parcelCopy) { + assertFalse("convertedFromPreCreated should not be set", two.convertedFromPreCreated); + } else { + assertEquals("convertedFromPreCreated not preserved", one.convertedFromPreCreated, + two.convertedFromPreCreated); + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 1ef0acd20835..02a3bb1f5632 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -22,7 +22,7 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; @@ -1694,7 +1694,7 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testGetLockTaskLaunchMode() { final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true); mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; - assertEquals(LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED, + assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED, ActivityRecord.getLockTaskLaunchMode(mActivity.info, options)); mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 1b42a0466cf7..e898c2573315 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1206,19 +1206,22 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testShouldSleepActivities() { // When focused activity and keyguard is going away, we should not sleep regardless - // of the display state + // of the display state, but keyguard-going-away should only take effects on default + // display since there is no keyguard on secondary displays (yet). verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, false /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); + verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, + true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */); // When not the focused stack, defer to display sleeping state. verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); // If keyguard is going away, defer to the display sleeping state. verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - false /* displaySleeping */, false /* expected*/); + false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); } @Test @@ -1428,9 +1431,11 @@ public class ActivityStackTests extends ActivityTestsBase { } private void verifyShouldSleepActivities(boolean focusedStack, - boolean keyguardGoingAway, boolean displaySleeping, boolean expected) { + boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, + boolean expected) { final DisplayContent display = mock(DisplayContent.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); + display.isDefaultDisplay = isDefaultDisplay; doReturn(display).when(mStack).getDisplay(); doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index dc838f1bb288..6a84c1390150 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -28,6 +28,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.Q; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.FLAG_PRIVATE; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; @@ -95,6 +96,7 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; +import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; @@ -1439,6 +1441,29 @@ public class DisplayContentTests extends WindowTestsBase { mDisplayContent.ensureActivitiesVisible(null, 0, false, false); } + @Test + public void testForceDesktopMode() { + mWm.mForceDesktopModeOnExternalDisplays = true; + // Not applicable for default display + final DisplayContent defaultDisplay = mWm.mRoot.getDefaultDisplay(); + assertFalse(defaultDisplay.forceDesktopMode()); + + // Not applicable for private secondary display. + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.flags = FLAG_PRIVATE; + final DisplayContent privateDc = createNewDisplay(displayInfo); + assertFalse(privateDc.forceDesktopMode()); + + // Applicable for public secondary display. + final DisplayContent publicDc = createNewDisplay(); + assertTrue(publicDc.forceDesktopMode()); + + // Make sure forceDesktopMode() is false when the force config is disabled. + mWm.mForceDesktopModeOnExternalDisplays = false; + assertFalse(publicDc.forceDesktopMode()); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 2d3b74b75826..cb9b49abfe2b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -20,6 +20,8 @@ import static android.view.Gravity.BOTTOM; import static android.view.Gravity.LEFT; import static android.view.Gravity.RIGHT; import static android.view.Gravity.TOP; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_TOP_GESTURES; @@ -198,10 +200,15 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void addingWindow_throwsException_WithMultipleInsetTypes() { - WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); - win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}; + WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); + win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}; + + expectThrows(IllegalArgumentException.class, () -> addWindow(win1)); + + WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); + win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; - expectThrows(IllegalArgumentException.class, () -> addWindow(win)); + expectThrows(IllegalArgumentException.class, () -> addWindow(win2)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 0a27e1a1da68..73ac408f7980 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.InsetsState.ITYPE_CLIMATE_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -292,12 +294,17 @@ public class InsetsStateControllerTest extends WindowTestsBase { public void testBarControllingWinChanged() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar"); + final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar"); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindow(climateBar, null, null); + getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindow(extraNavBar, null, + null); getController().onBarControlTargetChanged(app, null, app, null); InsetsSourceControl[] controls = getController().getControlsForDispatch(app); - assertEquals(2, controls.length); + assertEquals(4, controls.length); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index a137cde2d351..e345becf0499 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -168,8 +168,8 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_once() throws Exception { - // GIVEN a task record with whitelisted auth - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN a task record with allowlisted auth + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode without resuming mLockTaskController.startLockTaskMode(tr, false, TEST_UID); @@ -185,9 +185,9 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_twice() throws Exception { - // GIVEN two task records with whitelisted auth - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode on both tasks mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -205,7 +205,7 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_pinningRequest() { - // GIVEN a task record that is not whitelisted, i.e. with pinned auth + // GIVEN a task record that is not allowlisted, i.e. with pinned auth Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE); // WHEN calling startLockTaskMode @@ -236,23 +236,23 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN it's not a lock task violation to try and launch this task without clearing assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false)); - // THEN it's a lock task violation to launch another task that is not whitelisted + // THEN it's a lock task violation to launch another task that is not allowlisted assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_PINNABLE))); // THEN it's a lock task violation to launch another task that is disallowed from lock task assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_DONT_LOCK))); - // THEN it's no a lock task violation to launch another task that is whitelisted + // THEN it's no a lock task violation to launch another task that is allowlisted assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( - Task.LOCK_TASK_AUTH_WHITELISTED))); + Task.LOCK_TASK_AUTH_ALLOWLISTED))); assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_LAUNCHABLE))); // THEN it's not a lock task violation to launch another task that is priv launchable @@ -262,8 +262,8 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation_emergencyCall() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // GIVEN tasks necessary for emergency calling @@ -294,8 +294,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode() throws Exception { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN the same caller calls stopLockTaskMode @@ -311,8 +311,8 @@ public class LockTaskControllerTest { @Test(expected = SecurityException.class) public void testStopLockTaskMode_differentCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN a different caller calls stopLockTaskMode @@ -323,8 +323,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_systemCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN system calls stopLockTaskMode @@ -336,9 +336,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_twoTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -357,9 +357,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_rootTask() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -405,9 +405,9 @@ public class LockTaskControllerTest { @Test public void testClearLockedTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -434,7 +434,7 @@ public class LockTaskControllerTest { .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -454,7 +454,7 @@ public class LockTaskControllerTest { .thenReturn(true); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -471,7 +471,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -488,7 +488,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -500,45 +500,45 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskPackages() { - String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - String[] whitelist2 = {TEST_PACKAGE_NAME}; - - // No package is whitelisted initially - for (String pkg : whitelist1) { - assertFalse("Package shouldn't be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + String[] allowlist2 = {TEST_PACKAGE_NAME}; + + // No package is allowlisted initially + for (String pkg : allowlist1) { + assertFalse("Package shouldn't be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Apply whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1); + // Apply allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1); - // Assert the whitelist is applied to the correct user - for (String pkg : whitelist1) { - assertTrue("Package should be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + // Assert the allowlist is applied to the correct user + for (String pkg : allowlist1) { + assertTrue("Package should be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Update whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2); + // Update allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2); - // Assert the new whitelist is applied - assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME)); - assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); + // Assert the new allowlist is applied + assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME)); + assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); } @Test public void testUpdateLockTaskPackages_taskRemoved() throws Exception { - // GIVEN two tasks which are whitelisted initially + // GIVEN two tasks which are allowlisted initially Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true); Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false); - String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // GIVEN the tasks are launched into LockTask mode mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -548,9 +548,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr2)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing one package from whitelist - whitelist = new String[] {TEST_PACKAGE_NAME}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing one package from allowlist + allowlist = new String[] {TEST_PACKAGE_NAME}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the task running that package should be stopped verify(tr2).performClearTaskLocked(); @@ -560,9 +560,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr1)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing the last package from whitelist - whitelist = new String[] {}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing the last package from allowlist + allowlist = new String[] {}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the last task should be cleared, and the system should quit LockTask mode verify(tr1).performClearTaskLocked(); @@ -574,7 +574,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -616,7 +616,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_differentUser() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -638,7 +638,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_keyguard() { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN keyguard should be disabled @@ -704,7 +704,7 @@ public class LockTaskControllerTest { TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); // Start lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled @@ -719,15 +719,15 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS)); - // unwhitelisted package should not be allowed + // unallowlisted package should not be allowed assertFalse(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); - // update the whitelist - String[] whitelist = new String[] { TEST_PACKAGE_NAME }; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // update the allowlist + String[] allowlist = new String[] { TEST_PACKAGE_NAME }; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); - // whitelisted package should be allowed + // allowlisted package should be allowed assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); @@ -755,17 +755,17 @@ public class LockTaskControllerTest { } /** - * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest + * @param isAppAware {@code true} if the app has marked if_allowlisted in its manifest */ private Task getTaskForUpdate(String pkg, boolean isAppAware) { - final int authIfWhitelisted = isAppAware + final int authIfAllowlisted = isAppAware ? Task.LOCK_TASK_AUTH_LAUNCHABLE - : Task.LOCK_TASK_AUTH_WHITELISTED; - Task tr = getTask(pkg, authIfWhitelisted); + : Task.LOCK_TASK_AUTH_ALLOWLISTED; + Task tr = getTask(pkg, authIfAllowlisted); doAnswer((invocation) -> { - boolean isWhitelisted = - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg); - tr.mLockTaskAuth = isWhitelisted ? authIfWhitelisted : Task.LOCK_TASK_AUTH_PINNABLE; + boolean isAllowlisted = + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg); + tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE; return null; }).when(tr).setLockTaskAuth(); return tr; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index c848736a64c2..51db099676b0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -896,24 +896,6 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); } - @Test - public void testResumeFocusedStackOnSleepingDisplay() { - // Create an activity on secondary display. - final TestDisplayContent secondDisplay = addNewDisplayContentAt( - DisplayContent.POSITION_TOP); - final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build(); - spyOn(activity); - spyOn(stack); - - // Cannot resumed activities on secondary display if the display should sleep. - doReturn(true).when(secondDisplay).shouldSleep(); - mRootWindowContainer.resumeFocusedStacksTopActivities(); - verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any()); - verify(activity, never()).makeActiveIfNeeded(any()); - } - /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases. diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java index ef74861e9422..25ba6db38e05 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java @@ -68,6 +68,7 @@ import com.android.compatibility.common.util.SystemUtil; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -142,6 +143,9 @@ public class ScreenDecorWindowTests { assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness); } + // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported. + // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon. + @Ignore @Test public void testMultipleDecors() { // Test 2 decor windows on-top. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 0896db4b5532..0ab99122e21f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -78,6 +78,10 @@ public class WindowTokenTests extends WindowTestsBase { assertFalse(token.hasWindow(window12)); assertTrue(token.hasWindow(window2)); assertTrue(token.hasWindow(window3)); + + // The child windows should have the same window token as their parents. + assertEquals(window1.mToken, window11.mToken); + assertEquals(window1.mToken, window12.mToken); } @Test diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5d79775f686e..2795f939d230 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -32,6 +32,7 @@ import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsSsData; import com.android.internal.telephony.ICarrierConfigLoader; import com.android.telephony.Rlog; @@ -66,6 +67,20 @@ public class CarrierConfigManager { public static final String EXTRA_SUBSCRIPTION_INDEX = SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX; + /** + * Service class flag if not specify a service class. + * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK + * @hide + */ + public static final int SERVICE_CLASS_NONE = ImsSsData.SERVICE_CLASS_NONE; + + /** + * Service class flag for voice telephony. + * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK + * @hide + */ + public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE; + private final Context mContext; /** @@ -212,6 +227,19 @@ public class CarrierConfigManager { "call_barring_supports_deactivate_all_bool"; /** + * Specifies the service class for call barring service. Default value is + * {@link #SERVICE_CLASS_VOICE}. + * The value set as below: + * <ul> + * <li>0: {@link #SERVICE_CLASS_NONE}</li> + * <li>1: {@link #SERVICE_CLASS_VOICE}</li> + * </ul> + * @hide + */ + public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = + "call_barring_default_service_class_int"; + + /** * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED * events from the Sim. * If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and @@ -3849,10 +3877,23 @@ public class CarrierConfigManager { * Indicating whether DUN APN should be disabled when the device is roaming. In that case, * the default APN (i.e. internet) will be used for tethering. * + * This config is only available when using Preset APN(not user edited) as Preferred APN. + * + * @hide + */ + public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = + "disable_dun_apn_while_roaming_with_preset_apn_bool"; + + /** + * Where there is no preferred APN, specifies the carrier's default preferred APN. + * Specifies the {@link android.provider.Telephony.Carriers.APN} of the default preferred apn. + * + * This config is only available with Preset APN(not user edited). + * * @hide */ - public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING = - "disable_dun_apn_while_roaming"; + public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING = + "default_preferred_apn_name_string"; /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3926,6 +3967,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); + sDefaults.putInt(KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT, SERVICE_CLASS_VOICE); sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL, true); @@ -4388,7 +4430,8 @@ public class CarrierConfigManager { "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3" }); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); - sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING, false); + sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); + sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); } /** diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index a8206e69efd2..45a67b39ea55 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -138,7 +138,11 @@ public final class CellIdentityNr extends CellIdentity { @NonNull @Override public CellLocation asCellLocation() { - return new GsmCellLocation(); + GsmCellLocation cl = new GsmCellLocation(); + int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1; + cl.setLacAndCid(tac, -1); + cl.setPsc(0); + return cl; } @Override diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java index 30cea0e6dd59..bc8ee1dd9359 100644 --- a/telephony/java/android/telephony/gsm/GsmCellLocation.java +++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java @@ -55,7 +55,7 @@ public class GsmCellLocation extends CellLocation { } /** - * @return gsm cell id, -1 if unknown, 0xffff max legal value + * @return gsm cell id, -1 if unknown or invalid, 0xffff max legal value */ public int getCid() { return mCid; diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 2be4ae6bb214..3ccbad84f2e0 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -48,6 +48,9 @@ java_test_host { name: "MultiUserRollbackTest", srcs: ["MultiUserRollbackTest/src/**/*.java"], libs: ["tradefed"], + static_libs: [ + "frameworks-base-hostutils", + ], test_suites: ["general-tests"], test_config: "MultiUserRollbackTest.xml", } diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java index 42b886f0774f..f16084744853 100644 --- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java +++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java @@ -24,6 +24,7 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +41,9 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { private static final long SWITCH_USER_COMPLETED_NUMBER_OF_POLLS = 60; private static final long SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS = 1000; + @Rule + public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); + @After public void tearDown() throws Exception { removeSecondaryUserIfNecessary(); @@ -59,6 +63,30 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { runPhaseForUsers("testBasic", mSecondaryUserId); } + /** + * Tests staged install/rollback works correctly on the 2nd user. + */ + @Test + public void testStagedRollback() throws Exception { + runPhaseForUsers("testStagedRollback_Phase1", mSecondaryUserId); + getDevice().reboot(); + + // Need to unlock the user for device tests to run successfully + getDevice().startUser(mSecondaryUserId); + awaitUserUnlocked(mSecondaryUserId); + runPhaseForUsers("testStagedRollback_Phase2", mSecondaryUserId); + getDevice().reboot(); + + getDevice().startUser(mSecondaryUserId); + awaitUserUnlocked(mSecondaryUserId); + runPhaseForUsers("testStagedRollback_Phase3", mSecondaryUserId); + getDevice().reboot(); + + getDevice().startUser(mSecondaryUserId); + awaitUserUnlocked(mSecondaryUserId); + runPhaseForUsers("testStagedRollback_Phase4", mSecondaryUserId); + } + @Test public void testMultipleUsers() throws Exception { runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId); diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java index 8641f4d4013a..5d133a4de13d 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java @@ -115,4 +115,32 @@ public class MultiUserRollbackTest { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); InstallUtils.processUserData(TestApp.A); } + + @Test + public void testStagedRollback_Phase1() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + Install.single(TestApp.A1).setStaged().commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + } + + @Test + public void testStagedRollback_Phase2() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + } + + @Test + public void testStagedRollback_Phase3() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + RollbackInfo rollback = RollbackUtils.waitForAvailableRollback(TestApp.A); + assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1)); + RollbackUtils.rollback(rollback.getRollbackId()); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + } + + @Test + public void testStagedRollback_Phase4() { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + } } diff --git a/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java new file mode 100644 index 000000000000..5bfc75203ae3 --- /dev/null +++ b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java @@ -0,0 +1,66 @@ +/* + * 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.tests.rollback.host; + +import com.android.ddmlib.Log; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.rules.ExternalResource; + +public final class AbandonSessionsRule extends ExternalResource { + private static final String TAG = "AbandonSessionsRule"; + private final BaseHostJUnit4Test mHost; + + public AbandonSessionsRule(BaseHostJUnit4Test host) { + mHost = host; + } + + @Override + protected void before() throws Throwable { + abandonSessions(mHost.getDevice()); + } + + @Override + protected void after() { + try { + abandonSessions(mHost.getDevice()); + } catch (Exception e) { + mHost.getDevice().logOnDevice(TAG, Log.LogLevel.ERROR, + "%s", "Failed to abandon sessions"); + } + } + + /** + * Abandons all sessions to prevent interference in our tests. + */ + private static void abandonSessions(ITestDevice device) throws Exception { + // No point in abandoning applied or failed sessions. We care about ready sessions only. + String cmdListReadySessions = + "pm list staged-sessions --only-sessionid --only-parent --only-ready"; + String output = device.executeShellCommand(cmdListReadySessions); + if (output.trim().isEmpty()) { + // No sessions to abandon + return; + } + // Ensure we have sufficient privilege to abandon sessions from other apps + device.enableAdbRoot(); + device.executeShellCommand("for i in $(" + cmdListReadySessions + + "); do pm install-abandon $i; done"); + device.disableAdbRoot(); + } +} diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index aa3a13925894..b276f2ed4761 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -581,12 +581,18 @@ public final class ScanResult implements Parcelable { * 6 GHz band frequency of first channel in MHz * @hide */ - public static final int BAND_6_GHZ_START_FREQ_MHZ = 5945; + public static final int BAND_6_GHZ_START_FREQ_MHZ = 5955; /** * 6 GHz band frequency of last channel in MHz * @hide */ - public static final int BAND_6_GHZ_END_FREQ_MHZ = 7105; + public static final int BAND_6_GHZ_END_FREQ_MHZ = 7115; + + /** + * 6 GHz band operating class 136 channel 2 center frequency in MHz + * @hide + */ + public static final int BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ = 5935; /** * Utility function to check if a frequency within 2.4 GHz band @@ -618,7 +624,10 @@ public final class ScanResult implements Parcelable { * @hide */ public static boolean is6GHz(int freqMhz) { - return freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ; + if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) { + return true; + } + return (freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ); } /** @@ -649,6 +658,9 @@ public final class ScanResult implements Parcelable { } if (band == WifiScanner.WIFI_BAND_6_GHZ) { if (channel >= BAND_6_GHZ_FIRST_CH_NUM && channel <= BAND_6_GHZ_LAST_CH_NUM) { + if (channel == 2) { + return BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ; + } return ((channel - BAND_6_GHZ_FIRST_CH_NUM) * 5) + BAND_6_GHZ_START_FREQ_MHZ; } else { return UNSPECIFIED; @@ -673,6 +685,9 @@ public final class ScanResult implements Parcelable { } else if (is5GHz(freqMhz)) { return ((freqMhz - BAND_5_GHZ_START_FREQ_MHZ) / 5) + BAND_5_GHZ_FIRST_CH_NUM; } else if (is6GHz(freqMhz)) { + if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) { + return 2; + } return ((freqMhz - BAND_6_GHZ_START_FREQ_MHZ) / 5) + BAND_6_GHZ_FIRST_CH_NUM; } diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java index 5516f433070f..f1ec5e829316 100644 --- a/wifi/tests/src/android/net/wifi/ScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -102,9 +102,10 @@ public class ScanResultTest { 5845, WifiScanner.WIFI_BAND_5_GHZ, 169, 5865, WifiScanner.WIFI_BAND_5_GHZ, 173, /* Now some 6GHz channels */ - 5945, WifiScanner.WIFI_BAND_6_GHZ, 1, - 5960, WifiScanner.WIFI_BAND_6_GHZ, 4, - 6100, WifiScanner.WIFI_BAND_6_GHZ, 32 + 5955, WifiScanner.WIFI_BAND_6_GHZ, 1, + 5935, WifiScanner.WIFI_BAND_6_GHZ, 2, + 5970, WifiScanner.WIFI_BAND_6_GHZ, 4, + 6110, WifiScanner.WIFI_BAND_6_GHZ, 32 }; /** |