summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rwxr-xr-xapi/system-current.txt3
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/statsd/src/stats_log_util.cpp25
-rw-r--r--core/java/android/app/ApplicationPackageManager.java17
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java6
-rw-r--r--core/java/android/content/ContentProvider.java4
-rw-r--r--core/java/android/content/ContentResolver.java22
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java30
-rw-r--r--core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl2
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRenderService.java18
-rw-r--r--core/java/android/service/storage/ExternalStorageService.java33
-rw-r--r--core/java/android/service/storage/IExternalStorageService.aidl3
-rw-r--r--core/java/android/view/InsetsSource.java26
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java73
-rw-r--r--core/java/com/android/internal/widget/IMessagingLayout.java5
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java47
-rw-r--r--core/java/com/android/internal/widget/MessagingImageMessage.java30
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java12
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml25
-rw-r--r--core/res/res/values/dimens.xml4
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java19
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceTest.java42
-rw-r--r--media/java/android/media/MediaFormat.java2
-rw-r--r--media/jni/soundpool/StreamManager.cpp6
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java10
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java21
-rw-r--r--packages/SystemUI/res/layout/notification_conversation_info.xml271
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java256
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java325
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java37
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java44
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java147
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java17
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java1
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java32
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java32
-rw-r--r--services/core/java/com/android/server/wm/Task.java40
-rw-r--r--services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp20
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java2
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java2
-rw-r--r--tools/stats_log_api_gen/Collation.cpp7
-rw-r--r--tools/stats_log_api_gen/Collation.h2
-rw-r--r--tools/stats_log_api_gen/atoms_info_writer.cpp32
-rwxr-xr-xwifi/java/android/net/wifi/WifiMigration.java10
-rw-r--r--wifi/java/android/net/wifi/migration_samples/README.txt35
-rw-r--r--wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml200
-rw-r--r--wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml22
-rw-r--r--wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml81
-rw-r--r--wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml155
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">&quot;OPEN_SSID&quot;NONE</string>
+<string name="SSID">&quot;OPEN_SSID&quot;</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">&quot;ENTERPRISE_SSID&quot;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">&quot;ENTERPRISE_SSID&quot;WPA_EAP</string>
+<string name="SSID">&quot;ENTERPRISE_SSID&quot;</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">&quot;WPA3_SSID&quot;SAE</string>
+<string name="SSID">&quot;WPA3_SSID&quot;</string>
+<string name="PreSharedKey">&quot;sfsdfsfdsfsdf&quot;</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">&quot;OPEN_SSID&quot;NONE</string>
+<string name="SSID">&quot;OPEN_SSID&quot;</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>