diff options
60 files changed, 1660 insertions, 689 deletions
diff --git a/api/current.txt b/api/current.txt index b98ab5533ae3..377ecddc6064 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11970,7 +11970,6 @@ package android.content.pm { method @CheckResult public abstract int checkSignatures(@NonNull String, @NonNull String); method @CheckResult public abstract int checkSignatures(int, int); method public abstract void clearInstantAppCookie(); - method public void clearMimeGroup(@NonNull String); method @Deprecated public abstract void clearPackagePreferredActivities(@NonNull String); method public abstract String[] currentToCanonicalPackageNames(@NonNull String[]); method public abstract void extendVerificationTimeout(int, int, long); @@ -12006,7 +12005,7 @@ package android.content.pm { method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String); method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String); - method @Nullable public java.util.Set<java.lang.String> getMimeGroup(@NonNull String); + method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String); method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract String getNameForUid(int); method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int); diff --git a/api/system-current.txt b/api/system-current.txt index 28dcd155467c..84f70b531e66 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1771,6 +1771,7 @@ package android.content { } public abstract class ContentResolver { + method public int checkUriPermission(@NonNull android.net.Uri, int, int); method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File); method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri); method @Nullable @RequiresPermission("android.permission.CACHE_CONTENT") public android.os.Bundle getCache(@NonNull android.net.Uri); @@ -9668,6 +9669,7 @@ package android.service.autofill { method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Nullable public android.os.Bundle onGetInlineSuggestionsRendererInfo(); method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int); + method public final void startIntentSender(@NonNull android.content.IntentSender); field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService"; } @@ -10182,6 +10184,7 @@ package android.service.storage { method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onEndSession(@NonNull String) throws java.io.IOException; method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException; + method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException; field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2 field public static final int FLAG_SESSION_TYPE_FUSE = 1; // 0x1 field public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService"; diff --git a/api/test-current.txt b/api/test-current.txt index c50438a2e3f4..3586fec6e948 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3193,6 +3193,7 @@ package android.service.autofill { method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Nullable public android.os.Bundle onGetInlineSuggestionsRendererInfo(); method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int); + method public final void startIntentSender(@NonNull android.content.IntentSender); field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService"; } diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 3d02ffb0db75..77a3eb31fdd4 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -357,30 +357,7 @@ void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value); break; case STRING: { - bool isBytesField = false; - // Bytes field is logged via string format in log_msg format. So here we check - // if this string field is a byte field. - std::map<int, std::vector<int>>::const_iterator itr; - if (depth == 0 && (itr = AtomsInfo::kBytesFieldAtoms.find(tagId)) != - AtomsInfo::kBytesFieldAtoms.end()) { - const std::vector<int>& bytesFields = itr->second; - for (int bytesField : bytesFields) { - if (bytesField == fieldNum) { - // This is a bytes field - isBytesField = true; - break; - } - } - } - if (isBytesField) { - if (dim.mValue.str_value.length() > 0) { - protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum, - (const char*)dim.mValue.str_value.c_str(), - dim.mValue.str_value.length()); - } - } else { - protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); - } + protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); break; } case STORAGE: diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0b0a803c7dcd..a1ec27b3e9f7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -3358,31 +3358,20 @@ public class ApplicationPackageManager extends PackageManager { } } - public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) { - try { - mPM.setMimeGroup(mContext.getPackageName(), mimeGroup, - new ArrayList<String>(mimeTypes)); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - } - @Override - public void clearMimeGroup(String mimeGroup) { + public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) { try { - mPM.clearMimeGroup(mContext.getPackageName(), mimeGroup); + mPM.setMimeGroup(mContext.getPackageName(), mimeGroup, new ArrayList<>(mimeTypes)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } + @NonNull @Override public Set<String> getMimeGroup(String group) { try { List<String> mimeGroup = mPM.getMimeGroup(mContext.getPackageName(), group); - if (mimeGroup == null) { - return null; - } return new ArraySet<>(mimeGroup); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 4c4afb0ed9ae..99b730e89fd4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9388,9 +9388,9 @@ public class DevicePolicyManager { } /** - * Called by device owner or profile owner of secondary users that is affiliated with the - * device to disable the status bar. Disabling the status bar blocks notifications, quick - * settings and other screen overlays that allow escaping from a single use device. + * Called by device owner or profile owner of secondary users that is affiliated with the + * device to disable the status bar. Disabling the status bar blocks notifications and quick + * settings. * <p> * <strong>Note:</strong> This method has no effect for LockTask mode. The behavior of the * status bar in LockTask mode can be configured with diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index d8e8b27d0621..65eb642369c9 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -1585,6 +1585,10 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * This method is typically used when the provider implements more dynamic * access controls that cannot be expressed with {@code <path-permission>} * style static rules. + * <p> + * Because validation of these dynamic access controls has significant + * system health impact, this feature is only available to providers that + * are built into the system. * * @param uri the {@link Uri} to perform an access check on. * @param uid the UID to check the permission for. diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b134c3796b40..7510ce73a59a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1357,8 +1357,28 @@ public abstract class ContentResolver implements ContentInterface { } } - /** {@hide} */ + /** + * Perform a detailed internal check on a {@link Uri} to determine if a UID + * is able to access it with specific mode flags. + * <p> + * This method is typically used when the provider implements more dynamic + * access controls that cannot be expressed with {@code <path-permission>} + * style static rules. + * <p> + * Because validation of these dynamic access controls has significant + * system health impact, this feature is only available to providers that + * are built into the system. + * + * @param uri the {@link Uri} to perform an access check on. + * @param uid the UID to check the permission for. + * @param modeFlags the access flags to use for the access check, such as + * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}. + * @return {@link PackageManager#PERMISSION_GRANTED} if access is allowed, + * otherwise {@link PackageManager#PERMISSION_DENIED}. + * @hide + */ @Override + @SystemApi public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) { Objects.requireNonNull(uri, "uri"); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index b52034f637a4..5bad055810cc 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -743,8 +743,6 @@ interface IPackageManager { void setMimeGroup(String packageName, String group, in List<String> mimeTypes); - void clearMimeGroup(String packageName, String group); - List<String> getMimeGroup(String packageName, String group); boolean isAutoRevokeWhitelisted(String packageName); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2c7902239854..03b99ed7ffc4 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -7894,10 +7894,14 @@ public abstract class PackageManager { } /** - * Sets MIME group's MIME types + * Sets MIME group's MIME types. * - * @param mimeGroup MIME group to modify - * @param mimeTypes new MIME types contained by MIME group + * Libraries should use a reverse-DNS prefix followed by a ':' character and library-specific + * group name to avoid namespace collisions, e.g. "com.example:myFeature". + * + * @param mimeGroup MIME group to modify. + * @param mimeTypes new MIME types contained by MIME group. + * @throws IllegalArgumentException if the MIME group was not declared in the manifest. */ public void setMimeGroup(@NonNull String mimeGroup, @NonNull Set<String> mimeTypes) { throw new UnsupportedOperationException( @@ -7905,22 +7909,16 @@ public abstract class PackageManager { } /** - * Clears MIME group by removing all MIME types from it + * Gets all MIME types contained by MIME group. * - * @param mimeGroup MIME group to clear - */ - public void clearMimeGroup(@NonNull String mimeGroup) { - throw new UnsupportedOperationException( - "clearMimeGroup not implemented in subclass"); - } - - /** - * Gets all MIME types that MIME group contains + * Libraries should use a reverse-DNS prefix followed by a ':' character and library-specific + * group name to avoid namespace collisions, e.g. "com.example:myFeature". * - * @return MIME types contained by the MIME group, - * or null if the MIME group was not declared in the manifest. + * @param mimeGroup MIME group to retrieve. + * @return MIME types contained by the MIME group. + * @throws IllegalArgumentException if the MIME group was not declared in the manifest. */ - @Nullable + @NonNull public Set<String> getMimeGroup(@NonNull String mimeGroup) { throw new UnsupportedOperationException( "getMimeGroup not implemented in subclass"); diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl index 1bcc76bfca44..bed4302bcd4d 100644 --- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl @@ -16,6 +16,7 @@ package android.service.autofill; +import android.content.IntentSender; import android.os.IBinder; import android.view.SurfaceControlViewHost; @@ -30,4 +31,5 @@ oneway interface IInlineSuggestionUiCallback { void onContent(in SurfaceControlViewHost.SurfacePackage surface); void onError(); void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId); + void onStartIntentSender(in IntentSender intentSender); } diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index 7fbc309d308c..0d4be58f210f 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -24,6 +24,7 @@ import android.annotation.TestApi; import android.app.Service; import android.app.slice.Slice; import android.content.Intent; +import android.content.IntentSender; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.Handler; @@ -60,6 +61,8 @@ public abstract class InlineSuggestionRenderService extends Service { private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); + private IInlineSuggestionUiCallback mCallback; + private void handleRenderSuggestion(IInlineSuggestionUiCallback callback, InlinePresentation presentation, int width, int height, IBinder hostInputToken, int displayId) { @@ -84,6 +87,7 @@ public abstract class InlineSuggestionRenderService extends Service { } return; } + mCallback = callback; final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback); suggestionRoot.addView(suggestionView); @@ -155,6 +159,20 @@ public abstract class InlineSuggestionRenderService extends Service { } /** + * Starts the {@link IntentSender} from the client app. + * + * @param intentSender the {@link IntentSender} to start the attribution UI from the client app. + */ + public final void startIntentSender(@NonNull IntentSender intentSender) { + if (mCallback == null) return; + try { + mCallback.onStartIntentSender(intentSender); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** * Returns the metadata about the renderer. Returns {@code null} if no metadata is provided. */ @Nullable diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java index fe797eb02602..3b4d84a1c668 100644 --- a/core/java/android/service/storage/ExternalStorageService.java +++ b/core/java/android/service/storage/ExternalStorageService.java @@ -25,11 +25,13 @@ import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.storage.StorageVolume; + +import com.android.internal.os.BackgroundThread; import java.io.File; import java.io.IOException; @@ -97,7 +99,7 @@ public abstract class ExternalStorageService extends Service { public @interface SessionFlag {} private final ExternalStorageServiceWrapper mWrapper = new ExternalStorageServiceWrapper(); - private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); + private final Handler mHandler = BackgroundThread.getHandler(); /** * Called when the system starts a session associated with {@code deviceFd} @@ -131,6 +133,20 @@ public abstract class ExternalStorageService extends Service { */ public abstract void onEndSession(@NonNull String sessionId) throws IOException; + /** + * Called when any volume's state changes. + * + * <p> This is required to communicate volume state changes with the Storage Service before + * broadcasting to other apps. The Storage Service needs to process any change in the volume + * state (before other apps receive a broadcast for the same) to update the database so that + * other apps have the correct view of the volume. + * + * <p> Blocks until the Storage Service processes/scans the volume or fails in doing so. + * + * @param vol name of the volume that was changed + */ + public abstract void onVolumeStateChanged(@NonNull StorageVolume vol) throws IOException; + @Override @NonNull public final IBinder onBind(@NonNull Intent intent) { @@ -154,6 +170,19 @@ public abstract class ExternalStorageService extends Service { } @Override + public void notifyVolumeStateChanged(String sessionId, StorageVolume vol, + RemoteCallback callback) { + mHandler.post(() -> { + try { + onVolumeStateChanged(vol); + sendResult(sessionId, null /* throwable */, callback); + } catch (Throwable t) { + sendResult(sessionId, t, callback); + } + }); + } + + @Override public void endSession(String sessionId, RemoteCallback callback) throws RemoteException { mHandler.post(() -> { try { diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl index ae46f1fc2fd2..30fefd33016d 100644 --- a/core/java/android/service/storage/IExternalStorageService.aidl +++ b/core/java/android/service/storage/IExternalStorageService.aidl @@ -18,6 +18,7 @@ package android.service.storage; import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; +import android.os.storage.StorageVolume; /** * @hide @@ -27,4 +28,6 @@ oneway interface IExternalStorageService void startSession(@utf8InCpp String sessionId, int type, in ParcelFileDescriptor deviceFd, @utf8InCpp String upperPath, @utf8InCpp String lowerPath, in RemoteCallback callback); void endSession(@utf8InCpp String sessionId, in RemoteCallback callback); + void notifyVolumeStateChanged(@utf8InCpp String sessionId, in StorageVolume vol, + in RemoteCallback callback); }
\ No newline at end of file diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 719f649cb5a2..294faaf0b5c8 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -18,6 +18,7 @@ package android.view; import static android.view.InsetsState.ITYPE_IME; +import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; @@ -114,7 +115,7 @@ public class InsetsSource implements Parcelable { if (!ignoreVisibility && !mVisible) { return Insets.NONE; } - if (!mTmpFrame.setIntersect(frame, relativeFrame)) { + if (!getIntersection(frame, relativeFrame, mTmpFrame)) { return Insets.NONE; } @@ -144,12 +145,33 @@ public class InsetsSource implements Parcelable { } } + /** + * Outputs the intersection of two rectangles. The shared edges will also be counted in the + * intersection. + * + * @param a The first rectangle being intersected with. + * @param b The second rectangle being intersected with. + * @param out The rectangle which represents the intersection. + * @return {@code true} if there is any intersection. + */ + private static boolean getIntersection(@NonNull Rect a, @NonNull Rect b, @NonNull Rect out) { + if (a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom) { + out.left = Math.max(a.left, b.left); + out.top = Math.max(a.top, b.top); + out.right = Math.min(a.right, b.right); + out.bottom = Math.min(a.bottom, b.bottom); + return true; + } + out.setEmpty(); + return false; + } + public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType)); pw.print(" frame="); pw.print(mFrame.toShortString()); if (mVisibleFrame != null) { - pw.print(" visibleFrmae="); pw.print(mVisibleFrame.toShortString()); + pw.print(" visibleFrame="); pw.print(mVisibleFrame.toShortString()); } pw.print(" visible="); pw.print(mVisible); pw.println(); diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 67e592796181..4028fda6c1df 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -16,6 +16,9 @@ package com.android.internal.widget; +import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL; +import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE; + import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; @@ -35,6 +38,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; @@ -46,6 +50,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.TextView; @@ -109,13 +114,13 @@ public class ConversationLayout extends FrameLayout private View mExpandButtonContainer; private ViewGroup mExpandButtonAndContentContainer; private NotificationExpandButton mExpandButton; + private MessagingLinearLayout mImageMessageContainer; private int mExpandButtonExpandedTopMargin; private int mBadgedSideMargins; private int mIconSizeBadged; private int mIconSizeCentered; private CachingIconView mIcon; private int mExpandedGroupTopMargin; - private int mExpandButtonExpandedSize; private View mConversationFacePile; private int mNotificationBackgroundColor; private CharSequence mFallbackChatName; @@ -126,6 +131,7 @@ public class ConversationLayout extends FrameLayout private View mContentContainer; private boolean mExpandable = true; private int mContentMarginEnd; + private Rect mMessagingClipRect; public ConversationLayout(@NonNull Context context) { super(context); @@ -150,12 +156,13 @@ public class ConversationLayout extends FrameLayout super.onFinishInflate(); mMessagingLinearLayout = findViewById(R.id.notification_messaging); mMessagingLinearLayout.setMessagingLayout(this); + mImageMessageContainer = findViewById(R.id.conversation_image_message_container); // We still want to clip, but only on the top, since views can temporarily out of bounds // during transitions. DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); - Rect rect = new Rect(0, 0, size, size); - mMessagingLinearLayout.setClipBounds(rect); + mMessagingClipRect = new Rect(0, 0, size, size); + setMessagingClippingDisabled(false); mTitleView = findViewById(R.id.title); mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); mTextPaint.setTextAlign(Paint.Align.CENTER); @@ -176,8 +183,6 @@ public class ConversationLayout extends FrameLayout mExpandButton = findViewById(R.id.expand_button); mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize( R.dimen.conversation_expand_button_top_margin_expanded); - mExpandButtonExpandedSize = getResources().getDimensionPixelSize( - R.dimen.conversation_expand_button_expanded_size); mNotificationHeaderExpandedPadding = getResources().getDimensionPixelSize( R.dimen.conversation_header_expanded_padding_end); mContentMarginEnd = getResources().getDimensionPixelSize( @@ -370,6 +375,41 @@ public class ConversationLayout extends FrameLayout messagingGroup.setCanHideSenderIfFirst(canHide); } updateIconPositionAndSize(); + updateImageMessages(); + } + + private void updateImageMessages() { + boolean displayExternalImage = false; + ArraySet<View> newMessages = new ArraySet<>(); + if (mIsCollapsed) { + + // When collapsed, we're displaying all image messages in a dedicated container + // on the right of the layout instead of inline. Let's add all isolated images there + int imageIndex = 0; + for (int i = 0; i < mGroups.size(); i++) { + MessagingGroup messagingGroup = mGroups.get(i); + MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage(); + if (isolatedMessage != null) { + newMessages.add(isolatedMessage.getView()); + displayExternalImage = true; + if (imageIndex + != mImageMessageContainer.indexOfChild(isolatedMessage.getView())) { + mImageMessageContainer.removeView(isolatedMessage.getView()); + mImageMessageContainer.addView(isolatedMessage.getView(), imageIndex); + } + imageIndex++; + } + } + } + // Remove all messages that don't belong into the image layout + for (int i = 0; i < mImageMessageContainer.getChildCount(); i++) { + View child = mImageMessageContainer.getChildAt(i); + if (!newMessages.contains(child)) { + mImageMessageContainer.removeView(child); + i--; + } + } + mImageMessageContainer.setVisibility(displayExternalImage ? VISIBLE : GONE); } private void bindFacePile() { @@ -662,7 +702,9 @@ public class ConversationLayout extends FrameLayout newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); mAddedGroups.add(newGroup); } - newGroup.setDisplayImagesAtEnd(mIsCollapsed); + newGroup.setImageDisplayLocation(mIsCollapsed + ? IMAGE_DISPLAY_LOCATION_EXTERNAL + : IMAGE_DISPLAY_LOCATION_INLINE); newGroup.setIsInConversation(true); newGroup.setLayoutColor(mLayoutColor); newGroup.setTextColors(mSenderTextColor, mMessageTextColor); @@ -817,6 +859,10 @@ public class ConversationLayout extends FrameLayout return mMessagingLinearLayout; } + public @NonNull ViewGroup getImageMessageContainer() { + return mImageMessageContainer; + } + public ArrayList<MessagingGroup> getMessagingGroups() { return mGroups; } @@ -827,20 +873,17 @@ public class ConversationLayout extends FrameLayout int gravity; int topMargin = 0; ViewGroup newContainer; - int newContainerHeight; if (mIsCollapsed) { drawableId = R.drawable.ic_expand_notification; contentDescriptionId = R.string.expand_button_content_description_collapsed; gravity = Gravity.CENTER; newContainer = mExpandButtonAndContentContainer; - newContainerHeight = LayoutParams.MATCH_PARENT; } else { drawableId = R.drawable.ic_collapse_notification; contentDescriptionId = R.string.expand_button_content_description_expanded; gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; topMargin = mExpandButtonExpandedTopMargin; newContainer = this; - newContainerHeight = mExpandButtonExpandedSize; } mExpandButton.setImageDrawable(getContext().getDrawable(drawableId)); mExpandButton.setColorFilter(mExpandButton.getOriginalNotificationColor()); @@ -850,14 +893,11 @@ public class ConversationLayout extends FrameLayout if (newContainer != mExpandButtonContainer.getParent()) { ((ViewGroup) mExpandButtonContainer.getParent()).removeView(mExpandButtonContainer); newContainer.addView(mExpandButtonContainer); - MarginLayoutParams layoutParams = - (MarginLayoutParams) mExpandButtonContainer.getLayoutParams(); - layoutParams.height = newContainerHeight; - mExpandButtonContainer.setLayoutParams(layoutParams); } // update if the expand button is centered - FrameLayout.LayoutParams layoutParams = (LayoutParams) mExpandButton.getLayoutParams(); + LinearLayout.LayoutParams layoutParams = + (LinearLayout.LayoutParams) mExpandButton.getLayoutParams(); layoutParams.gravity = gravity; layoutParams.topMargin = topMargin; mExpandButton.setLayoutParams(layoutParams); @@ -905,4 +945,9 @@ public class ConversationLayout extends FrameLayout } updateContentPaddings(); } + + @Override + public void setMessagingClippingDisabled(boolean clippingDisabled) { + mMessagingLinearLayout.setClipBounds(clippingDisabled ? null : mMessagingClipRect); + } } diff --git a/core/java/com/android/internal/widget/IMessagingLayout.java b/core/java/com/android/internal/widget/IMessagingLayout.java index 149d05641a0b..b72c081f134c 100644 --- a/core/java/com/android/internal/widget/IMessagingLayout.java +++ b/core/java/com/android/internal/widget/IMessagingLayout.java @@ -39,4 +39,9 @@ public interface IMessagingLayout { * @return the list of messaging groups */ ArrayList<MessagingGroup> getMessagingGroups(); + + /** + * Disable the clipping of the messaging container. + */ + void setMessagingClippingDisabled(boolean clippingDisabled); } diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index c68da97ab3b4..53272f7eebf9 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -17,6 +17,7 @@ package com.android.internal.widget; import android.annotation.AttrRes; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StyleRes; @@ -44,6 +45,8 @@ import android.widget.RemoteViews; import com.android.internal.R; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -54,6 +57,23 @@ import java.util.List; public class MessagingGroup extends LinearLayout implements MessagingLinearLayout.MessagingChild { private static Pools.SimplePool<MessagingGroup> sInstancePool = new Pools.SynchronizedPool<>(10); + + /** + * Images are displayed inline. + */ + public static final int IMAGE_DISPLAY_LOCATION_INLINE = 0; + + /** + * Images are displayed at the end of the group. + */ + public static final int IMAGE_DISPLAY_LOCATION_AT_END = 1; + + /** + * Images are displayed externally. + */ + public static final int IMAGE_DISPLAY_LOCATION_EXTERNAL = 2; + + private MessagingLinearLayout mMessageContainer; ImageFloatingTextView mSenderView; private ImageView mAvatarView; @@ -70,7 +90,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mIsHidingAnimated; private boolean mNeedsGeneratedAvatar; private Person mSender; - private boolean mImagesAtEnd; + private @ImageDisplayLocation int mImageDisplayLocation; private ViewGroup mImageContainer; private MessagingImageMessage mIsolatedMessage; private boolean mClippingDisabled; @@ -476,7 +496,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mAddedMessages.add(message); } boolean isImage = message instanceof MessagingImageMessage; - if (mImagesAtEnd && isImage) { + if (mImageDisplayLocation != IMAGE_DISPLAY_LOCATION_INLINE && isImage) { isolatedMessage = (MessagingImageMessage) message; } else { if (removeFromParentIfDifferent(message, mMessageContainer)) { @@ -500,9 +520,12 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } if (isolatedMessage != null) { - if (removeFromParentIfDifferent(isolatedMessage, mImageContainer)) { + if (mImageDisplayLocation == IMAGE_DISPLAY_LOCATION_AT_END + && removeFromParentIfDifferent(isolatedMessage, mImageContainer)) { mImageContainer.removeAllViews(); mImageContainer.addView(isolatedMessage.getView()); + } else if (mImageDisplayLocation == IMAGE_DISPLAY_LOCATION_EXTERNAL) { + mImageContainer.removeAllViews(); } isolatedMessage.setIsolated(true); } else if (mIsolatedMessage != null) { @@ -515,7 +538,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } private void updateImageContainerVisibility() { - mImageContainer.setVisibility(mIsolatedMessage != null && mImagesAtEnd + mImageContainer.setVisibility(mIsolatedMessage != null + && mImageDisplayLocation == IMAGE_DISPLAY_LOCATION_AT_END ? View.VISIBLE : View.GONE); } @@ -620,9 +644,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mClippingDisabled = disabled; } - public void setDisplayImagesAtEnd(boolean atEnd) { - if (mImagesAtEnd != atEnd) { - mImagesAtEnd = atEnd; + public void setImageDisplayLocation(@ImageDisplayLocation int displayLocation) { + if (mImageDisplayLocation != displayLocation) { + mImageDisplayLocation = displayLocation; updateImageContainerVisibility(); } } @@ -670,4 +694,13 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mMessagingIconContainer.setLayoutParams(layoutParams); } } + + @IntDef(prefix = {"IMAGE_DISPLAY_LOCATION_"}, value = { + IMAGE_DISPLAY_LOCATION_INLINE, + IMAGE_DISPLAY_LOCATION_AT_END, + IMAGE_DISPLAY_LOCATION_EXTERNAL + }) + @Retention(RetentionPolicy.SOURCE) + private @interface ImageDisplayLocation { + } } diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java index c243f3b583e5..27689d4d43f9 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -149,10 +149,16 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage protected void onDraw(Canvas canvas) { canvas.save(); canvas.clipPath(getRoundedRectPath()); - int width = (int) Math.max(getActualWidth(), getActualHeight() * mAspectRatio); - int height = (int) (width / mAspectRatio); + // Calculate the right sizing ensuring that the image is nicely centered in the layout + // during transitions + int width = (int) Math.max((Math.min(getHeight(), getActualHeight()) * mAspectRatio), + getActualWidth()); + int height = (int) Math.max((Math.min(getWidth(), getActualWidth()) / mAspectRatio), + getActualHeight()); + height = (int) Math.max(height, width / mAspectRatio); int left = (int) ((getActualWidth() - width) / 2.0f); - mDrawable.setBounds(left, 0, left + width, height); + int top = (int) ((getActualHeight() - height) / 2.0f); + mDrawable.setBounds(left, top, left + width, top + height); mDrawable.draw(canvas); canvas.restore(); } @@ -222,8 +228,17 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mIsIsolated) { + // When isolated we have a fixed size, let's use that sizing. setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); + } else { + // If we are displaying inline, we never want to go wider than actual size of the + // image, otherwise it will look quite blurry. + int width = Math.min(MeasureSpec.getSize(widthMeasureSpec), + mDrawable.getIntrinsicWidth()); + int height = (int) Math.min(MeasureSpec.getSize(heightMeasureSpec), width + / mAspectRatio); + setMeasuredDimension(width, height); } } @@ -231,7 +246,7 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // TODO: ensure that this isn't called when transforming - setActualWidth(getStaticWidth()); + setActualWidth(getWidth()); setActualHeight(getHeight()); } @@ -258,13 +273,6 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage return mActualHeight; } - public int getStaticWidth() { - if (mIsIsolated) { - return getWidth(); - } - return (int) (getHeight() * mAspectRatio); - } - public void setIsolated(boolean isolated) { if (mIsIsolated != isolated) { mIsIsolated = isolated; diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 3fb5d43bea5a..a162e4e10c71 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -16,6 +16,9 @@ package com.android.internal.widget; +import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_AT_END; +import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE; + import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; @@ -447,7 +450,9 @@ public class MessagingLayout extends FrameLayout newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); mAddedGroups.add(newGroup); } - newGroup.setDisplayImagesAtEnd(mDisplayImagesAtEnd); + newGroup.setImageDisplayLocation(mDisplayImagesAtEnd + ? IMAGE_DISPLAY_LOCATION_AT_END + : IMAGE_DISPLAY_LOCATION_INLINE); newGroup.setIsInConversation(false); newGroup.setLayoutColor(mLayoutColor); newGroup.setTextColors(mSenderTextColor, mMessageTextColor); @@ -599,4 +604,9 @@ public class MessagingLayout extends FrameLayout public ArrayList<MessagingGroup> getMessagingGroups() { return mGroups; } + + @Override + public void setMessagingClippingDisabled(boolean clippingDisabled) { + // Don't do anything, this is only used for the ConversationLayout + } } diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index 2348deeed7fb..82465831aa69 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -31,6 +31,7 @@ android:clipChildren="false" android:clipToPadding="false" android:paddingTop="12dp" + android:paddingBottom="12dp" > <FrameLayout @@ -186,13 +187,29 @@ </com.android.internal.widget.RemeasuringLinearLayout> <!--This is dynamically placed between here and at the end of the layout--> - <FrameLayout + <LinearLayout android:id="@+id/expand_button_container" android:layout_width="wrap_content" - android:layout_height="@dimen/conversation_expand_button_expanded_size" + android:layout_height="@dimen/conversation_expand_button_size" android:layout_gravity="end|top" android:paddingStart="16dp" - android:paddingEnd="@dimen/notification_content_margin_end"> + android:orientation="horizontal" + android:paddingEnd="@dimen/notification_content_margin_end" + android:clipToPadding="false" + android:clipChildren="false" + > + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:forceHasOverlappingRendering="false" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_marginEnd="11dp" + android:spacing="0dp" + android:layout_gravity="center" + android:clipToPadding="false" + android:clipChildren="false" + /> <com.android.internal.widget.NotificationExpandButton android:id="@+id/expand_button" android:layout_width="@dimen/notification_header_expand_icon_size" @@ -202,5 +219,5 @@ android:clickable="false" android:importantForAccessibility="no" /> - </FrameLayout> + </LinearLayout> </com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 4dedc63f2e48..6fdc2236208f 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -686,8 +686,8 @@ <dimen name="conversation_avatar_size">52dp</dimen> <!-- Start of the content in the conversation template --> <dimen name="conversation_content_start">80dp</dimen> - <!-- Size of the expand button when expanded --> - <dimen name="conversation_expand_button_expanded_size">80dp</dimen> + <!-- Size of the expand button in the conversation layout --> + <dimen name="conversation_expand_button_size">80dp</dimen> <!-- Top margin of the expand button for conversations when expanded --> <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 20d943d787e1..46fbe225f70b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3870,8 +3870,8 @@ <java-symbol type="id" name="conversation_face_pile" /> <java-symbol type="id" name="conversation_text" /> <java-symbol type="id" name="message_icon_container" /> + <java-symbol type="id" name="conversation_image_message_container" /> <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" /> - <java-symbol type="dimen" name="conversation_expand_button_expanded_size" /> <java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" /> <java-symbol type="dimen" name="conversation_badge_side_margin" /> <java-symbol type="dimen" name="conversation_icon_size_badged" /> diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java index a027f9eb3a4e..79b803a0cda6 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java +++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java @@ -15,19 +15,24 @@ */ package com.android.coretests.apps.bstatstestapp; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + import android.R; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.graphics.Color; +import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.util.Size; +import android.view.Display; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -46,11 +51,17 @@ public class TestService extends Service { private static final int TIMEOUT_OVERLAY_SEC = 2; + private Context mOverlayContext; private View mOverlay; @Override public void onCreate() { Log.d(TAG, "onCreate called. myUid=" + Process.myUid()); + + final DisplayManager dm = getSystemService(DisplayManager.class); + final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY); + final Context defaultDisplayContext = createDisplayContext(defaultDisplay); + mOverlayContext = defaultDisplayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null); } @Override @@ -93,7 +104,7 @@ public class TestService extends Service { private void removeOverlays() { if (mOverlay != null) { - final WindowManager wm = TestService.this.getSystemService(WindowManager.class); + final WindowManager wm = mOverlayContext.getSystemService(WindowManager.class); wm.removeView(mOverlay); mOverlay = null; } @@ -107,11 +118,11 @@ public class TestService extends Service { @Override public void showApplicationOverlay() throws RemoteException { - final WindowManager wm = TestService.this.getSystemService(WindowManager.class); + final WindowManager wm = mOverlayContext.getSystemService(WindowManager.class); final Size size = wm.getCurrentWindowMetrics().getSize(); final WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams( - WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, + TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); @@ -124,7 +135,7 @@ public class TestService extends Service { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - mOverlay = new View(TestService.this); + mOverlay = new View(mOverlayContext); mOverlay.setBackgroundColor(Color.GREEN); mOverlay.setLayoutParams(vglp); diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index 756d63d2857d..b3f6fe9e88b9 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -127,6 +127,48 @@ public class InsetsSourceTest { } @Test + public void testCalculateInsets_noIntersection_vertical() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 500), false); + assertEquals(Insets.NONE, insets); + } + + @Test + public void testCalculateInsets_zeroWidthIntersection_vertical_start() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 0, 500), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test + public void testCalculateInsets_zeroWidthIntersection_vertical_end() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(500, 0, 500, 500), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test + public void testCalculateInsets_noIntersection_horizontal() { + mSource.setFrame(new Rect(0, 0, 100, 500)); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), false); + assertEquals(Insets.NONE, insets); + } + + @Test + public void testCalculateInsets_zeroWidthIntersection_horizontal_start() { + mSource.setFrame(new Rect(0, 0, 100, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 0), false); + assertEquals(Insets.of(100, 0, 0, 0), insets); + } + + @Test + public void testCalculateInsets_zeroWidthIntersection_horizontal_end() { + mSource.setFrame(new Rect(0, 0, 100, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 500, 500, 500), false); + assertEquals(Insets.of(100, 0, 0, 0), insets); + } + + @Test public void testCalculateVisibleInsets_override() { mSource.setFrame(new Rect(0, 0, 500, 100)); mSource.setVisibleFrame(new Rect(0, 0, 500, 200)); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index a8f71851ee02..cbf2364b50a4 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -316,7 +316,7 @@ public final class MediaFormat { * A key describing the hardware AV sync id. * The associated value is an integer * - * @see android.media.tv.tuner.Tuner#getAvSyncHwId + * See android.media.tv.tuner.Tuner#getAvSyncHwId. */ public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id"; diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 79e4d8ae6e26..c61221892c28 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -340,8 +340,10 @@ void StreamManager::run(int32_t id) int64_t waitTimeNs = kWaitTimeBeforeCloseNs; std::unique_lock lock(mStreamManagerLock); while (!mQuit) { - mStreamManagerCondition.wait_for( - lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs)); + if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty. + mStreamManagerCondition.wait_for( + lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs)); + } ALOGV("%s(%d) awake", __func__, id); sanityCheckQueue_l(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 2d351c74bf54..d93c0150410d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -1313,7 +1313,8 @@ final class SettingsState { } // isValuePreservedInRestore shouldn't change back to false if it has been set to true. - boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault); + boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault, + packageName, value); // Is something gonna change? if (Objects.equals(value, this.value) @@ -1339,11 +1340,16 @@ final class SettingsState { } private boolean shouldPreserveSetting(boolean overrideableByRestore, - boolean resetToDefault) { + boolean resetToDefault, String packageName, String value) { if (resetToDefault) { // By default settings are not marked as preserved. return false; } + if (value != null && value.equals(this.value) + && SYSTEM_PACKAGE_NAME.equals(packageName)) { + // Do not mark preserved if it's the system reinitializing to the same value. + return false; + } // isValuePreservedInRestore shouldn't change back to false if it has been set to true. return this.isValuePreservedInRestore || !overrideableByRestore; diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 6a3c6619c0ef..9f448af7f344 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -47,6 +47,7 @@ public class SettingsStateTest extends AndroidTestCase { "日本語"; private static final String TEST_PACKAGE = "package"; + private static final String SYSTEM_PACKAGE = "android"; private static final String SETTING_NAME = "test_setting"; private final Object mLock = new Object(); @@ -253,6 +254,26 @@ public class SettingsStateTest extends AndroidTestCase { } + public void testModifySettingBySystemPackage_sameValue_preserveFlagNotSet() { + SettingsState settingsState = getSettingStateObject(); + // Initialize the setting. + settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE); + // Update the setting. + settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE); + + assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); + } + + public void testModifySettingBySystemPackage_newValue_preserveFlagSet() { + SettingsState settingsState = getSettingStateObject(); + // Initialize the setting. + settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE); + // Update the setting. + settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, SYSTEM_PACKAGE); + + assertTrue(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); + } + private SettingsState getSettingStateObject() { SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 6a7f9e2620db..87cb5c7f746c 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -53,8 +53,7 @@ android:layout_centerVertical="true" android:gravity="center_vertical" android:layout_alignEnd="@id/conversation_icon" - android:layout_toEndOf="@id/conversation_icon" - android:layout_alignStart="@id/mute"> + android:layout_toEndOf="@id/conversation_icon"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -127,96 +126,230 @@ <!-- end aligned fields --> <ImageButton - android:id="@+id/mute" - android:layout_width="@dimen/notification_importance_toggle_size" - android:layout_height="@dimen/notification_importance_toggle_size" - android:layout_centerVertical="true" - android:background="@drawable/ripple_drawable" - android:layout_toStartOf="@id/fave" - android:tint="@color/notification_guts_link_icon_tint"/> - <ImageButton - android:id="@+id/fave" + android:id="@+id/info" android:layout_width="@dimen/notification_importance_toggle_size" android:layout_height="@dimen/notification_importance_toggle_size" android:layout_centerVertical="true" android:background="@drawable/ripple_drawable" + android:contentDescription="@string/notification_more_settings" + android:src="@drawable/ic_settings" android:layout_alignParentEnd="true" android:tint="@color/notification_guts_link_icon_tint"/> </LinearLayout> <LinearLayout - android:id="@+id/actions" + android:id="@+id/inline_controls" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginEnd="@*android:dimen/notification_content_margin_end" + android:paddingEnd="@*android:dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_guts_option_vertical_padding" android:clipChildren="false" android:clipToPadding="false" android:orientation="vertical"> - <View + <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings--> + <TextView + android:id="@+id/non_configurable_text" + android:text="@string/notification_unblockable_desc" + android:visibility="gone" android:layout_width="match_parent" - android:layout_height="0.5dp" - android:background="@color/GM2_grey_300" /> + android:layout_height="wrap_content" + style="@*android:style/TextAppearance.DeviceDefault.Notification" /> - <Button - android:id="@+id/snooze" - android:layout_height="@dimen/notification_guts_conversation_action_height" - android:layout_width="match_parent" - style="?android:attr/borderlessButtonStyle" - android:text="@string/notification_menu_snooze_action" - android:gravity="left|center_vertical" - android:drawableStart="@drawable/ic_snooze" - android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start" - android:drawableTint="@color/notification_guts_link_icon_tint"/> - - <View + <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings--> + <TextView + android:id="@+id/non_configurable_multichannel_text" + android:text="@string/notification_multichannel_desc" + android:visibility="gone" android:layout_width="match_parent" - android:layout_height="0.5dp" - android:background="@color/GM2_grey_300" /> + android:layout_height="wrap_content" + style="@*android:style/TextAppearance.DeviceDefault.Notification" /> - <Button - android:id="@+id/bubble" - android:layout_height="@dimen/notification_guts_conversation_action_height" - android:layout_width="match_parent" - style="?android:attr/borderlessButtonStyle" - android:text="@string/notification_conversation_favorite" - android:gravity="left|center_vertical" - android:drawableStart="@drawable/ic_create_bubble" - android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start" - android:drawableTint="@color/notification_guts_link_icon_tint"/> - - <View - android:layout_width="match_parent" - android:layout_height="0.5dp" - android:background="@color/GM2_grey_300" /> - <Button - android:id="@+id/home" - android:layout_height="@dimen/notification_guts_conversation_action_height" - android:layout_width="match_parent" - style="?android:attr/borderlessButtonStyle" - android:text="@string/notification_conversation_home_screen" - android:gravity="left|center_vertical" - android:drawableStart="@drawable/ic_add_to_home" - android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start" - android:drawableTint="@color/notification_guts_link_icon_tint"/> - - <View + <LinearLayout + android:id="@+id/interruptiveness_settings" android:layout_width="match_parent" - android:layout_height="0.5dp" - android:background="@color/GM2_grey_300" /> + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical"> - <Button - android:id="@+id/info" - android:layout_height="@dimen/notification_guts_conversation_action_height" - android:layout_width="match_parent" - style="?android:attr/borderlessButtonStyle" - android:drawableStart="@drawable/ic_settings" - android:text="@string/notification_menu_settings_action" - android:gravity="left|center_vertical" - android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start" - android:drawableTint="@color/notification_guts_link_icon_tint"/> + <com.android.systemui.statusbar.notification.row.ButtonLinearLayout + android:id="@+id/priority" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="@dimen/notification_importance_button_padding" + android:clickable="true" + android:focusable="true" + android:background="@drawable/notification_guts_priority_button_bg" + android:orientation="vertical"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + > + <ImageView + android:id="@+id/priority_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_important_outline" + android:background="@android:color/transparent" + android:tint="@color/notification_guts_priority_contents" + android:clickable="false" + android:focusable="false"/> + <TextView + android:id="@+id/priority_label" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_importance_drawable_padding" + android:layout_weight="1" + android:ellipsize="end" + android:maxLines="1" + android:clickable="false" + android:focusable="false" + android:textAppearance="@style/TextAppearance.NotificationImportanceButton" + android:text="@string/notification_priority_title"/> + </LinearLayout> + <TextView + android:id="@+id/priority_summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_importance_button_description_top_margin" + android:visibility="gone" + android:text="@string/notification_channel_summary_priority" + android:clickable="false" + android:focusable="false" + android:ellipsize="end" + android:maxLines="2" + android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> + </com.android.systemui.statusbar.notification.row.ButtonLinearLayout> + + <com.android.systemui.statusbar.notification.row.ButtonLinearLayout + android:id="@+id/default_behavior" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_importance_button_separation" + android:padding="@dimen/notification_importance_button_padding" + android:clickable="true" + android:focusable="true" + android:background="@drawable/notification_guts_priority_button_bg" + android:orientation="vertical"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + > + <ImageView + android:id="@+id/default_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_notifications_alert" + android:background="@android:color/transparent" + android:tint="@color/notification_guts_priority_contents" + android:clickable="false" + android:focusable="false"/> + <TextView + android:id="@+id/default_label" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_importance_drawable_padding" + android:layout_weight="1" + android:ellipsize="end" + android:maxLines="1" + android:clickable="false" + android:focusable="false" + android:textAppearance="@style/TextAppearance.NotificationImportanceButton" + android:text="@string/notification_alert_title"/> + </LinearLayout> + <TextView + android:id="@+id/default_summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_importance_button_description_top_margin" + android:visibility="gone" + android:text="@string/notification_channel_summary_default" + android:clickable="false" + android:focusable="false" + android:ellipsize="end" + android:maxLines="2" + android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> + </com.android.systemui.statusbar.notification.row.ButtonLinearLayout> + + <com.android.systemui.statusbar.notification.row.ButtonLinearLayout + android:id="@+id/silence" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_importance_button_separation" + android:padding="@dimen/notification_importance_button_padding" + android:clickable="true" + android:focusable="true" + android:background="@drawable/notification_guts_priority_button_bg" + android:orientation="vertical"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + > + <ImageView + android:id="@+id/silence_icon" + android:src="@drawable/ic_notifications_silence" + android:background="@android:color/transparent" + android:tint="@color/notification_guts_priority_contents" + android:layout_gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="false" + android:focusable="false"/> + <TextView + android:id="@+id/silence_label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:clickable="false" + android:focusable="false" + android:layout_toEndOf="@id/silence_icon" + android:layout_marginStart="@dimen/notification_importance_drawable_padding" + android:textAppearance="@style/TextAppearance.NotificationImportanceButton" + android:text="@string/notification_silence_title"/> + </LinearLayout> + <TextView + android:id="@+id/silence_summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_importance_button_description_top_margin" + android:visibility="gone" + android:text="@string/notification_channel_summary_low" + android:clickable="false" + android:focusable="false" + android:ellipsize="end" + android:maxLines="2" + android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> + </com.android.systemui.statusbar.notification.row.ButtonLinearLayout> </LinearLayout> + <RelativeLayout + android:id="@+id/bottom_buttons" + android:layout_width="match_parent" + android:layout_height="60dp" + android:gravity="center_vertical" + android:paddingStart="4dp" + android:paddingEnd="4dp" + > + <TextView + android:id="@+id/done" + android:text="@string/inline_ok_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:gravity="end|center_vertical" + android:minWidth="@dimen/notification_importance_toggle_size" + android:minHeight="@dimen/notification_importance_toggle_size" + style="@style/TextAppearance.NotificationInfo.Button"/> + </RelativeLayout> + </LinearLayout> + </com.android.systemui.statusbar.notification.row.NotificationConversationInfo> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 6ab573bbfba7..73b711d275f3 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -52,8 +52,7 @@ android:layout_centerVertical="true" android:gravity="center_vertical" android:layout_alignEnd="@id/pkg_icon" - android:layout_toEndOf="@id/pkg_icon" - android:layout_alignStart="@id/mute"> + android:layout_toEndOf="@id/pkg_icon"> <TextView android:id="@+id/channel_name" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ae38e34ce2c3..6905de2d9c4f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1819,6 +1819,18 @@ <!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary --> <string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string> + <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary --> + <string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble.</string> + + <!--[CHAR LIMIT=150] Conversation inline controls footer shown when all conversations from the app are allowed to show as bubbles --> + <string name="notification_conversation_channel_all_bubble">All conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default. Manage in <xliff:g id="app_name" example="Settings">%2$s</xliff:g>.</string> + + <!--[CHAR LIMIT=30] Linkable text to Settings app --> + <string name="notification_conversation_channel_settings">Settings</string> + + <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level --> + <string name="notification_priority_title">Priority</string> + <!-- [CHAR LIMIT=NONE] Empty overflow title --> <string name="bubble_overflow_empty_title">No recent bubbles</string> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java index a3fb2251f5aa..2ee21539ca1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java @@ -82,7 +82,7 @@ public class MessagingImageTransformState extends ImageTransformState { float startActualWidth = getStartActualWidth(); mImageMessage.setActualWidth( (int) NotificationUtils.interpolate(startActualWidth, - mImageMessage.getStaticWidth(), + mImageMessage.getWidth(), interpolatedValue)); float startActualHeight = getStartActualHeight(); mImageMessage.setActualHeight( @@ -121,7 +121,7 @@ public class MessagingImageTransformState extends ImageTransformState { @Override protected void resetTransformedView() { super.resetTransformedView(); - mImageMessage.setActualWidth(mImageMessage.getStaticWidth()); + mImageMessage.setActualWidth(mImageMessage.getWidth()); mImageMessage.setActualHeight(mImageMessage.getHeight()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java index 9383f537db45..ccf5a0bb5932 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java @@ -245,6 +245,7 @@ public class MessagingLayoutTransformState extends TransformState { if (isGone(child)) { continue; } + float messageAmount = transformationAmount; int otherIndex = otherMessages.size() - 1 - i; View otherChild = null; if (otherIndex >= 0) { @@ -257,18 +258,19 @@ public class MessagingLayoutTransformState extends TransformState { // Let's fade out as we approach the top of the screen. We can only do this if // we're actually moving up float distanceToTop = child.getTop() + child.getHeight() + previousTranslation; - transformationAmount = distanceToTop / child.getHeight(); - transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount)); + messageAmount = distanceToTop / child.getHeight(); + messageAmount = Math.max(0.0f, Math.min(1.0f, messageAmount)); if (to) { - transformationAmount = 1.0f - transformationAmount; + messageAmount = 1.0f - messageAmount; } } - transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */ + transformView(messageAmount, to, child, otherChild, false, /* sameAsAny */ useLinearTransformation); boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild; - if (transformationAmount == 0.0f + if (messageAmount == 0.0f && (otherIsIsolated || otherGroup.isSingleLine())) { ownGroup.setClippingDisabled(true); + mMessagingLayout.setMessagingClippingDisabled(true); } if (otherChild == null) { child.setTranslationY(previousTranslation); @@ -362,6 +364,9 @@ public class MessagingLayoutTransformState extends TransformState { if (view.getVisibility() == View.GONE) { return true; } + if (view.getParent() == null) { + return true; + } final ViewGroup.LayoutParams lp = view.getLayoutParams(); if (lp instanceof MessagingLinearLayout.LayoutParams && ((MessagingLinearLayout.LayoutParams) lp).hide) { @@ -433,6 +438,7 @@ public class MessagingLayoutTransformState extends TransformState { ownGroup.setClippingDisabled(false); ownGroup.updateClipRect(); } + mMessagingLayout.setMessagingClippingDisabled(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index 337f31295308..82fb49144181 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -100,7 +100,7 @@ public class TransformState { transformViewFullyFrom(otherState, transformationAmount); } - protected void ensureVisible() { + public void ensureVisible() { if (mTransformedView.getVisibility() == View.INVISIBLE || mTransformedView.getAlpha() != 1.0f) { // We have the same content, lets show ourselves 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 1088cdc30f37..8e2bfb84e2dd 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 @@ -20,11 +20,10 @@ import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS; +import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; @@ -36,7 +35,6 @@ import android.app.NotificationChannelGroup; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; @@ -49,6 +47,10 @@ import android.os.UserHandle; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.text.TextUtils; +import android.transition.ChangeBounds; +import android.transition.Fade; +import android.transition.TransitionManager; +import android.transition.TransitionSet; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; @@ -65,13 +67,10 @@ import com.android.settingslib.notification.ConversationIconFactory; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.bubbles.BubbleController; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.ShadeController; import java.lang.annotation.Retention; -import java.util.Arrays; import java.util.List; /** @@ -83,11 +82,9 @@ public class NotificationConversationInfo extends LinearLayout implements private INotificationManager mINotificationManager; - private LauncherApps mLauncherApps; ShortcutManager mShortcutManager; private PackageManager mPm; private VisualStabilityManager mVisualStabilityManager; - private ShadeController mShadeController; private ConversationIconFactory mIconFactory; private String mPackageName; @@ -97,44 +94,34 @@ public class NotificationConversationInfo extends LinearLayout implements private NotificationChannel mNotificationChannel; private ShortcutInfo mShortcutInfo; private String mConversationId; - private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; - private boolean mStartedAsBubble; - private boolean mIsBubbleable; + private TextView mPriorityDescriptionView; + private TextView mDefaultDescriptionView; + private TextView mSilentDescriptionView; private @Action int mSelectedAction = -1; private OnSnoozeClickListener mOnSnoozeClickListener; private OnSettingsClickListener mOnSettingsClickListener; private NotificationGuts mGutsContainer; - private BubbleController mBubbleController; @VisibleForTesting boolean mSkipPost = false; @Retention(SOURCE) - @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE, - ACTION_UNBUBBLE, ACTION_SETTINGS}) + @IntDef({ACTION_DEFAULT, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE, + ACTION_SETTINGS}) private @interface Action {} - static final int ACTION_BUBBLE = 0; + static final int ACTION_DEFAULT = 0; static final int ACTION_HOME = 1; static final int ACTION_FAVORITE = 2; static final int ACTION_SNOOZE = 3; static final int ACTION_MUTE = 4; static final int ACTION_SETTINGS = 5; - static final int ACTION_UNBUBBLE = 6; - - private OnClickListener mOnBubbleClick = v -> { - mSelectedAction = mStartedAsBubble ? ACTION_UNBUBBLE : ACTION_BUBBLE; - if (mStartedAsBubble) { - mBubbleController.onUserDemotedBubbleFromNotification(mEntry); - } else { - mBubbleController.onUserCreatedBubbleFromNotification(mEntry); - } - closeControls(v, true); - }; + // TODO: b/152050825 + /* private OnClickListener mOnHomeClick = v -> { mSelectedAction = ACTION_HOME; mShortcutManager.requestPinShortcut(mShortcutInfo, null); @@ -142,21 +129,30 @@ public class NotificationConversationInfo extends LinearLayout implements closeControls(v, true); }; - private OnClickListener mOnFavoriteClick = v -> { - mSelectedAction = ACTION_FAVORITE; - updateChannel(); - - }; - private OnClickListener mOnSnoozeClick = v -> { mSelectedAction = ACTION_SNOOZE; mOnSnoozeClickListener.onClick(v, 1); closeControls(v, true); }; + */ + + private OnClickListener mOnFavoriteClick = v -> { + mSelectedAction = ACTION_FAVORITE; + updateToggleActions(mSelectedAction, true); + }; + + private OnClickListener mOnDefaultClick = v -> { + mSelectedAction = ACTION_DEFAULT; + updateToggleActions(mSelectedAction, true); + }; private OnClickListener mOnMuteClick = v -> { mSelectedAction = ACTION_MUTE; - updateChannel(); + updateToggleActions(mSelectedAction, true); + }; + + private OnClickListener mOnDone = v -> { + closeControls(v, true); }; public NotificationConversationInfo(Context context, AttributeSet attrs) { @@ -177,7 +173,6 @@ public class NotificationConversationInfo extends LinearLayout implements public void bindNotification( ShortcutManager shortcutManager, - LauncherApps launcherApps, PackageManager pm, INotificationManager iNotificationManager, VisualStabilityManager visualStabilityManager, @@ -185,16 +180,13 @@ public class NotificationConversationInfo extends LinearLayout implements NotificationChannel notificationChannel, NotificationEntry entry, OnSettingsClickListener onSettingsClick, - OnAppSettingsClickListener onAppSettingsClick, OnSnoozeClickListener onSnoozeClickListener, ConversationIconFactory conversationIconFactory, boolean isDeviceProvisioned) { mSelectedAction = -1; mINotificationManager = iNotificationManager; mVisualStabilityManager = visualStabilityManager; - mBubbleController = Dependency.get(BubbleController.class); mPackageName = pkg; - mEntry = entry; mSbn = entry.getSbn(); mPm = pm; mAppName = mPackageName; @@ -204,11 +196,9 @@ public class NotificationConversationInfo extends LinearLayout implements mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; mOnSnoozeClickListener = onSnoozeClickListener; - mShadeController = Dependency.get(ShadeController.class); mIconFactory = conversationIconFactory; mShortcutManager = shortcutManager; - mLauncherApps = launcherApps; mConversationId = mNotificationChannel.getConversationId(); if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) { mConversationId = mSbn.getShortcutId(mContext); @@ -218,16 +208,13 @@ public class NotificationConversationInfo extends LinearLayout implements } mShortcutInfo = entry.getRanking().getShortcutInfo(); - mIsBubbleable = mEntry.getBubbleMetadata() != null - && Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.NOTIFICATION_BUBBLES, 0) == 1; - mStartedAsBubble = mEntry.isBubble(); - createConversationChannelIfNeeded(); bindHeader(); bindActions(); + View done = findViewById(R.id.done); + done.setOnClickListener(mOnDone); } void createConversationChannelIfNeeded() { @@ -252,37 +239,28 @@ public class NotificationConversationInfo extends LinearLayout implements } private void bindActions() { - // TODO: figure out what should happen for non-configurable channels - - Button bubble = findViewById(R.id.bubble); - bubble.setVisibility(mIsBubbleable ? VISIBLE : GONE); - bubble.setOnClickListener(mOnBubbleClick); - if (mStartedAsBubble) { - bubble.setText(R.string.notification_conversation_unbubble); - } else { - bubble.setText(R.string.notification_conversation_bubble); - } + // TODO: b/152050825 + /* Button home = findViewById(R.id.home); home.setOnClickListener(mOnHomeClick); home.setVisibility(mShortcutInfo != null && mShortcutManager.isRequestPinShortcutSupported() ? VISIBLE : GONE); - View favorite = findViewById(R.id.fave); - favorite.setOnClickListener(mOnFavoriteClick); - Button snooze = findViewById(R.id.snooze); snooze.setOnClickListener(mOnSnoozeClick); + */ - View mute = findViewById(R.id.mute); - mute.setOnClickListener(mOnMuteClick); + findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick); + findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick); + findViewById(R.id.silence).setOnClickListener(mOnMuteClick); final View settingsButton = findViewById(R.id.info); settingsButton.setOnClickListener(getSettingsOnClickListener()); settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE); - updateToggleActions(); + updateToggleActions(getSelectedAction(), false); } private void bindHeader() { @@ -310,16 +288,16 @@ public class NotificationConversationInfo extends LinearLayout implements // TODO: bring back when channel name does not include name // bindName(); bindPackage(); - bindIcon(); + bindIcon(mNotificationChannel.isImportantConversation()); } - private void bindIcon() { + private void bindIcon(boolean important) { ImageView image = findViewById(R.id.conversation_icon); if (mShortcutInfo != null) { image.setImageDrawable(mIconFactory.getConversationDrawable( mShortcutInfo, mPackageName, mAppUid, - mNotificationChannel.isImportantConversation())); + important)); } else { if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) { // TODO: maybe use a generic group icon, or a composite of recent senders @@ -378,11 +356,6 @@ public class NotificationConversationInfo extends LinearLayout implements ((TextView) findViewById(R.id.pkg_name)).setText(mAppName); } - private boolean bubbleImportantConversations() { - return Settings.Secure.getInt(mContext.getContentResolver(), - BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1; - } - private void bindDelegate() { TextView delegateView = findViewById(R.id.delegate_name); @@ -431,6 +404,15 @@ public class NotificationConversationInfo extends LinearLayout implements } @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mPriorityDescriptionView = findViewById(R.id.priority_summary); + mDefaultDescriptionView = findViewById(R.id.default_summary); + mSilentDescriptionView = findViewById(R.id.silence_summary); + } + + @Override public void onFinishedClosing() { // TODO: do we need to do anything here? } @@ -450,32 +432,84 @@ public class NotificationConversationInfo extends LinearLayout implements } } - private void updateToggleActions() { - ImageButton favorite = findViewById(R.id.fave); - if (mNotificationChannel.isImportantConversation()) { - favorite.setContentDescription( - mContext.getString(R.string.notification_conversation_favorite)); - favorite.setImageResource(R.drawable.ic_important); - } else { - favorite.setContentDescription( - mContext.getString(R.string.notification_conversation_unfavorite)); - favorite.setImageResource(R.drawable.ic_important_outline); + private void updateToggleActions(int selectedAction, boolean userTriggered) { + if (userTriggered) { + TransitionSet transition = new TransitionSet(); + transition.setOrdering(TransitionSet.ORDERING_TOGETHER); + transition.addTransition(new Fade(Fade.OUT)) + .addTransition(new ChangeBounds()) + .addTransition( + new Fade(Fade.IN) + .setStartDelay(150) + .setDuration(200) + .setInterpolator(FAST_OUT_SLOW_IN)); + transition.setDuration(350); + transition.setInterpolator(FAST_OUT_SLOW_IN); + TransitionManager.beginDelayedTransition(this, transition); } - ImageButton mute = findViewById(R.id.mute); - if (mNotificationChannel.getImportance() >= IMPORTANCE_DEFAULT - || mNotificationChannel.getImportance() == IMPORTANCE_UNSPECIFIED) { - mute.setContentDescription( - mContext.getString(R.string.notification_conversation_unmute)); - mute.setImageResource(R.drawable.ic_notifications_alert); - } else { - mute.setContentDescription( - mContext.getString(R.string.notification_conversation_mute)); - mute.setImageResource(R.drawable.ic_notifications_silence); + View priority = findViewById(R.id.priority); + View defaultBehavior = findViewById(R.id.default_behavior); + View silence = findViewById(R.id.silence); + + switch (selectedAction) { + case ACTION_FAVORITE: + mPriorityDescriptionView.setVisibility(VISIBLE); + mDefaultDescriptionView.setVisibility(GONE); + mSilentDescriptionView.setVisibility(GONE); + post(() -> { + priority.setSelected(true); + defaultBehavior.setSelected(false); + silence.setSelected(false); + }); + break; + + case ACTION_MUTE: + mSilentDescriptionView.setVisibility(VISIBLE); + mDefaultDescriptionView.setVisibility(GONE); + mPriorityDescriptionView.setVisibility(GONE); + post(() -> { + priority.setSelected(false); + defaultBehavior.setSelected(false); + silence.setSelected(true); + }); + break; + + case ACTION_DEFAULT: + mDefaultDescriptionView.setVisibility(VISIBLE); + mSilentDescriptionView.setVisibility(GONE); + mPriorityDescriptionView.setVisibility(GONE); + post(() -> { + priority.setSelected(false); + defaultBehavior.setSelected(true); + silence.setSelected(false); + }); + break; + + default: + throw new IllegalArgumentException("Unrecognized behavior: " + mSelectedAction); } + boolean isAChange = getSelectedAction() != selectedAction; + TextView done = findViewById(R.id.done); + done.setText(isAChange + ? R.string.inline_ok_button + : R.string.inline_done_button); + // update icon in case importance has changed - bindIcon(); + bindIcon(selectedAction == ACTION_FAVORITE); + } + + int getSelectedAction() { + if (mNotificationChannel.getImportance() <= IMPORTANCE_LOW + && mNotificationChannel.getImportance() > IMPORTANCE_UNSPECIFIED) { + return ACTION_MUTE; + } else { + if (mNotificationChannel.isImportantConversation()) { + return ACTION_FAVORITE; + } + } + return ACTION_DEFAULT; } private void updateChannel() { @@ -517,11 +551,7 @@ public class NotificationConversationInfo extends LinearLayout implements @Override public boolean shouldBeSaved() { - // Toggle actions are already saved by the time the guts are closed; save for any other - // taps - return mSelectedAction > -1 - && mSelectedAction != ACTION_FAVORITE - && mSelectedAction != ACTION_MUTE; + return mSelectedAction == ACTION_FAVORITE || mSelectedAction == ACTION_MUTE; } @Override @@ -568,45 +598,41 @@ public class NotificationConversationInfo extends LinearLayout implements @Override public void run() { try { - boolean channelSettingChanged = mAction != ACTION_HOME && mAction != ACTION_SNOOZE; switch (mAction) { - case ACTION_BUBBLE: - case ACTION_UNBUBBLE: - boolean canBubble = mAction == ACTION_BUBBLE; - if (mChannelToUpdate.canBubble() != canBubble) { - channelSettingChanged = true; - mChannelToUpdate.setAllowBubbles(canBubble); - } else { - channelSettingChanged = false; - } - break; case ACTION_FAVORITE: mChannelToUpdate.setImportantConversation( !mChannelToUpdate.isImportantConversation()); - if (mChannelToUpdate.isImportantConversation() - && bubbleImportantConversations()) { + if (mChannelToUpdate.isImportantConversation()) { mChannelToUpdate.setAllowBubbles(true); } + mChannelToUpdate.setImportance(Math.max( + mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT)); + break; + case ACTION_DEFAULT: + mChannelToUpdate.setImportance(Math.max( + mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT)); + if (mChannelToUpdate.isImportantConversation()) { + mChannelToUpdate.setImportantConversation(false); + mChannelToUpdate.setAllowBubbles(false); + } break; case ACTION_MUTE: if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED || mChannelToUpdate.getImportance() >= IMPORTANCE_DEFAULT) { mChannelToUpdate.setImportance(IMPORTANCE_LOW); - } else { - mChannelToUpdate.setImportance(Math.max( - mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT)); + } + if (mChannelToUpdate.isImportantConversation()) { + mChannelToUpdate.setImportantConversation(false); + mChannelToUpdate.setAllowBubbles(false); } break; } - if (channelSettingChanged) { - mINotificationManager.updateNotificationChannelForPackage( + mINotificationManager.updateNotificationChannelForPackage( mAppPkg, mAppUid, mChannelToUpdate); - } } catch (RemoteException e) { Log.e(TAG, "Unable to update notification channel", e); } - ThreadUtils.postOnMainThread(() -> updateToggleActions()); } } } 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 1d7d611e51db..2487d1a898a3 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 @@ -394,7 +394,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.bindNotification( mShortcutManager, - mLauncherApps, pmUser, mNotificationManager, mVisualStabilityManager, @@ -402,7 +401,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx row.getEntry().getChannel(), row.getEntry(), onSettingsClick, - onAppSettingsClick, onSnoozeClickListener, iconFactoryLoader, mDeviceProvisionedController.isDeviceProvisioned()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt index 162786cc1fbd..8b6081ea547c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt @@ -18,12 +18,16 @@ package com.android.systemui.statusbar.notification.row.wrapper import android.content.Context import android.view.View - +import android.view.ViewGroup import com.android.internal.widget.ConversationLayout import com.android.internal.widget.MessagingLinearLayout import com.android.systemui.R +import com.android.systemui.statusbar.TransformableView +import com.android.systemui.statusbar.ViewTransformationHelper import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.TransformState import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.HybridNotificationView /** * Wraps a notification containing a converation template @@ -41,6 +45,7 @@ class NotificationConversationTemplateViewWrapper constructor( private var conversationBadge: View? = null private var expandButton: View? = null private lateinit var expandButtonContainer: View + private lateinit var imageMessageContainer: ViewGroup private var messagingLinearLayout: MessagingLinearLayout? = null init { @@ -51,6 +56,7 @@ class NotificationConversationTemplateViewWrapper constructor( private fun resolveViews() { messagingLinearLayout = conversationLayout.messagingLinearLayout + imageMessageContainer = conversationLayout.imageMessageContainer conversationIcon = conversationLayout.requireViewById( com.android.internal.R.id.conversation_icon) conversationBadge = conversationLayout.requireViewById( @@ -74,6 +80,36 @@ class NotificationConversationTemplateViewWrapper constructor( messagingLinearLayout?.let { mTransformationHelper.addTransformedView(it.id, it) } + + // Let's ignore the image message container since that is transforming as part of the + // messages already + mTransformationHelper.setCustomTransformation( + object : ViewTransformationHelper.CustomTransformation() { + override fun transformTo(ownState: TransformState, + otherView: TransformableView, + transformationAmount: Float): Boolean { + if (otherView is HybridNotificationView) { + return false + } + // we're hidden by default by the transformState + ownState.ensureVisible(); + // Let's do nothing otherwise, this is already handled by the messages + return true + } + + override fun transformFrom(ownState: TransformState, + otherView: TransformableView, + transformationAmount: Float): Boolean { + if (otherView is HybridNotificationView) { + return false + } + // we're hidden by default by the transformState + ownState.ensureVisible(); + // Let's do nothing otherwise, this is already handled by the messages + return true + } + }, imageMessageContainer.id) + conversationIcon?.let { mTransformationHelper.addViewTransformingToSimilar(it.id, it) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 2e3a57af2adb..6998edda3127 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -26,6 +26,8 @@ import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -223,7 +225,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_SetsShortcutIcon() { mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -232,7 +233,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon); @@ -244,7 +244,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -253,7 +252,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); @@ -291,7 +289,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -300,7 +297,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); @@ -314,7 +310,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_GroupNameHiddenIfNoGroup() { mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -323,7 +318,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); @@ -336,7 +330,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_noDelegate() { mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -345,7 +338,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); @@ -365,7 +357,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { NotificationEntry entry = new NotificationEntryBuilder().setSbn(mSbn).build(); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -374,7 +365,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { entry, null, null, - null, mIconFactory, true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); @@ -387,7 +377,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -399,7 +388,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { latch.countDown(); }, null, - null, mIconFactory, true); @@ -413,7 +401,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() { mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -422,7 +409,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); @@ -434,7 +420,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -446,7 +431,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { latch.countDown(); }, null, - null, mIconFactory, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); @@ -454,33 +438,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { } @Test - public void testBindNotification_bubbleActionVisibleWhenCanBubble() { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1); - mNotificationInfo.bindNotification( - mShortcutManager, - mLauncherApps, - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mBubbleEntry, - null, - null, - null, - mIconFactory, - true); - - View bubbleView = mNotificationInfo.findViewById(R.id.bubble); - assertEquals(View.VISIBLE, bubbleView.getVisibility()); - } - - @Test - public void testBindNotification_bubbleAction_noBubbleMetadata() { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1); + public void testBindNotification_silentSelected_isFave_isSilent() { + mConversationChannel.setImportance(IMPORTANCE_LOW); + mConversationChannel.setImportantConversation(true); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -489,203 +451,186 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - - View bubbleView = mNotificationInfo.findViewById(R.id.bubble); - assertEquals(View.GONE, bubbleView.getVisibility()); + View view = mNotificationInfo.findViewById(R.id.silence); + assertThat(view.isSelected()).isTrue(); } @Test - public void testBindNotification_bubbleActionGloballyOff() { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0); + public void testBindNotification_defaultSelected_notFave_notSilent() { + mConversationChannel.setImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(false); + mConversationChannel.setAllowBubbles(true); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, - mBubbleEntry, - null, + mEntry, null, null, mIconFactory, true); - - View bubbleView = mNotificationInfo.findViewById(R.id.bubble); - assertEquals(View.GONE, bubbleView.getVisibility()); + View view = mNotificationInfo.findViewById(R.id.default_behavior); + assertThat(view.isSelected()).isTrue(); } @Test - public void testAddToHome() throws Exception { - when(mShortcutManager.isRequestPinShortcutSupported()).thenReturn(true); + public void testFavorite() throws Exception { + mConversationChannel.setAllowBubbles(false); + mConversationChannel.setImportance(IMPORTANCE_LOW); + mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, - mBubbleEntry, - null, + mEntry, null, null, mIconFactory, true); - // Promote it - mNotificationInfo.findViewById(R.id.home).performClick(); + View fave = mNotificationInfo.findViewById(R.id.priority); + fave.performClick(); mTestableLooper.processAllMessages(); - verify(mShortcutManager, times(1)).requestPinShortcut(mShortcutInfo, null); + // silence subtext visible, others not + assertThat(mNotificationInfo.findViewById(R.id.priority_summary).getVisibility()) + .isEqualTo(VISIBLE); + assertThat(mNotificationInfo.findViewById(R.id.default_summary).getVisibility()) + .isEqualTo(GONE); + assertThat(mNotificationInfo.findViewById(R.id.silence_summary).getVisibility()) + .isEqualTo(GONE); + + // no changes until hit done verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); - verify(mShadeController).animateCollapsePanels(); + assertFalse(mConversationChannel.isImportantConversation()); + assertFalse(mConversationChannel.canBubble()); + assertEquals(IMPORTANCE_LOW, mConversationChannel.getImportance()); } @Test - public void testSnooze() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - + public void testDefault() throws Exception { + mConversationChannel.setAllowBubbles(false); + mConversationChannel.setImportance(IMPORTANCE_LOW); + mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, - mBubbleEntry, + mEntry, null, null, - (View v, int hours) -> { - latch.countDown(); - }, mIconFactory, true); - // Promote it - mNotificationInfo.findViewById(R.id.snooze).performClick(); + mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mTestableLooper.processAllMessages(); - assertEquals(0, latch.getCount()); + // silence subtext visible, others not + assertThat(mNotificationInfo.findViewById(R.id.priority_summary).getVisibility()) + .isEqualTo(GONE); + assertThat(mNotificationInfo.findViewById(R.id.default_summary).getVisibility()) + .isEqualTo(VISIBLE); + assertThat(mNotificationInfo.findViewById(R.id.silence_summary).getVisibility()) + .isEqualTo(GONE); + + // no changes until hit done verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); + assertFalse(mConversationChannel.isImportantConversation()); + assertFalse(mConversationChannel.canBubble()); + assertEquals(IMPORTANCE_LOW, mConversationChannel.getImportance()); } @Test - public void testBubble_promotesBubble() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1); - mNotificationChannel.setAllowBubbles(false); - mConversationChannel.setAllowBubbles(false); + public void testSilence() throws Exception { + mConversationChannel.setImportance(IMPORTANCE_DEFAULT); + mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, - mBubbleEntry, - null, + mEntry, null, null, mIconFactory, true); - assertFalse(mBubbleEntry.isBubble()); + View silence = mNotificationInfo.findViewById(R.id.silence); - // Promote it - mNotificationInfo.findViewById(R.id.bubble).performClick(); + silence.performClick(); mTestableLooper.processAllMessages(); - verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry); - ArgumentCaptor<NotificationChannel> captor = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), anyInt(), captor.capture()); - assertTrue(captor.getValue().canBubble()); + // silence subtext visible, others not + assertThat(mNotificationInfo.findViewById(R.id.priority_summary).getVisibility()) + .isEqualTo(GONE); + assertThat(mNotificationInfo.findViewById(R.id.default_summary).getVisibility()) + .isEqualTo(GONE); + assertThat(mNotificationInfo.findViewById(R.id.silence_summary).getVisibility()) + .isEqualTo(VISIBLE); + + // no changes until save + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), anyInt(), any()); + assertEquals(IMPORTANCE_DEFAULT, mConversationChannel.getImportance()); } @Test - public void testBubble_demotesBubble() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1); - mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE; + public void testFavorite_andSave() throws Exception { + mConversationChannel.setAllowBubbles(false); + mConversationChannel.setImportance(IMPORTANCE_LOW); + mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, - mBubbleEntry, - null, + mEntry, null, null, mIconFactory, true); - assertTrue(mBubbleEntry.isBubble()); - - // Demote it - mNotificationInfo.findViewById(R.id.bubble).performClick(); + View fave = mNotificationInfo.findViewById(R.id.priority); + fave.performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); - verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry); ArgumentCaptor<NotificationChannel> captor = ArgumentCaptor.forClass(NotificationChannel.class); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( anyString(), anyInt(), captor.capture()); - assertFalse(captor.getValue().canBubble()); + assertTrue(captor.getValue().isImportantConversation()); + assertTrue(captor.getValue().canBubble()); + assertEquals(IMPORTANCE_DEFAULT, captor.getValue().getImportance()); } @Test - public void testBubble_noChannelChange() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1); - mNotificationInfo.bindNotification( - mShortcutManager, - mLauncherApps, - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mBubbleEntry, - null, - null, - null, - mIconFactory, - true); - - assertFalse(mBubbleEntry.isBubble()); - assertTrue(mNotificationChannel.canBubble()); - - // Promote it - mNotificationInfo.findViewById(R.id.bubble).performClick(); - mTestableLooper.processAllMessages(); - - verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry); - verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( - anyString(), anyInt(), any()); - } + public void testFavorite_andSave_doesNotLowerImportance() throws Exception { + mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportance(9); - @Test - public void testFavorite_favorite_noBubble() throws Exception { - Settings.Secure.putInt(mContext.getContentResolver(), - BUBBLE_IMPORTANT_CONVERSATIONS, 0); - mNotificationChannel.setAllowBubbles(false); - mConversationChannel.setAllowBubbles(false); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -694,35 +639,28 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - ImageButton fave = mNotificationInfo.findViewById(R.id.fave); - assertEquals(mContext.getString(R.string.notification_conversation_unfavorite), - fave.getContentDescription().toString()); - + View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); ArgumentCaptor<NotificationChannel> captor = ArgumentCaptor.forClass(NotificationChannel.class); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( anyString(), anyInt(), captor.capture()); - assertTrue(captor.getValue().isImportantConversation()); - assertFalse(captor.getValue().canBubble()); - verify(mBubbleController, never()).onUserCreatedBubbleFromNotification(mEntry); + assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance()); } @Test - public void testFavorite_favorite_bubble() throws Exception { - Settings.Secure.putInt(mContext.getContentResolver(), - BUBBLE_IMPORTANT_CONVERSATIONS, 1); - mNotificationChannel.setAllowBubbles(false); - mConversationChannel.setAllowBubbles(false); + public void testDefault_andSave() throws Exception { + mConversationChannel.setAllowBubbles(true); + mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(true); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -731,33 +669,29 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - ImageButton fave = mNotificationInfo.findViewById(R.id.fave); - assertEquals(mContext.getString(R.string.notification_conversation_unfavorite), - fave.getContentDescription().toString()); - - fave.performClick(); + mNotificationInfo.findViewById(R.id.default_behavior).performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); ArgumentCaptor<NotificationChannel> captor = ArgumentCaptor.forClass(NotificationChannel.class); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( anyString(), anyInt(), captor.capture()); - assertTrue(captor.getValue().isImportantConversation()); - assertTrue(captor.getValue().canBubble()); + assertFalse(captor.getValue().isImportantConversation()); + assertFalse(captor.getValue().canBubble()); + assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance()); } @Test - public void testFavorite_unfavorite() throws Exception { - mNotificationChannel.setImportantConversation(true); - mConversationChannel.setImportantConversation(true); - + public void testDefault_andSave_doesNotChangeNonImportantBubbling() throws Exception { + mConversationChannel.setAllowBubbles(true); + mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -766,15 +700,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - ImageButton fave = mNotificationInfo.findViewById(R.id.fave); - assertEquals(mContext.getString(R.string.notification_conversation_favorite), - fave.getContentDescription().toString()); - - fave.performClick(); + mNotificationInfo.findViewById(R.id.default_behavior).performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); ArgumentCaptor<NotificationChannel> captor = @@ -782,16 +712,17 @@ public class NotificationConversationInfoTest extends SysuiTestCase { verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( anyString(), anyInt(), captor.capture()); assertFalse(captor.getValue().isImportantConversation()); + assertTrue(captor.getValue().canBubble()); + assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance()); } @Test - public void testMute_mute() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mConversationChannel.setImportance(IMPORTANCE_DEFAULT); + public void testDefault_andSave_doesNotDemoteImportance() throws Exception { + mConversationChannel.setImportance(9); + mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -800,34 +731,28 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - ImageButton mute = mNotificationInfo.findViewById(R.id.mute); - assertEquals(mContext.getString(R.string.notification_conversation_unmute), - mute.getContentDescription().toString()); - - mute.performClick(); + mNotificationInfo.findViewById(R.id.default_behavior).performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); ArgumentCaptor<NotificationChannel> captor = ArgumentCaptor.forClass(NotificationChannel.class); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( anyString(), anyInt(), captor.capture()); - assertEquals(IMPORTANCE_LOW, captor.getValue().getImportance()); + assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance()); } @Test - public void testMute_unmute() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationChannel.setOriginalImportance(IMPORTANCE_HIGH); - mConversationChannel.setImportance(IMPORTANCE_LOW); - mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); + public void testSilence_andSave() throws Exception { + mConversationChannel.setImportance(IMPORTANCE_DEFAULT); + mConversationChannel.setImportantConversation(true); + mConversationChannel.setAllowBubbles(true); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -836,29 +761,27 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - ImageButton mute = mNotificationInfo.findViewById(R.id.mute); - assertEquals(mContext.getString(R.string.notification_conversation_mute), - mute.getContentDescription().toString()); - - mute.performClick(); + View silence = mNotificationInfo.findViewById(R.id.silence); + silence.performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); ArgumentCaptor<NotificationChannel> captor = ArgumentCaptor.forClass(NotificationChannel.class); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( anyString(), anyInt(), captor.capture()); - assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance()); + assertFalse(captor.getValue().isImportantConversation()); + assertFalse(captor.getValue().canBubble()); + assertEquals(IMPORTANCE_LOW, captor.getValue().getImportance()); } @Test public void testBindNotification_createsNewChannel() throws Exception { mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -867,7 +790,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); @@ -880,7 +802,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mNotificationChannel.setConversationId("", CONVERSATION_ID); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -889,7 +810,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); @@ -903,7 +823,6 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification( mShortcutManager, - mLauncherApps, mMockPackageManager, mMockINotificationManager, mVisualStabilityManager, @@ -912,11 +831,13 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mEntry, null, null, - null, mIconFactory, true); - mNotificationInfo.findViewById(R.id.mute).performClick(); + mNotificationInfo.findViewById(R.id.silence).performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); + + mTestableLooper.processAllMessages(); verify(mVisualStabilityManager).temporarilyAllowReordering(); } diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index b4b0641e2f2a..8b50b010a22c 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -28,6 +28,7 @@ import android.app.AppGlobals; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.os.Bundle; @@ -250,23 +251,31 @@ final class RemoteAugmentedAutofillService final InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse( request, inlineSuggestionsData, focusedId, - dataset -> { - mCallbacks.logAugmentedAutofillSelected(sessionId, - dataset.getId()); - try { - final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); - final int size = fieldIds.size(); - final boolean hideHighlight = size == 1 - && fieldIds.get(0).equals(focusedId); - if (dataset.getAuthentication() != null) { - client.startIntentSender(dataset.getAuthentication(), - new Intent()); - } else { + new InlineSuggestionFactory.InlineSuggestionUiCallback() { + @Override + public void autofill(Dataset dataset) { + mCallbacks.logAugmentedAutofillSelected(sessionId, + dataset.getId()); + try { + final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); + final int size = fieldIds.size(); + final boolean hideHighlight = size == 1 + && fieldIds.get(0).equals(focusedId); client.autofill(sessionId, fieldIds, dataset.getFieldValues(), hideHighlight); + } catch (RemoteException e) { + Slog.w(TAG, "Encounter exception autofilling the values"); + } + } + + @Override + public void startIntentSender(IntentSender intentSender, + Intent intent) { + try { + client.startIntentSender(intentSender, intent); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException starting intent sender"); } - } catch (RemoteException e) { - Slog.w(TAG, "Encounter exception autofilling the values"); } }, onErrorCallback, remoteRenderService); diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index c26d7eea8351..e98ac75053a7 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -21,6 +21,8 @@ import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Intent; +import android.content.IntentSender; import android.os.IBinder; import android.os.RemoteException; import android.service.autofill.Dataset; @@ -49,6 +51,7 @@ import com.android.server.inputmethod.InputMethodManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.regex.Pattern; public final class InlineSuggestionFactory { @@ -62,6 +65,11 @@ public final class InlineSuggestionFactory { * Callback to autofill a dataset to the client app. */ void autofill(@NonNull Dataset dataset); + + /** + * Callback to start Intent in client app. + */ + void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent); } /** @@ -94,7 +102,8 @@ public final class InlineSuggestionFactory { response.getAuthentication() == null ? null : response.getInlinePresentation(); return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, response.getDatasets(), filterText, inlineAuthentication, autofillId, - onErrorCallback, onClickFactory, remoteRenderService); + onErrorCallback, onClickFactory, (intentSender) -> + client.startIntentSender(intentSender, new Intent()), remoteRenderService); } /** @@ -111,8 +120,12 @@ public final class InlineSuggestionFactory { if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called"); return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request, datasets, /* filterText= */ null, /* inlineAuthentication= */ null, - autofillId, onErrorCallback, (dataset, datasetIndex) -> - inlineSuggestionUiCallback.autofill(dataset), remoteRenderService); + autofillId, onErrorCallback, + (dataset, datasetIndex) -> + inlineSuggestionUiCallback.autofill(dataset), + (intentSender) -> + inlineSuggestionUiCallback.startIntentSender(intentSender, new Intent()), + remoteRenderService); } @Nullable @@ -121,12 +134,13 @@ public final class InlineSuggestionFactory { @Nullable List<Dataset> datasets, @Nullable String filterText, @Nullable InlinePresentation inlineAuthentication, @NonNull AutofillId autofillId, @NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory, + @NonNull Consumer<IntentSender> intentSenderConsumer, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); if (inlineAuthentication != null) { InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication, - remoteRenderService, onClickFactory, onErrorCallback, + remoteRenderService, onClickFactory, onErrorCallback, intentSenderConsumer, request.getHostInputToken(), request.getHostDisplayId()); inlineSuggestions.add(inlineAuthSuggestion); @@ -157,7 +171,7 @@ public final class InlineSuggestionFactory { InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset, datasetIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation), - onClickFactory, remoteRenderService, onErrorCallback, + onClickFactory, remoteRenderService, onErrorCallback, intentSenderConsumer, request.getHostInputToken(), request.getHostDisplayId()); inlineSuggestions.add(inlineSuggestion); @@ -201,7 +215,8 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, - @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken, + @NonNull Runnable onErrorCallback, @NonNull Consumer<IntentSender> intentSenderConsumer, + @Nullable IBinder hostInputToken, int displayId) { final String suggestionSource = isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM : InlineSuggestionInfo.SOURCE_AUTOFILL; @@ -216,7 +231,7 @@ public final class InlineSuggestionFactory { final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo, createInlineContentProvider(inlinePresentation, () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback, - remoteRenderService, hostInputToken, displayId)); + intentSenderConsumer, remoteRenderService, hostInputToken, displayId)); return inlineSuggestion; } @@ -225,6 +240,7 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback, + @NonNull Consumer<IntentSender> intentSenderConsumer, @Nullable IBinder hostInputToken, int displayId) { final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), @@ -235,7 +251,8 @@ public final class InlineSuggestionFactory { createInlineContentProvider(inlinePresentation, () -> onClickFactory.accept(null, AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED), - onErrorCallback, remoteRenderService, hostInputToken, displayId)); + onErrorCallback, intentSenderConsumer, remoteRenderService, hostInputToken, + displayId)); } /** @@ -261,6 +278,7 @@ public final class InlineSuggestionFactory { private static IInlineContentProvider.Stub createInlineContentProvider( @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction, @NonNull Runnable onErrorCallback, + @NonNull Consumer<IntentSender> intentSenderConsumer, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, @Nullable IBinder hostInputToken, int displayId) { @@ -269,7 +287,7 @@ public final class InlineSuggestionFactory { public void provideContent(int width, int height, IInlineContentCallback callback) { UiThread.getHandler().post(() -> { final IInlineSuggestionUiCallback uiCallback = createInlineSuggestionUiCallback( - callback, onClickAction, onErrorCallback); + callback, onClickAction, onErrorCallback, intentSenderConsumer); if (remoteRenderService == null) { Slog.e(TAG, "RemoteInlineSuggestionRenderService is null"); @@ -285,7 +303,8 @@ public final class InlineSuggestionFactory { private static IInlineSuggestionUiCallback.Stub createInlineSuggestionUiCallback( @NonNull IInlineContentCallback callback, @NonNull Runnable onAutofillCallback, - @NonNull Runnable onErrorCallback) { + @NonNull Runnable onErrorCallback, + @NonNull Consumer<IntentSender> intentSenderConsumer) { return new IInlineSuggestionUiCallback.Stub() { @Override public void onClick() throws RemoteException { @@ -320,6 +339,11 @@ public final class InlineSuggestionFactory { onErrorCallback.run(); } } + + @Override + public void onStartIntentSender(IntentSender intentSender) { + intentSenderConsumer.accept(intentSender); + } }; } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index a7673473728f..0bf81e0678dc 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -682,6 +682,7 @@ class StorageManagerService extends IStorageManager.Stub private static final int H_ABORT_IDLE_MAINT = 12; private static final int H_BOOT_COMPLETED = 13; private static final int H_COMPLETE_UNLOCK_USER = 14; + private static final int H_VOLUME_STATE_CHANGED = 15; class StorageManagerServiceHandler extends Handler { public StorageManagerServiceHandler(Looper looper) { @@ -805,6 +806,11 @@ class StorageManagerService extends IStorageManager.Stub completeUnlockUser((int) msg.obj); break; } + case H_VOLUME_STATE_CHANGED: { + final SomeArgs args = (SomeArgs) msg.obj; + onVolumeStateChangedInternal((VolumeInfo) args.arg1, (int) args.arg2, + (int) args.arg3); + } } } } @@ -1323,7 +1329,11 @@ class StorageManagerService extends IStorageManager.Stub final int oldState = vol.state; final int newState = state; vol.state = newState; - onVolumeStateChangedLocked(vol, oldState, newState); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = vol; + args.arg2 = oldState; + args.arg3 = newState; + mHandler.obtainMessage(H_VOLUME_STATE_CHANGED, args).sendToTarget(); } } } @@ -1496,78 +1506,89 @@ class StorageManagerService extends IStorageManager.Stub return true; } - @GuardedBy("mLock") - private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) { - if (vol.type == VolumeInfo.TYPE_EMULATED && newState != VolumeInfo.STATE_MOUNTED) { - mFuseMountedUser.remove(vol.getMountUserId()); - } - // Remember that we saw this volume so we're ready to accept user - // metadata, or so we can annoy them when a private volume is ejected - if (!TextUtils.isEmpty(vol.fsUuid)) { - VolumeRecord rec = mRecords.get(vol.fsUuid); - if (rec == null) { - rec = new VolumeRecord(vol.type, vol.fsUuid); - rec.partGuid = vol.partGuid; - rec.createdMillis = System.currentTimeMillis(); - if (vol.type == VolumeInfo.TYPE_PRIVATE) { - rec.nickname = vol.disk.getDescription(); - } - mRecords.put(rec.fsUuid, rec); - } else { - // Handle upgrade case where we didn't store partition GUID - if (TextUtils.isEmpty(rec.partGuid)) { + private void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) { + synchronized (mLock) { + if (vol.type == VolumeInfo.TYPE_EMULATED && newState != VolumeInfo.STATE_MOUNTED) { + mFuseMountedUser.remove(vol.getMountUserId()); + } + // Remember that we saw this volume so we're ready to accept user + // metadata, or so we can annoy them when a private volume is ejected + if (!TextUtils.isEmpty(vol.fsUuid)) { + VolumeRecord rec = mRecords.get(vol.fsUuid); + if (rec == null) { + rec = new VolumeRecord(vol.type, vol.fsUuid); rec.partGuid = vol.partGuid; + rec.createdMillis = System.currentTimeMillis(); + if (vol.type == VolumeInfo.TYPE_PRIVATE) { + rec.nickname = vol.disk.getDescription(); + } + mRecords.put(rec.fsUuid, rec); + } else { + // Handle upgrade case where we didn't store partition GUID + if (TextUtils.isEmpty(rec.partGuid)) { + rec.partGuid = vol.partGuid; + } } - } - rec.lastSeenMillis = System.currentTimeMillis(); - writeSettingsLocked(); + rec.lastSeenMillis = System.currentTimeMillis(); + writeSettingsLocked(); + } } - - mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); - - // Do not broadcast before boot has completed to avoid launching the - // processes that receive the intent unnecessarily. - if (mBootCompleted && isBroadcastWorthy(vol)) { - final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); - intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); - intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); - intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); + // This is a blocking call to Storage Service which needs to process volume state changed + // before notifying other listeners. + // Intentionally called without the mLock to avoid deadlocking from the Storage Service. + try { + mStorageSessionController.notifyVolumeStateChanged(vol); + } catch (ExternalStorageServiceException e) { + Log.e(TAG, "Failed to notify volume state changed to the Storage Service", e); } - - final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); - final String newStateEnv = VolumeInfo.getEnvironmentForState(newState); - - if (!Objects.equals(oldStateEnv, newStateEnv)) { - // Kick state changed event towards all started users. Any users - // started after this point will trigger additional - // user-specific broadcasts. - for (int userId : mSystemUnlockedUsers) { - if (vol.isVisibleForRead(userId)) { - final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); - mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); - - mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv, - newStateEnv); + synchronized (mLock) { + mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); + + // Do not broadcast before boot has completed to avoid launching the + // processes that receive the intent unnecessarily. + if (mBootCompleted && isBroadcastWorthy(vol)) { + final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); + intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); + } + + final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); + final String newStateEnv = VolumeInfo.getEnvironmentForState(newState); + + if (!Objects.equals(oldStateEnv, newStateEnv)) { + // Kick state changed event towards all started users. Any users + // started after this point will trigger additional + // user-specific broadcasts. + for (int userId : mSystemUnlockedUsers) { + if (vol.isVisibleForRead(userId)) { + final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, + false); + mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); + + mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv, + newStateEnv); + } } } - } - if ((vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_STUB) + if ((vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_STUB) && vol.state == VolumeInfo.STATE_EJECTING) { - // TODO: this should eventually be handled by new ObbVolume state changes - /* - * Some OBBs might have been unmounted when this volume was - * unmounted, so send a message to the handler to let it know to - * remove those from the list of mounted OBBS. - */ - mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage( - OBB_FLUSH_MOUNT_STATE, vol.path)); + // TODO: this should eventually be handled by new ObbVolume state changes + /* + * Some OBBs might have been unmounted when this volume was + * unmounted, so send a message to the handler to let it know to + * remove those from the list of mounted OBBS. + */ + mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage( + OBB_FLUSH_MOUNT_STATE, vol.path)); + } + maybeLogMediaMount(vol, newState); } - maybeLogMediaMount(vol, newState); } private void maybeLogMediaMount(VolumeInfo vol, int newState) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 89e245cd8568..b79f75a2d335 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -24660,15 +24660,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void clearMimeGroup(String packageName, String mimeGroup) { - boolean changed = mSettings.mPackages.get(packageName).clearMimeGroup(mimeGroup); - - if (changed) { - applyMimeGroupChanges(packageName, mimeGroup); - } - } - - @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup); } diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index a83a8476b8db..9a8692d029e0 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -241,7 +241,8 @@ public class PackageSetting extends PackageSettingBase { public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) { ArraySet<String> oldMimeTypes = getMimeGroupInternal(mimeGroup); if (oldMimeTypes == null) { - return false; + throw new IllegalArgumentException("Unknown MIME group " + mimeGroup + + " for package " + name); } ArraySet<String> newMimeTypes = new ArraySet<>(mimeTypes); @@ -250,21 +251,11 @@ public class PackageSetting extends PackageSettingBase { return hasChanges; } - public boolean clearMimeGroup(String mimeGroup) { - ArraySet<String> mimeTypes = getMimeGroupInternal(mimeGroup); - - if (mimeTypes == null || mimeTypes.isEmpty()) { - return false; - } - - mimeTypes.clear(); - return true; - } - public List<String> getMimeGroup(String mimeGroup) { ArraySet<String> mimeTypes = getMimeGroupInternal(mimeGroup); if (mimeTypes == null) { - return null; + throw new IllegalArgumentException("Unknown MIME group " + mimeGroup + + " for package " + name); } return new ArrayList<>(mimeTypes); } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index e3abcda2530f..a726d39b8595 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -125,7 +125,6 @@ public final class DefaultPermissionGrantPolicy { static { - PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE); PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE); PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG); PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG); diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index baef5d6ef2a0..0fd77b9c03dd 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -106,6 +106,38 @@ public final class StorageSessionController { } /** + * Notifies the Storage Service that volume state for {@code vol} is changed. + * A session may already be created for this volume if it is mounted before or the volume state + * has changed to mounted. + * + * Does nothing if {@link #shouldHandle} is {@code false} + * + * Blocks until the Storage Service processes/scans the volume or fails in doing so. + * + * @throws ExternalStorageServiceException if it fails to connect to ExternalStorageService + */ + public void notifyVolumeStateChanged(VolumeInfo vol) throws ExternalStorageServiceException { + if (!shouldHandle(vol)) { + return; + } + String sessionId = vol.getId(); + int userId = vol.getMountUserId(); + + StorageUserConnection connection = null; + synchronized (mLock) { + connection = mConnections.get(userId); + if (connection != null) { + Slog.i(TAG, "Notifying volume state changed for session with id: " + sessionId); + connection.notifyVolumeStateChanged(sessionId, + vol.buildStorageVolume(mContext, userId, false)); + } else { + Slog.w(TAG, "No available storage user connection for userId : " + userId); + } + } + } + + + /** * Removes and returns the {@link StorageUserConnection} for {@code vol}. * * Does nothing if {@link #shouldHandle} is {@code false} diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java index dd18f4e5ab17..c207a7b4039b 100644 --- a/services/core/java/com/android/server/storage/StorageUserConnection.java +++ b/services/core/java/com/android/server/storage/StorageUserConnection.java @@ -35,6 +35,7 @@ import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.UserHandle; import android.os.storage.StorageManagerInternal; +import android.os.storage.StorageVolume; import android.service.storage.ExternalStorageService; import android.service.storage.IExternalStorageService; import android.text.TextUtils; @@ -100,6 +101,23 @@ public final class StorageUserConnection { } /** + * Notifies Storage Service about volume state changed. + * + * @throws ExternalStorageServiceException if failed to notify the Storage Service that + * {@code StorageVolume} is changed + */ + public void notifyVolumeStateChanged(String sessionId, StorageVolume vol) + throws ExternalStorageServiceException { + Objects.requireNonNull(sessionId); + Objects.requireNonNull(vol); + + prepareRemote(); + synchronized (mLock) { + mActiveConnection.notifyVolumeStateChangedLocked(sessionId, vol); + } + } + + /** * Removes a session without ending it or waiting for exit. * * This should only be used if the session has certainly been ended because the volume was @@ -287,6 +305,20 @@ public final class StorageUserConnection { } } + public void notifyVolumeStateChangedLocked(String sessionId, StorageVolume vol) throws + ExternalStorageServiceException { + CountDownLatch latch = new CountDownLatch(1); + try { + mRemote.notifyVolumeStateChanged(sessionId, vol, new RemoteCallback( + result -> setResultLocked(latch, result))); + waitForLatch(latch, "notify_volume_state_changed " + vol); + maybeThrowExceptionLocked(); + } catch (Exception e) { + throw new ExternalStorageServiceException("Failed to notify volume state changed " + + "for vol : " + vol, e); + } + } + private void setResultLocked(CountDownLatch latch, Bundle result) { mLastException = result.getParcelable(EXTRA_ERROR); latch.countDown(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3c0db0961c15..fb079f355867 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1918,7 +1918,6 @@ class Task extends WindowContainer<WindowContainer> { super.onConfigurationChanged(newParentConfig); if (wasInMultiWindowMode != inMultiWindowMode()) { mStackSupervisor.scheduleUpdateMultiWindowMode(this); - updateShadowsRadius(isFocused(), getPendingTransaction()); } final int newWinMode = getWindowingMode(); @@ -3325,6 +3324,7 @@ class Task extends WindowContainer<WindowContainer> { } updateSurfaceCrop(); + updateShadowsRadius(isFocused(), getPendingTransaction()); if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) { scheduleAnimation(); @@ -4149,26 +4149,40 @@ class Task extends WindowContainer<WindowContainer> { } /** + * @return true if the task is visible and has at least one visible child. + */ + private boolean hasVisibleChildren() { + if (!isAttached() || isForceHidden()) { + return false; + } + + return getActivity(ActivityRecord::isVisible) != null; + } + + /** * @return the desired shadow radius in pixels for the current task. */ private float getShadowRadius(boolean taskIsFocused) { - if (mDisplayContent == null) { - return 0; - } + int elevation = 0; + // Get elevation for a specific windowing mode. if (inPinnedWindowingMode()) { - return dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, - mDisplayContent.getDisplayMetrics()); - } - // TODO(b/149585281) remove when root task has the correct bounds for freeform - if (ENABLE_FREEFORM_COMPOSITOR_SHADOWS && inFreeformWindowingMode()) { - final int elevation = taskIsFocused + elevation = PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; + } else if (ENABLE_FREEFORM_COMPOSITOR_SHADOWS && inFreeformWindowingMode()) { + // TODO(b/149585281) remove when root task has the correct bounds for freeform + elevation = taskIsFocused ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; - return dipToPixel(elevation, mDisplayContent.getDisplayMetrics()); + } else { + // For all other windowing modes, do not draw a shadow. + return 0; + } + + // If the task has no visible children, do not draw a shadow. + if (!hasVisibleChildren()) { + return 0; } - // For all other windowing modes, do not draw a shadow. - return 0; + return dipToPixel(elevation, getDisplayContent().getDisplayMetrics()); } /** diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 5f0c8fe3f1f7..725036c9df0f 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -51,7 +51,8 @@ using BlockSize = int16_t; using FileIdx = int16_t; using BlockIdx = int32_t; using NumBlocks = int32_t; -using CompressionType = int16_t; +using BlockType = int8_t; +using CompressionType = int8_t; using RequestType = int16_t; using MagicType = uint32_t; @@ -59,7 +60,7 @@ static constexpr int BUFFER_SIZE = 256 * 1024; static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE; static constexpr int COMMAND_SIZE = 4 + 2 + 2 + 4; // bytes -static constexpr int HEADER_SIZE = 2 + 2 + 4 + 2; // bytes +static constexpr int HEADER_SIZE = 2 + 1 + 1 + 4 + 2; // bytes static constexpr std::string_view OKAY = "OKAY"sv; static constexpr MagicType INCR = 0x52434e49; // BE INCR @@ -110,6 +111,7 @@ const JniIds& jniIds(JNIEnv* env) { struct BlockHeader { FileIdx fileIdx = -1; + BlockType blockType = -1; CompressionType compressionType = -1; BlockIdx blockIdx = -1; BlockSize blockSize = -1; @@ -649,8 +651,8 @@ private: auto remainingData = std::span(data); while (!remainingData.empty()) { auto header = readHeader(remainingData); - if (header.fileIdx == -1 && header.compressionType == 0 && header.blockIdx == 0 && - header.blockSize == 0) { + if (header.fileIdx == -1 && header.blockType == 0 && header.compressionType == 0 && + header.blockIdx == 0 && header.blockSize == 0) { ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).", int(remainingData.size())); @@ -658,8 +660,8 @@ private: mStopReceiving = true; break; } - if (header.fileIdx < 0 || header.blockSize <= 0 || header.compressionType < 0 || - header.blockIdx < 0) { + if (header.fileIdx < 0 || header.blockSize <= 0 || header.blockType < 0 || + header.compressionType < 0 || header.blockIdx < 0) { ALOGE("invalid header received. Abort."); mStopReceiving = true; break; @@ -687,7 +689,7 @@ private: .fileFd = writeFd, .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx), .compression = static_cast<IncFsCompressionKind>(header.compressionType), - .kind = INCFS_BLOCK_KIND_DATA, + .kind = static_cast<IncFsBlockKind>(header.blockType), .dataSize = static_cast<uint16_t>(header.blockSize), .data = (const char*)remainingData.data(), }; @@ -761,8 +763,8 @@ BlockHeader readHeader(std::span<uint8_t>& data) { } header.fileIdx = static_cast<FileIdx>(be16toh(*reinterpret_cast<const uint16_t*>(&data[0]))); - header.compressionType = - static_cast<CompressionType>(be16toh(*reinterpret_cast<const uint16_t*>(&data[2]))); + header.blockType = static_cast<BlockType>(data[2]); + header.compressionType = static_cast<CompressionType>(data[3]); header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<const uint32_t*>(&data[4]))); header.blockSize = static_cast<BlockSize>(be16toh(*reinterpret_cast<const uint16_t*>(&data[8]))); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 1a4ce8aa97f8..891acc1fe6ef 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -158,6 +158,7 @@ public class PowerManagerServiceTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + FakeSettingsProvider.clearSettingsProvider(); mPowerSaveState = new PowerSaveState.Builder() .setBatterySaverEnabled(BATTERY_SAVER_ENABLED) @@ -263,6 +264,7 @@ public class PowerManagerServiceTest { LocalServices.removeServiceForTest(DisplayManagerInternal.class); LocalServices.removeServiceForTest(BatteryManagerInternal.class); LocalServices.removeServiceForTest(ActivityManagerInternal.class); + FakeSettingsProvider.clearSettingsProvider(); } /** diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 4940cb2b143e..93fbb00ff9d5 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -690,7 +690,7 @@ public final class NetworkRegistrationInfo implements Parcelable { */ public void updateNrState() { mNrState = NR_STATE_NONE; - if (mDataSpecificInfo.isEnDcAvailable) { + if (mDataSpecificInfo != null && mDataSpecificInfo.isEnDcAvailable) { if (!mDataSpecificInfo.isDcNrRestricted && mDataSpecificInfo.isNrAvailable) { mNrState = NR_STATE_NOT_RESTRICTED; } else { diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index f31a2af87b2b..47eb63e823e7 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -55,8 +55,7 @@ AtomDecl::AtomDecl(const AtomDecl &that) resetState(that.resetState), nested(that.nested), uidField(that.uidField), - whitelisted(that.whitelisted), - binaryFields(that.binaryFields) {} + whitelisted(that.whitelisted) {} AtomDecl::AtomDecl(int c, const string& n, const string& m) :code(c), @@ -422,10 +421,6 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, continue; } } - // Binary field validity is already checked above. - if (isBinaryField) { - atomDecl->binaryFields.push_back(it->first); - } } return errorCount; diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index d99b931338f2..c6dad1d07d89 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -147,8 +147,6 @@ struct AtomDecl { bool whitelisted = false; - vector<int> binaryFields; - AtomDecl(); AtomDecl(const AtomDecl& that); AtomDecl(int code, const string& name, const string& message); diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp index 58f13a4c1934..4f66f68e6d8c 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.cpp +++ b/tools/stats_log_api_gen/atoms_info_writer.cpp @@ -48,9 +48,6 @@ static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) { " const static std::map<int, StateAtomFieldOptions> " "kStateAtomsFieldOptions;\n"); fprintf(out, - " const static std::map<int, std::vector<int>> " - "kBytesFieldAtoms;\n"); - fprintf(out, " const static std::set<int> kWhitelistedAtoms;\n"); fprintf(out, "};\n"); fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId); @@ -175,35 +172,6 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "const std::map<int, StateAtomFieldOptions> " "AtomsInfo::kStateAtomsFieldOptions = " "getStateAtomFieldOptions();\n"); - - fprintf(out, - "static std::map<int, std::vector<int>> " - "getBinaryFieldAtoms() {\n"); - fprintf(out, " std::map<int, std::vector<int>> options;\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { - if (atom->binaryFields.size() == 0) { - continue; - } - fprintf(out, - "\n // Adding binary fields for atom " - "(%d)%s\n", - atom->code, atom->name.c_str()); - - for (const auto& field : atom->binaryFields) { - fprintf(out, " options[%d /* %s */].push_back(%d);\n", - atom->code, make_constant_name(atom->name).c_str(), field); - } - } - - fprintf(out, " return options;\n"); - fprintf(out, "}\n"); - - fprintf(out, - "const std::map<int, std::vector<int>> " - "AtomsInfo::kBytesFieldAtoms = " - "getBinaryFieldAtoms();\n"); - } int write_atoms_info_header(FILE* out, const Atoms &atoms, const string& namespaceStr) { diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java index 666d72d32ac7..87afdc59c2b9 100755 --- a/wifi/java/android/net/wifi/WifiMigration.java +++ b/wifi/java/android/net/wifi/WifiMigration.java @@ -138,7 +138,10 @@ public final class WifiMigration { /** * Load data from legacy shared wifi config store file. - * TODO(b/149418926): Add XSD for the AOSP file format for each file from R. + * <p> + * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/ + * java/android/net/wifi/migration_samples}. + * </p> * <p> * Note: * <li>OEMs need to change the implementation of @@ -214,7 +217,10 @@ public final class WifiMigration { /** * Load data from legacy user wifi config store file. - * TODO(b/149418926): Add XSD for the AOSP file format for each file from R. + * <p> + * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/ + * java/android/net/wifi/migration_samples}. + * </p> * <p> * Note: * <li>OEMs need to change the implementation of diff --git a/wifi/java/android/net/wifi/migration_samples/README.txt b/wifi/java/android/net/wifi/migration_samples/README.txt new file mode 100644 index 000000000000..264debaa51f9 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/README.txt @@ -0,0 +1,35 @@ +This folder contains sample files for each of the 4 XML Wi-Fi config store files in Android 11 AOSP. +OEMs can use these files as reference for converting their previous customized +formats into the AOSP format. The conversion logic needs to be written in +WifiMigration.java class, i.e each OEM needs to modify +WifiMigration.convertAndRetrieveSharedConfigStoreFile() and the +WifiMigration.convertAndRetrieveUserConfigStoreFile() methods. + +The 4 files are: + +Shared files +============ +1) WifiConfigStore.xml - General storage for shared configurations. Includes +user's saved Wi-Fi networks. +AOSP Path in Android 10: /data/misc/wifi/WifiConfigStore.xml +AOSP Path in Android 11: /data/misc/apexdata/com.android/wifi/WifiConfigStore.xml +Sample File (in this folder): Shared_WifiConfigStore.xml + +2) WifiConfigStoreSoftAp.xml - Storage for user's softap/tethering configuration. +AOSP Path in Android 10: /data/misc/wifi/softap.conf. +Note: Was key/value format in Android 10. Conversion to XML done in SoftApConfToXmlMigrationUtil.java. +AOSP Path in Android 11: /data/misc/apexdata/com.android/wifi/WifiConfigStore.xml +Sample File (in this folder): Shared_WifiConfigStoreSoftAp.xml + +User specific files +================== +3) WifiConfigStore.xml - General storage for user specific configurations. Includes +user's saved passpoint networks, Wi-Fi network request approvals, etc. +AOSP Path in Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStore.xml +AOSP Path in Android 11: /data/misc_ce/<userId>/apexdata/com.android/wifi/WifiConfigStore.xml +Sample File (in this folder): User_WifiConfigStore.xml + +4) WifiConfigStoreNetworkSuggestions.xml - Storage for app installed network suggestions. +AOSP Path in Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStoreNetworkSuggestions.xml +AOSP Path in Android 11: /data/misc_ce/<userId>/apexdata/com.android/wifi/WifiConfigStoreNetworkSuggestions.xml +Sample File (in this folder): User_WifiConfigStoreNetworkSuggestions.xml diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml new file mode 100644 index 000000000000..3063276fae6a --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml @@ -0,0 +1,200 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<NetworkList> +<Network> +<WifiConfiguration> +<string name="ConfigKey">"OPEN_SSID"NONE</string> +<string name="SSID">"OPEN_SSID"</string> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="1">01</byte-array> +<byte-array name="AllowedProtocols" num="1">03</byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="1">2f</byte-array> +<byte-array name="AllowedPairwiseCiphers" num="1">0e</byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="1">04</byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="2" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="true" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="3" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">android.uid.system:1000</string> +<int name="LastUpdateUid" value="1000" /> +<string name="LastUpdateName">android.uid.system:1000</string> +<int name="LastConnectUid" value="1000" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">ce:b1:36:bb:71:ac</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<NetworkStatus> +<string name="SelectionStatus">NETWORK_SELECTION_ENABLED</string> +<string name="DisableReason">NETWORK_SELECTION_ENABLE</string> +<string name="ConnectChoice">"ENTERPRISE_SSID"WPA_EAP</string> +<boolean name="HasEverConnected" value="true" /> +</NetworkStatus> +<IpConfiguration> +<string name="IpAssignment">DHCP</string> +<string name="ProxySettings">NONE</string> +</IpConfiguration> +</Network> +<Network> +<WifiConfiguration> +<string name="ConfigKey">"ENTERPRISE_SSID"WPA_EAP</string> +<string name="SSID">"ENTERPRISE_SSID"</string> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="1">0c</byte-array> +<byte-array name="AllowedProtocols" num="1">03</byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="1">2f</byte-array> +<byte-array name="AllowedPairwiseCiphers" num="1">0e</byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="1">04</byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="2" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">android.uid.system:1000</string> +<int name="LastUpdateUid" value="1000" /> +<string name="LastUpdateName">android.uid.system:1000</string> +<int name="LastConnectUid" value="1000" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">f6:b3:94:44:40:87</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<NetworkStatus> +<string name="SelectionStatus">NETWORK_SELECTION_TEMPORARY_DISABLED</string> +<string name="DisableReason">NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE</string> +<null name="ConnectChoice" /> +<boolean name="HasEverConnected" value="false" /> +</NetworkStatus> +<IpConfiguration> +<string name="IpAssignment">DHCP</string> +<string name="ProxySettings">NONE</string> +</IpConfiguration> +<WifiEnterpriseConfiguration> +<string name="Identity">adadadasdaddsa</string> +<string name="AnonIdentity">asdadaddadasd</string> +<string name="Password">adasdadadad</string> +<string name="ClientCert"></string> +<string name="CaCert"></string> +<string name="SubjectMatch"></string> +<string name="Engine">0</string> +<string name="EngineId"></string> +<string name="PrivateKeyId"></string> +<string name="AltSubjectMatch"></string> +<string name="DomSuffixMatch">adsad</string> +<string name="CaPath">/system/etc/security/cacerts</string> +<int name="EapMethod" value="0" /> +<int name="Phase2Method" value="3" /> +<string name="PLMN"></string> +<string name="Realm"></string> +<int name="Ocsp" value="0" /> +<string name="WapiCertSuite"></string> +</WifiEnterpriseConfiguration> +</Network> +<Network> +<WifiConfiguration> +<string name="ConfigKey">"WPA3_SSID"SAE</string> +<string name="SSID">"WPA3_SSID"</string> +<string name="PreSharedKey">"sfsdfsfdsfsdf"</string> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="true" /> +<byte-array name="AllowedKeyMgmt" num="2">0001</byte-array> +<byte-array name="AllowedProtocols" num="1">02</byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="1">28</byte-array> +<byte-array name="AllowedPairwiseCiphers" num="1">0c</byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="1">04</byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="1" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">android.uid.system:1000</string> +<int name="LastUpdateUid" value="1000" /> +<string name="LastUpdateName">android.uid.system:1000</string> +<int name="LastConnectUid" value="1000" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">a6:3d:b0:13:ed:41</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<NetworkStatus> +<string name="SelectionStatus">NETWORK_SELECTION_PERMANENTLY_DISABLED</string> +<string name="DisableReason">NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD</string> +<null name="ConnectChoice" /> +<boolean name="HasEverConnected" value="false" /> +</NetworkStatus> +<IpConfiguration> +<string name="IpAssignment">DHCP</string> +<string name="ProxySettings">NONE</string> +</IpConfiguration> +</Network> +</NetworkList> +<MacAddressMap> +<map name="MacMapEntry" /> +</MacAddressMap> +<Settings> +<map name="Values"> +<boolean name="wifi_p2p_pending_factory_reset" value="false" /> +<boolean name="wifi_scan_throttle_enabled" value="true" /> +<null name="wifi_p2p_device_name" /> +<boolean name="wifi_scan_always_enabled" value="false" /> +<boolean name="wifi_verbose_logging_enabled" value="true" /> +</map> +</Settings> +<PasspointConfigData> +<long name="ProviderIndex" value="0" /> +</PasspointConfigData> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml new file mode 100644 index 000000000000..fd99dd3df8b2 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml @@ -0,0 +1,22 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<SoftAp> +<string name="SSID">HOTSPOT_SSID</string> +<int name="ApBand" value="1" /> +<int name="Channel" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<int name="SecurityType" value="1" /> +<string name="Passphrase">blahblahblah</string> +<int name="MaxNumberOfClients" value="0" /> +<boolean name="ClientControlByUser" value="false" /> +<boolean name="AutoShutdownEnabled" value="true" /> +<long name="ShutdownTimeoutMillis" value="0" /> +<BlockedClientList> +<string name="ClientMacAddress">00:11:22:33:44:55</string> +</BlockedClientList> +<AllowedClientList> +<string name="ClientMacAddress">aa:bb:cc:dd:ee:ff</string> +</AllowedClientList> +</SoftAp> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml new file mode 100644 index 000000000000..67d5aab215f2 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml @@ -0,0 +1,81 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<NetworkList /> +<PasspointConfigData> +<ProviderList> +<Provider> +<long name="ProviderID" value="0" /> +<int name="CreatorUID" value="10085" /> +<string name="PackageName">com.android.certinstaller</string> +<list name="CaCertificateAliases"> +<string>HS2_0_0</string> +</list> +<null name="ClientPrivateKeyAlias" /> +<boolean name="HasEverConnected" value="false" /> +<boolean name="IsFromSuggestion" value="false" /> +<boolean name="IsTrusted" value="true" /> +<Configuration> +<int name="UpdateIdentifier" value="-2147483648" /> +<int name="CredentialPriority" value="-2147483648" /> +<null name="TrustRootCertList" /> +<long name="SubscriptionCreationTime" value="-9223372036854775808" /> +<long name="SubscriptionExpirationTime" value="-9223372036854775808" /> +<null name="SubscriptionType" /> +<long name="UsageLimitTimePeriod" value="-9223372036854775808" /> +<long name="UsageLimitStartTime" value="-9223372036854775808" /> +<long name="UsageLimitDataLimit" value="-9223372036854775808" /> +<long name="UsageLimitTimeLimit" value="-9223372036854775808" /> +<HomeSP> +<string name="FQDN">Passpoint.net</string> +<string name="FriendlyName">Passpoint Friendly Name</string> +<null name="IconURL" /> +<null name="HomeNetworkIDs" /> +<null name="MatchAllOIs" /> +<null name="MatchAnyOIs" /> +<null name="OtherHomePartners" /> +<null name="RoamingConsortiumOIs" /> +</HomeSP> +<Credential> +<long name="CreationTime" value="-9223372036854775808" /> +<long name="ExpirationTime" value="-9223372036854775808" /> +<string name="Realm">passpoint.com</string> +<boolean name="CheckAAAServerCertStatus" value="false" /> +<UserCredential> +<string name="Username">blahblahblah</string> +<string name="Password">doubleblahlah</string> +<boolean name="MachineManaged" value="true" /> +<null name="SoftTokenApp" /> +<boolean name="AbleToShare" value="false" /> +<int name="EAPType" value="21" /> +<string name="NonEAPInnerMethod">PAP</string> +</UserCredential> +</Credential> +<int name="CarrierId" value="-1" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="IsMacRandomizationEnabled" value="true" /> +<int name="MeteredOverride" value="0" /> +</Configuration> +<null name="RemediationCaCertificateAlias" /> +</Provider> +</ProviderList> +</PasspointConfigData> +<OpenNetworkNotifierBlacklistConfigData /> +<NetworkRequestMap> +<ApprovedAccessPointsPerApp> +<string name="RequestorPackageName">com.android.cts.verifier</string> +<AccessPoint> +<string name="SSID">OPEN_SSID</string> +<string name="BSSID">00:11:22:33:44:55</string> +<int name="NetworkType" value="0" /> +</AccessPoint> +</ApprovedAccessPointsPerApp> +</NetworkRequestMap> +<WakeupConfigStoreData> +<FeatureState> +<boolean name="IsActive" value="false" /> +<boolean name="IsOnboarded" value="false" /> +<int name="NotificationsShown" value="1" /> +</FeatureState> +</WakeupConfigStoreData> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml new file mode 100644 index 000000000000..4ecdd29709b4 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml @@ -0,0 +1,155 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<NetworkSuggestionMap> +<NetworkSuggestionPerApp> +<string name="SuggestorPackageName">com.android.cts.verifier</string> +<null name="SuggestorFeatureId" /> +<boolean name="SuggestorHasUserApproved" value="true" /> +<int name="SuggestorMaxSize" value="1" /> +<int name="SuggestorUid" value="10228" /> +<int name="SuggestorCarrierId" value="-1" /> +<NetworkSuggestion> +<WifiConfiguration> +<string name="ConfigKey">"OPEN_SSID"NONE</string> +<string name="SSID">"OPEN_SSID"</string> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="1">01</byte-array> +<byte-array name="AllowedProtocols" num="0"></byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="0"></byte-array> +<byte-array name="AllowedPairwiseCiphers" num="0"></byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="0"></byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="0" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="1" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="10228" /> +<string name="CreatorName">com.android.cts.verifier</string> +<int name="LastUpdateUid" value="-1" /> +<null name="LastUpdateName" /> +<int name="LastConnectUid" value="0" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">02:00:00:00:00:00</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<boolean name="IsAppInteractionRequired" value="false" /> +<boolean name="IsUserInteractionRequired" value="false" /> +<boolean name="IsUserAllowedToManuallyConnect" value="false" /> +<boolean name="InitializedAutoJoinEnabled" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +</NetworkSuggestion> +<NetworkSuggestion> +<WifiConfiguration> +<string name="ConfigKey">passpoint.net</string> +<null name="SSID" /> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="0"></byte-array> +<byte-array name="AllowedProtocols" num="0"></byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="0"></byte-array> +<byte-array name="AllowedPairwiseCiphers" num="0"></byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="0"></byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="0" /> +<string name="FQDN">passpoint.net</string> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">com.android.cts.verifier</string> +<int name="LastUpdateUid" value="-1" /> +<null name="LastUpdateName" /> +<int name="LastConnectUid" value="0" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">02:00:00:00:00:00</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +<boolean name="IsMostRecentlyConnected" value="false" /> +</WifiConfiguration> +<PasspointConfiguration> +<int name="UpdateIdentifier" value="-2147483648" /> +<int name="CredentialPriority" value="-2147483648" /> +<null name="TrustRootCertList" /> +<long name="SubscriptionCreationTime" value="-9223372036854775808" /> +<long name="SubscriptionExpirationTime" value="-9223372036854775808" /> +<null name="SubscriptionType" /> +<long name="UsageLimitTimePeriod" value="-9223372036854775808" /> +<long name="UsageLimitStartTime" value="-9223372036854775808" /> +<long name="UsageLimitDataLimit" value="-9223372036854775808" /> +<long name="UsageLimitTimeLimit" value="-9223372036854775808" /> +<HomeSP> +<string name="FQDN">passpoint.net</string> +<string name="FriendlyName">Passpoint Friendly Name</string> +<null name="IconURL" /> +<null name="HomeNetworkIDs" /> +<null name="MatchAllOIs" /> +<null name="MatchAnyOIs" /> +<null name="OtherHomePartners" /> +<null name="RoamingConsortiumOIs" /> +</HomeSP> +<Credential> +<long name="CreationTime" value="-9223372036854775808" /> +<long name="ExpirationTime" value="-9223372036854775808" /> +<string name="Realm">passpoint.com</string> +<boolean name="CheckAAAServerCertStatus" value="false" /> +<UserCredential> +<string name="Username">blahblahblah</string> +<string name="Password">doubleblahblah</string> +<boolean name="MachineManaged" value="false" /> +<null name="SoftTokenApp" /> +<boolean name="AbleToShare" value="false" /> +<int name="EAPType" value="21" /> +<string name="NonEAPInnerMethod">PAP</string> +</UserCredential> +</Credential> +<int name="CarrierId" value="-1" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="IsMacRandomizationEnabled" value="true" /> +<int name="MeteredOverride" value="0" /> +</PasspointConfiguration> +<boolean name="IsAppInteractionRequired" value="false" /> +<boolean name="IsUserInteractionRequired" value="false" /> +<boolean name="IsUserAllowedToManuallyConnect" value="true" /> +<boolean name="InitializedAutoJoinEnabled" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +</NetworkSuggestion> +</NetworkSuggestionPerApp> +</NetworkSuggestionMap> +<ImsiPrivacyProtectionExemptionMap> +<map name="CarrierExemptionMap" /> +</ImsiPrivacyProtectionExemptionMap> +</WifiConfigStoreData> |