diff options
47 files changed, 702 insertions, 252 deletions
diff --git a/api/current.txt b/api/current.txt index f720e191d6d6..661b9c6ed827 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5335,6 +5335,7 @@ package android.app { field public static final String EXTRA_TITLE = "android.title"; field public static final String EXTRA_TITLE_BIG = "android.title.big"; field public static final int FLAG_AUTO_CANCEL = 16; // 0x10 + field public static final int FLAG_BUBBLE = 4096; // 0x1000 field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40 field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200 field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80 @@ -38478,8 +38479,8 @@ package android.provider { public final class MediaStore { ctor public MediaStore(); - method @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri); + method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context); method public static android.net.Uri getMediaScannerUri(); method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri); method @NonNull public static String getVersion(@NonNull android.content.Context); @@ -38523,6 +38524,7 @@ package android.provider { field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service"; field public static final String UNKNOWN_STRING = "<unknown>"; field public static final String VOLUME_EXTERNAL = "external"; + field public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary"; field public static final String VOLUME_INTERNAL = "internal"; } @@ -38771,6 +38773,7 @@ package android.provider { field public static final String RELATIVE_PATH = "relative_path"; field public static final String SIZE = "_size"; field public static final String TITLE = "title"; + field public static final String VOLUME_NAME = "volume_name"; field public static final String WIDTH = "width"; } diff --git a/api/removed.txt b/api/removed.txt index fe3e866de682..70ff50ed40a6 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -514,6 +514,7 @@ package android.provider { public final class MediaStore { method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams); + method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f7608f5320e8..82d177aeede2 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3251,13 +3251,12 @@ message BinaryPushStateChanged { INSTALLER_ROLLBACK_BOOT_TRIGGERED_FAILURE = 14; INSTALLER_ROLLBACK_SUCCESS = 15; INSTALLER_ROLLBACK_FAILURE = 16; - INSTALLER_ROLLBACK_CANCEL_STAGED_REMOVE_FROM_QUEUE = 17; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_INITIATED = 18; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_SUCCESS = 19; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_FAILURE = 20; - INSTALL_STAGED_CANCEL_REQUESTED = 21; - INSTALL_STAGED_CANCEL_SUCCESS = 22; - INSTALL_STAGED_CANCEL_FAILURE = 23; + INSTALLER_ROLLBACK_STAGED_CANCEL_REQUESTED = 17; + INSTALLER_ROLLBACK_STAGED_CANCEL_SUCCESS = 18; + INSTALLER_ROLLBACK_STAGED_CANCEL_FAILURE = 19; + INSTALL_STAGED_CANCEL_REQUESTED = 20; + INSTALL_STAGED_CANCEL_SUCCESS = 21; + INSTALL_STAGED_CANCEL_FAILURE = 22; } optional State state = 6; // Possible experiment ids for monitoring this push. @@ -5748,13 +5747,12 @@ message TrainInfo { INSTALLER_ROLLBACK_BOOT_TRIGGERED_FAILURE = 14; INSTALLER_ROLLBACK_SUCCESS = 15; INSTALLER_ROLLBACK_FAILURE = 16; - INSTALLER_ROLLBACK_CANCEL_STAGED_REMOVE_FROM_QUEUE = 17; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_INITIATED = 18; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_SUCCESS = 19; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_FAILURE = 20; - INSTALL_STAGED_CANCEL_REQUESTED = 21; - INSTALL_STAGED_CANCEL_SUCCESS = 22; - INSTALL_STAGED_CANCEL_FAILURE = 23; + INSTALLER_ROLLBACK_STAGED_CANCEL_REQUESTED = 17; + INSTALLER_ROLLBACK_STAGED_CANCEL_SUCCESS = 18; + INSTALLER_ROLLBACK_STAGED_CANCEL_FAILURE = 19; + INSTALL_STAGED_CANCEL_REQUESTED = 20; + INSTALL_STAGED_CANCEL_SUCCESS = 21; + INSTALL_STAGED_CANCEL_FAILURE = 22; } optional Status status = 4; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0ab1a85372f5..a90185cf1468 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -618,9 +618,11 @@ public class Notification implements Parcelable public static final int FLAG_CAN_COLORIZE = 0x00000800; /** - * Bit to be bitswised-ored into the {@link #flags} field that should be - * set if this notification can be shown as a bubble. - * @hide + * Bit to be bitswised-ored into the {@link #flags} field that should be set if this + * notification is showing as a bubble. This will be set by the system if it is determined + * that your notification is allowed to be a bubble. + * + * @see {@link Notification.Builder#setBubbleMetadata(BubbleMetadata)} */ public static final int FLAG_BUBBLE = 0x00001000; @@ -3578,9 +3580,9 @@ public class Notification implements Parcelable * <p>This data will be ignored unless the notification is posted to a channel that * allows {@link NotificationChannel#canBubble() bubbles}.</p> * - * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state - * outside of the notification shade on unlocked devices. When a user interacts with the - * collapsed state, the bubble intent will be invoked and displayed.</b> + * <p>Notifications allowed to bubble that have valid bubble metadata will display in + * collapsed state outside of the notification shade on unlocked devices. When a user + * interacts with the collapsed state, the bubble intent will be invoked and displayed.</p> */ @NonNull public Builder setBubbleMetadata(@Nullable BubbleMetadata data) { diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 014bc242e17a..3523e956656a 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -52,7 +52,7 @@ public class SQLiteQueryBuilder { private static final Pattern sLimitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); private static final Pattern sAggregationPattern = Pattern.compile( - "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL)\\((.+)\\)"); + "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL|GROUP_CONCAT)\\((.+)\\)"); private Map<String, String> mProjectionMap = null; private List<Pattern> mProjectionGreylist = null; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 075b650ed8f4..080ff7301ed2 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1130,7 +1130,7 @@ public class StorageManager { public @NonNull StorageVolume getStorageVolume(@NonNull Uri uri) { final String volumeName = MediaStore.getVolumeName(uri); switch (volumeName) { - case MediaStore.VOLUME_EXTERNAL: + case MediaStore.VOLUME_EXTERNAL_PRIMARY: return getPrimaryStorageVolume(); default: for (StorageVolume vol : getStorageVolumes()) { diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 225ecfa315aa..6280600823d7 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -265,8 +265,13 @@ public final class StorageVolume implements Parcelable { } /** {@hide} */ + public static @Nullable String normalizeUuid(@Nullable String fsUuid) { + return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null; + } + + /** {@hide} */ public @Nullable String getNormalizedUuid() { - return mFsUuid != null ? mFsUuid.toLowerCase(Locale.US) : null; + return normalizeUuid(mFsUuid); } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 0b1647d05ef4..da19d59367a0 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -102,20 +102,40 @@ public final class MediaStore { public static final @NonNull Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); /** - * Volume name used for content on "internal" storage of device. This - * volume contains media distributed with the device, such as built-in - * ringtones and wallpapers. + * Synthetic volume name that provides a view of all content across the + * "internal" storage of the device. + * <p> + * This synthetic volume provides a merged view of all media distributed + * with the device, such as built-in ringtones and wallpapers. + * <p> + * Because this is a synthetic volume, you can't insert new content into + * this volume. */ public static final String VOLUME_INTERNAL = "internal"; /** - * Volume name used for content on "external" storage of device. This only - * includes media on the primary shared storage device; the contents of any - * secondary storage devices can be obtained using - * {@link #getAllVolumeNames(Context)}. + * Synthetic volume name that provides a view of all content across the + * "external" storage of the device. + * <p> + * This synthetic volume provides a merged view of all media across all + * currently attached external storage devices. + * <p> + * Because this is a synthetic volume, you can't insert new content into + * this volume. Instead, you can insert content into a specific storage + * volume obtained from {@link #getExternalVolumeNames(Context)}. */ public static final String VOLUME_EXTERNAL = "external"; + /** + * Specific volume name that represents the primary external storage device + * at {@link Environment#getExternalStorageDirectory()}. + * <p> + * This volume may not always be available, such as when the user has + * ejected the device. You can find a list of all specific volume names + * using {@link #getExternalVolumeNames(Context)}. + */ + public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary"; + /** {@hide} */ public static final String SCAN_FILE_CALL = "scan_file"; /** {@hide} */ @@ -1037,6 +1057,16 @@ public final class MediaStore { public static final String OWNER_PACKAGE_NAME = "owner_package_name"; /** + * Volume name of the specific storage device where this media item is + * persisted. The value is typically one of the volume names returned + * from {@link MediaStore#getExternalVolumeNames(Context)}. + * <p> + * This is a read-only column that is automatically computed. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String VOLUME_NAME = "volume_name"; + + /** * Relative path of this media item within the storage device where it * is persisted. For example, an item stored at * {@code /storage/0000-0000/DCIM/Vacation/IMG1024.JPG} would have a @@ -1408,7 +1438,7 @@ public final class MediaStore { final StorageVolume sv = sm.getStorageVolume(path); if (sv != null) { if (sv.isPrimary()) { - return VOLUME_EXTERNAL; + return VOLUME_EXTERNAL_PRIMARY; } else { return checkArgumentVolumeName(sv.getNormalizedUuid()); } @@ -1710,7 +1740,7 @@ public final class MediaStore { String stringUrl = null; /* value to be returned */ try { - url = cr.insert(EXTERNAL_CONTENT_URI, values); + url = cr.insert(getContentUri(VOLUME_EXTERNAL_PRIMARY), values); if (source != null) { try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( @@ -3224,22 +3254,29 @@ public final class MediaStore { } } + /** @removed */ + @Deprecated + public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) { + return getExternalVolumeNames(context); + } + /** - * Return list of all volume names currently available. This includes a - * unique name for each shared storage device that is currently mounted. + * Return list of all specific volume names that make up + * {@link #VOLUME_EXTERNAL}. This includes a unique volume name for each + * shared storage device that is currently attached, which typically + * includes {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}. * <p> - * Each name can be passed to APIs like - * {@link MediaStore.Images.Media#getContentUri(String)} to query media at - * that location. + * Each specific volume name can be passed to APIs like + * {@link MediaStore.Images.Media#getContentUri(String)} to interact with + * media on that storage device. */ - public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) { + public static @NonNull Set<String> getExternalVolumeNames(@NonNull Context context) { final StorageManager sm = context.getSystemService(StorageManager.class); final Set<String> volumeNames = new ArraySet<>(); - volumeNames.add(VOLUME_INTERNAL); for (VolumeInfo vi : sm.getVolumes()) { if (vi.isVisibleForUser(UserHandle.myUserId()) && vi.isMountedReadable()) { if (vi.isPrimary()) { - volumeNames.add(VOLUME_EXTERNAL); + volumeNames.add(VOLUME_EXTERNAL_PRIMARY); } else { volumeNames.add(vi.getNormalizedFsUuid()); } @@ -3270,6 +3307,8 @@ public final class MediaStore { return volumeName; } else if (VOLUME_EXTERNAL.equals(volumeName)) { return volumeName; + } else if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName)) { + return volumeName; } // When not one of the well-known values above, it must be a hex UUID @@ -3285,8 +3324,9 @@ public final class MediaStore { } /** - * Return path where the given volume is mounted. Not valid for - * {@link #VOLUME_INTERNAL}. + * Return path where the given specific volume is mounted. Not valid for + * {@link #VOLUME_INTERNAL} or {@link #VOLUME_EXTERNAL}, since those are + * broad collections that cover many paths. * * @hide */ @@ -3297,8 +3337,12 @@ public final class MediaStore { throw new IllegalArgumentException(); } - if (VOLUME_EXTERNAL.equals(volumeName)) { - return Environment.getExternalStorageDirectory(); + switch (volumeName) { + case VOLUME_INTERNAL: + case VOLUME_EXTERNAL: + throw new FileNotFoundException(volumeName + " has no associated path"); + case VOLUME_EXTERNAL_PRIMARY: + return Environment.getExternalStorageDirectory(); } final StorageManager sm = AppGlobals.getInitialApplication() @@ -3328,23 +3372,31 @@ public final class MediaStore { throw new IllegalArgumentException(); } + final Context context = AppGlobals.getInitialApplication(); + final UserManager um = context.getSystemService(UserManager.class); + final ArrayList<File> res = new ArrayList<>(); if (VOLUME_INTERNAL.equals(volumeName)) { - addCanoncialFile(res, new File(Environment.getRootDirectory(), "media")); - addCanoncialFile(res, new File(Environment.getOemDirectory(), "media")); - addCanoncialFile(res, new File(Environment.getProductDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getRootDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getOemDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getProductDirectory(), "media")); + } else if (VOLUME_EXTERNAL.equals(volumeName)) { + for (String exactVolume : getExternalVolumeNames(context)) { + addCanonicalFile(res, getVolumePath(exactVolume)); + } + if (um.isDemoUser()) { + addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory()); + } } else { - addCanoncialFile(res, getVolumePath(volumeName)); - final UserManager um = AppGlobals.getInitialApplication() - .getSystemService(UserManager.class); - if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) { - addCanoncialFile(res, Environment.getDataPreloadsMediaDirectory()); + addCanonicalFile(res, getVolumePath(volumeName)); + if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName) && um.isDemoUser()) { + addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory()); } } return res; } - private static void addCanoncialFile(List<File> list, File file) { + private static void addCanonicalFile(List<File> list, File file) { try { list.add(file.getCanonicalFile()); } catch (IOException e) { @@ -3382,12 +3434,12 @@ public final class MediaStore { * <p> * No other assumptions should be made about the meaning of the version. * <p> - * This method returns the version for {@link MediaStore#VOLUME_EXTERNAL}; - * to obtain a version for a different volume, use - * {@link #getVersion(Context, String)}. + * This method returns the version for + * {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}; to obtain a version for a + * different volume, use {@link #getVersion(Context, String)}. */ public static @NonNull String getVersion(@NonNull Context context) { - return getVersion(context, VOLUME_EXTERNAL); + return getVersion(context, VOLUME_EXTERNAL_PRIMARY); } /** @@ -3401,7 +3453,7 @@ public final class MediaStore { * * @param volumeName specific volume to obtain an opaque version string for. * Must be one of the values returned from - * {@link #getAllVolumeNames(Context)}. + * {@link #getExternalVolumeNames(Context)}. */ public static @NonNull String getVersion(@NonNull Context context, @NonNull String volumeName) { final ContentResolver resolver = context.getContentResolver(); diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index f9b629c85fb0..1fc7f0e36095 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -416,23 +416,8 @@ public abstract class LayoutInflater { } private void initPrecompiledViews() { - // Use the device config if enabled, otherwise default to the system property. - String usePrecompiledLayout = null; - try { - usePrecompiledLayout = DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_RUNTIME, - USE_PRECOMPILED_LAYOUT); - } catch (Exception e) { - // May be caused by permission errors reading the property (i.e. instant apps). - } + // Precompiled layouts are not supported in this release. boolean enabled = false; - if (TextUtils.isEmpty(usePrecompiledLayout)) { - enabled = SystemProperties.getBoolean( - USE_PRECOMPILED_LAYOUT, - false); - } else { - enabled = Boolean.parseBoolean(usePrecompiledLayout); - } initPrecompiledViews(enabled); } diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java index cf171d738524..6f9d4d30909f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureCondition.java +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -54,7 +54,9 @@ public final class ContentCaptureCondition implements Parcelable { * * @param locusId id of the condition, as defined by * {@link ContentCaptureContext#getLocusId()}. - * @param flags either {@link ContentCaptureCondition#FLAG_IS_REGEX} or {@code 0}. + * @param flags either {@link ContentCaptureCondition#FLAG_IS_REGEX} (to use a regular + * expression match) or {@code 0} (in which case the {@code LocusId} must be an exact match of + * the {@code LocusId} used in the {@link ContentCaptureContext}). */ public ContentCaptureCondition(@NonNull LocusId locusId, @Flags int flags) { this.mLocusId = Preconditions.checkNotNull(locusId); diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java index f2d878a8bf54..b8cb7bed8712 100644 --- a/core/java/android/view/textclassifier/ConversationAction.java +++ b/core/java/android/view/textclassifier/ConversationAction.java @@ -200,13 +200,11 @@ public final class ConversationAction implements Parcelable { /** * Returns the extended data related to this conversation action. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** Builder class to construct {@link ConversationAction}. */ diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index dc7521296e9f..eddc672dc87f 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -214,13 +214,11 @@ public final class ConversationActions implements Parcelable { /** * Returns the extended data related to this conversation action. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** Builder class to construct a {@link Message} */ diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 9ede8fbd176e..4c4cb55cd3a0 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -265,13 +265,11 @@ public final class TextClassification implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } @Override @@ -635,13 +633,11 @@ public final class TextClassification implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index cde27a08fc79..c815f63b1708 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -125,13 +125,11 @@ public final class TextLinks implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** @@ -413,13 +411,11 @@ public final class TextLinks implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 52989397e473..e378e65aa29e 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -112,13 +112,11 @@ public final class TextSelection implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } @Override @@ -296,13 +294,11 @@ public final class TextSelection implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 51303f70f878..eeca73234c30 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2592,10 +2592,8 @@ public class ChooserActivity extends ResolverActivity { if (startType != lastStartType || rowPosition == getContentPreviewRowCount()) { row.setBackground(mChooserRowLayer); - setVertPadding(row, 0, 0); } else { row.setBackground(null); - setVertPadding(row, 0, 0); } int columnCount = holder.getColumnCount(); @@ -2642,10 +2640,6 @@ public class ChooserActivity extends ResolverActivity { } } - private void setVertPadding(ViewGroup row, int top, int bottom) { - row.setPadding(row.getPaddingLeft(), top, row.getPaddingRight(), bottom); - } - int getFirstRowPosition(int row) { row -= getContentPreviewRowCount(); diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp index 29051f14c72f..1c247cbb7743 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -19,6 +19,7 @@ #include <inttypes.h> #include <string.h> #include <algorithm> +#include <array> #include <memory> #include <vector> #include <cmath> @@ -976,6 +977,153 @@ static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8 return OK; } +static void undistort(/*inout*/double& x, /*inout*/double& y, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f) { + double xp = (x - cx) / f; + double yp = (y - cy) / f; + + double x2 = xp * xp; + double y2 = yp * yp; + double r2 = x2 + y2; + double xy2 = 2.0 * xp * yp; + + const float k0 = distortion[0]; + const float k1 = distortion[1]; + const float k2 = distortion[2]; + const float k3 = distortion[3]; + const float p1 = distortion[4]; + const float p2 = distortion[5]; + + double kr = k0 + ((k3 * r2 + k2) * r2 + k1) * r2; + double xpp = xp * kr + p1 * xy2 + p2 * (r2 + 2.0 * x2); + double ypp = yp * kr + p1 * (r2 + 2.0 * y2) + p2 * xy2; + + x = xpp * f + cx; + y = ypp * f + cy; + return; +} + +static inline bool unDistortWithinPreCorrArray( + double x, double y, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f, + int preCorrW, int preCorrH) { + undistort(x, y, distortion, cx, cy, f); + if (x < 0.0 || y < 0.0 || x > preCorrW - 1 || y > preCorrH - 1) { + return false; + } + return true; +} + +static inline bool boxWithinPrecorrectionArray( + int left, int top, int right, int bottom, + const std::array<float, 6>& distortion, + const float& cx, const float& cy, const float& f, + const int& preCorrW, const int& preCorrH){ + // Top row + if (!unDistortWithinPreCorrArray(left, top, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(cx, top, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(right, top, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + // Middle row + if (!unDistortWithinPreCorrArray(left, cy, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(right, cy, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + // Bottom row + if (!unDistortWithinPreCorrArray(left, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(cx, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(right, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + return true; +} + +static inline bool scaledBoxWithinPrecorrectionArray( + double scale/*must be <= 1.0*/, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f, + const int preCorrW, const int preCorrH){ + + double left = cx * (1.0 - scale); + double right = (preCorrW - 1) * scale + cx * (1.0 - scale); + double top = cy * (1.0 - scale); + double bottom = (preCorrH - 1) * scale + cy * (1.0 - scale); + + return boxWithinPrecorrectionArray(left, top, right, bottom, + distortion, cx, cy, f, preCorrW, preCorrH); +} + +static status_t findPostCorrectionScale( + double stepSize, double minScale, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f, + const int preCorrW, const int preCorrH, + /*out*/ double* outScale) { + if (outScale == nullptr) { + ALOGE("%s: outScale must not be null", __FUNCTION__); + return BAD_VALUE; + } + + for (double scale = 1.0; scale > minScale; scale -= stepSize) { + if (scaledBoxWithinPrecorrectionArray( + scale, distortion, cx, cy, f, preCorrW, preCorrH)) { + *outScale = scale; + return OK; + } + } + ALOGE("%s: cannot find cropping scale for lens distortion: stepSize %f, minScale %f", + __FUNCTION__, stepSize, minScale); + return BAD_VALUE; +} + +// Apply a scale factor to distortion coefficients so that the image is zoomed out and all pixels +// are sampled within the precorrection array +static void normalizeLensDistortion( + /*inout*/std::array<float, 6>& distortion, + float cx, float cy, float f, int preCorrW, int preCorrH) { + ALOGV("%s: distortion [%f, %f, %f, %f, %f, %f], (cx,cy) (%f, %f), f %f, (W,H) (%d, %d)", + __FUNCTION__, distortion[0], distortion[1], distortion[2], + distortion[3], distortion[4], distortion[5], + cx, cy, f, preCorrW, preCorrH); + + // Only update distortion coeffients if we can find a good bounding box + double scale = 1.0; + if (OK == findPostCorrectionScale(0.002, 0.5, + distortion, cx, cy, f, preCorrW, preCorrH, + /*out*/&scale)) { + ALOGV("%s: scaling distortion coefficients by %f", __FUNCTION__, scale); + // The formula: + // xc = xi * (k0 + k1*r^2 + k2*r^4 + k3*r^6) + k4 * (2*xi*yi) + k5 * (r^2 + 2*xi^2) + // To create effective zoom we want to replace xi by xi *m, yi by yi*m and r^2 by r^2*m^2 + // Factor the extra m power terms into k0~k6 + std::array<float, 6> scalePowers = {1, 3, 5, 7, 2, 2}; + for (size_t i = 0; i < 6; i++) { + distortion[i] *= pow(scale, scalePowers[i]); + } + } + return; +} + // ---------------------------------------------------------------------------- extern "C" { @@ -1086,9 +1234,9 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]); if (!((imageWidth == preWidth && imageHeight == preHeight) || - (imageWidth == pixWidth && imageHeight == pixHeight))) { + (imageWidth == pixWidth && imageHeight == pixHeight))) { jniThrowException(env, "java/lang/AssertionError", - "Height and width of imate buffer did not match height and width of" + "Height and width of image buffer did not match height and width of" "either the preCorrectionActiveArraySize or the pixelArraySize."); return nullptr; } @@ -1793,7 +1941,7 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image status_t err = OK; // Set up rectilinear distortion correction - float distortion[6] {1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array<float, 6> distortion = {1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; bool gotDistortion = false; camera_metadata_entry entry4 = @@ -1810,6 +1958,19 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image results.find(ANDROID_LENS_DISTORTION); if (entry3.count == 5) { gotDistortion = true; + + + // Scale the distortion coefficients to create a zoom in warpped image so that all + // pixels are drawn within input image. + for (size_t i = 0; i < entry3.count; i++) { + distortion[i+1] = entry3.data.f[i]; + } + + // TODO b/118690688: deal with the case where RAW size != preCorrSize + if (preWidth == imageWidth && preHeight == imageHeight) { + normalizeLensDistortion(distortion, cx, cy, f, preWidth, preHeight); + } + float m_x = std::fmaxf(preWidth-1 - cx, cx); float m_y = std::fmaxf(preHeight-1 - cy, cy); float m_sq = m_x*m_x + m_y*m_y; @@ -1831,7 +1992,7 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image m / f }; for (size_t i = 0; i < entry3.count; i++) { - distortion[i+1] = convCoeff[i] * entry3.data.f[i]; + distortion[i+1] *= convCoeff[i]; } } else { entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION); @@ -1859,8 +2020,8 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image } } if (gotDistortion) { - err = builder.addWarpRectilinearForMetadata(distortion, preWidth, preHeight, cx, - cy); + err = builder.addWarpRectilinearForMetadata( + distortion.data(), preWidth, preHeight, cx, cy); if (err != OK) { ALOGE("%s: Could not add distortion correction.", __FUNCTION__); jniThrowRuntimeException(env, "failed to add distortion correction."); diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 6bbd2587d78d..37e452d72c9a 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -28,4 +28,6 @@ <!-- The background color of a notification card. --> <color name="notification_material_background_color">@color/black</color> -</resources>
\ No newline at end of file + + <color name="chooser_row_divider">@color/list_divider_color_dark</color> +</resources> diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index e35b750272da..98f209d3f401 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -71,4 +71,8 @@ easier. <style name="ThemeOverlay.DeviceDefault.Accent.DayNight" parent="@style/ThemeOverlay.DeviceDefault.Accent" /> + <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon"> + <item name="windowLightNavigationBar">false</item> + </style> + </resources> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index e9b1bd3af0dc..ef26cd74cde0 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -215,6 +215,5 @@ <!-- Magnifier --> <color name="default_magnifier_color_overlay">#00FFFFFF</color> - <color name="chooser_row_divider">#1f000000</color> - + <color name="chooser_row_divider">@color/list_divider_color_light</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0163fc0e20ce..fe4fd133dbef 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1031,6 +1031,9 @@ <!-- Boolean indicating whether display white balance is supported. --> <bool name="config_displayWhiteBalanceAvailable">false</bool> + <!-- Boolean indicating whether display white balance should be enabled by default. --> + <bool name="config_displayWhiteBalanceEnabledDefault">false</bool> + <!-- Minimum color temperature, in Kelvin, supported by display white balance. --> <integer name="config_displayWhiteBalanceColorTemperatureMin">4000</integer> diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml index 5c446d667015..f22a91ff75c1 100644 --- a/core/res/res/values/dimens_car.xml +++ b/core/res/res/values/dimens_car.xml @@ -66,9 +66,10 @@ <dimen name="car_padding_0">4dp</dimen> <dimen name="car_padding_1">8dp</dimen> <dimen name="car_padding_2">16dp</dimen> - <dimen name="car_padding_3">28dp</dimen> + <dimen name="car_padding_3">24dp</dimen> <dimen name="car_padding_4">32dp</dimen> <dimen name="car_padding_5">64dp</dimen> + <dimen name="car_padding_6">96dp</dimen> <!-- Radius --> <dimen name="car_radius_1">4dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fb72da519f53..dc8c62c9489e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3150,7 +3150,6 @@ <java-symbol type="bool" name="config_setColorTransformAccelerated" /> <java-symbol type="bool" name="config_setColorTransformAcceleratedPerLayer" /> - <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" /> <java-symbol type="bool" name="config_nightDisplayAvailable" /> <java-symbol type="bool" name="config_allowDisablingAssistDisclosure" /> <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" /> @@ -3162,8 +3161,8 @@ <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" /> <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" /> <java-symbol type="array" name="config_availableColorModes" /> - <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" /> + <java-symbol type="bool" name="config_displayWhiteBalanceEnabledDefault" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMax" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 9f20ee6e19a0..78a8db420a67 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1658,7 +1658,7 @@ easier. <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" /> <!-- Theme used for the intent picker activity. --> - <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.DayNight"> + <style name="Theme.DeviceDefault.ResolverCommon" parent="Theme.DeviceDefault.DayNight"> <item name="windowEnterTransition">@empty</item> <item name="windowExitTransition">@empty</item> <item name="windowIsTranslucent">true</item> @@ -1670,6 +1670,12 @@ easier. <item name="colorControlActivated">?attr/colorControlHighlight</item> <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item> <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item> + <item name="navigationBarColor">?attr/colorBackgroundFloating</item> + <item name="navigationBarDividerColor">@color/chooser_row_divider</item> + </style> + + <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon"> + <item name="windowLightNavigationBar">true</item> </style> <!-- @hide DeviceDefault themes for the autofill FillUi --> diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp index c564ed632786..70a5f5acbb6e 100644 --- a/libs/hwui/tests/unit/CommonPoolTests.cpp +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -135,4 +135,48 @@ TEST(CommonPool, fullQueue) { for (auto& f : futures) { f.get(); } +} + +class ObjectTracker { + static std::atomic_int sGlobalCount; + +public: + ObjectTracker() { + sGlobalCount++; + } + ObjectTracker(const ObjectTracker&) { + sGlobalCount++; + } + ObjectTracker(ObjectTracker&&) { + sGlobalCount++; + } + ~ObjectTracker() { + sGlobalCount--; + } + + static int count() { return sGlobalCount.load(); } +}; + +std::atomic_int ObjectTracker::sGlobalCount{0}; + +TEST(CommonPool, asyncLifecycleCheck) { + ASSERT_EQ(0, ObjectTracker::count()); + { + ObjectTracker obj; + ASSERT_EQ(1, ObjectTracker::count()); + EXPECT_LT(1, CommonPool::async([obj] { return ObjectTracker::count(); }).get()); + } + CommonPool::waitForIdle(); + ASSERT_EQ(0, ObjectTracker::count()); +} + +TEST(CommonPool, syncLifecycleCheck) { + ASSERT_EQ(0, ObjectTracker::count()); + { + ObjectTracker obj; + ASSERT_EQ(1, ObjectTracker::count()); + EXPECT_LT(1, CommonPool::runSync([obj] { return ObjectTracker::count(); })); + } + CommonPool::waitForIdle(); + ASSERT_EQ(0, ObjectTracker::count()); }
\ No newline at end of file diff --git a/libs/hwui/thread/CommonPool.cpp b/libs/hwui/thread/CommonPool.cpp index 7f94a152cf8d..d011bdfe945e 100644 --- a/libs/hwui/thread/CommonPool.cpp +++ b/libs/hwui/thread/CommonPool.cpp @@ -49,9 +49,13 @@ CommonPool::CommonPool() { } } -void CommonPool::post(Task&& task) { +CommonPool& CommonPool::instance() { static CommonPool pool; - pool.enqueue(std::move(task)); + return pool; +} + +void CommonPool::post(Task&& task) { + instance().enqueue(std::move(task)); } void CommonPool::enqueue(Task&& task) { @@ -86,5 +90,18 @@ void CommonPool::workerLoop() { } } +void CommonPool::waitForIdle() { + instance().doWaitForIdle(); +} + +void CommonPool::doWaitForIdle() { + std::unique_lock lock(mLock); + while (mWaitingThreads != THREAD_COUNT) { + lock.unlock(); + usleep(100); + lock.lock(); + } +} + } // namespace uirenderer } // namespace android
\ No newline at end of file diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h index aef2990d6343..7603eeef4692 100644 --- a/libs/hwui/thread/CommonPool.h +++ b/libs/hwui/thread/CommonPool.h @@ -57,11 +57,13 @@ public: mHead = newHead; } - constexpr T&& pop() { + constexpr T pop() { LOG_ALWAYS_FATAL_IF(mTail == mHead, "empty"); int index = mTail; mTail = (mTail + 1) % SIZE; - return std::move(mBuffer[index]); + T ret = std::move(mBuffer[index]); + mBuffer[index] = nullptr; + return ret; } private: @@ -95,11 +97,17 @@ public: return task.get_future().get(); }; + // For testing purposes only, blocks until all worker threads are parked. + static void waitForIdle(); + private: + static CommonPool& instance(); + CommonPool(); ~CommonPool() {} void enqueue(Task&&); + void doWaitForIdle(); void workerLoop(); diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java index 74057be8434b..ff40d8e00603 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java @@ -20,14 +20,12 @@ import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; import android.location.SettingInjectorService; import android.os.Bundle; import android.os.Handler; @@ -37,9 +35,9 @@ import android.os.Messenger; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; -import android.util.IconDrawableFactory; import android.util.Log; import android.util.Xml; @@ -56,8 +54,8 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; -import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -157,22 +155,8 @@ public class SettingsInjector { * Adds the InjectedSetting information to a Preference object */ private void populatePreference(Preference preference, InjectedSetting setting) { - final PackageManager pm = mContext.getPackageManager(); - Drawable appIcon = null; - try { - final PackageItemInfo itemInfo = new PackageItemInfo(); - itemInfo.icon = setting.iconId; - itemInfo.packageName = setting.packageName; - final ApplicationInfo appInfo = pm.getApplicationInfo(setting.packageName, - PackageManager.GET_META_DATA); - appIcon = IconDrawableFactory.newInstance(mContext) - .getBadgedIcon(itemInfo, appInfo, setting.mUserHandle.getIdentifier()); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e); - } preference.setTitle(setting.title); preference.setSummary(R.string.loading_injected_setting_summary); - preference.setIcon(appIcon); preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting)); } @@ -182,13 +166,15 @@ public class SettingsInjector { * @param profileId Identifier of the user/profile to obtain the injected settings for or * UserHandle.USER_CURRENT for all profiles associated with current user. */ - public List<Preference> getInjectedSettings(Context prefContext, final int profileId) { + public Map<Integer, List<Preference>> getInjectedSettings(Context prefContext, + final int profileId) { final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); final List<UserHandle> profiles = um.getUserProfiles(); - ArrayList<Preference> prefs = new ArrayList<>(); + final ArrayMap<Integer, List<Preference>> result = new ArrayMap<>(); mSettings.clear(); for (UserHandle userHandle : profiles) { if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) { + final List<Preference> prefs = new ArrayList<>(); Iterable<InjectedSetting> settings = getSettings(userHandle); for (InjectedSetting setting : settings) { Preference preference = createPreference(prefContext, setting); @@ -196,12 +182,14 @@ public class SettingsInjector { prefs.add(preference); mSettings.add(new Setting(setting, preference)); } + if (!prefs.isEmpty()) { + result.put(userHandle.getIdentifier(), prefs); + } } } reloadStatusMessages(); - - return prefs; + return result; } /** @@ -303,28 +291,6 @@ public class SettingsInjector { } /** - * Checks wheteher there is any preference that other apps have injected. - * - * @param profileId Identifier of the user/profile to obtain the injected settings for or - * UserHandle.USER_CURRENT for all profiles associated with current user. - */ - public boolean hasInjectedSettings(final int profileId) { - final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - final List<UserHandle> profiles = um.getUserProfiles(); - final int profileCount = profiles.size(); - for (int i = 0; i < profileCount; ++i) { - final UserHandle userHandle = profiles.get(i); - if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) { - Iterable<InjectedSetting> settings = getSettings(userHandle); - for (InjectedSetting setting : settings) { - return true; - } - } - } - return false; - } - - /** * Reloads the status messages for all the preference items. */ public void reloadStatusMessages() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index b738b576ef2f..70366a839da5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -138,6 +138,7 @@ public class KeyguardClockSwitch extends RelativeLayout { ClockManager clockManager) { super(context, attrs); mStatusBarStateController = statusBarStateController; + mStatusBarState = mStatusBarStateController.getState(); mSysuiColorExtractor = colorExtractor; mClockManager = clockManager; mTransition = new ClockBoundsTransition(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 64e56f955058..bc00b5cf9a45 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -20,8 +20,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -35,6 +37,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager.DockEventListener; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.PluginListener; +import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; @@ -61,6 +64,7 @@ public final class ClockManager { private final ContentResolver mContentResolver; private final SettingsWrapper mSettingsWrapper; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + private final CurrentUserTracker mCurrentUserTracker; /** * Observe settings changes to know when to switch the clock face. @@ -68,9 +72,11 @@ public final class ClockManager { private final ContentObserver mContentObserver = new ContentObserver(mMainHandler) { @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - reload(); + public void onChange(boolean selfChange, Uri uri, int userId) { + super.onChange(selfChange, uri, userId); + if (userId == mCurrentUserTracker.getCurrentUserId()) { + reload(); + } } }; @@ -123,6 +129,12 @@ public final class ClockManager { mPluginManager = pluginManager; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; + mCurrentUserTracker = new CurrentUserTracker(context) { + @Override + public void onUserSwitched(int newUserId) { + reload(); + } + }; mPreviewClocks = new AvailableClocks(); Resources res = context.getResources(); @@ -203,10 +215,11 @@ public final class ClockManager { mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), - false, mContentObserver); + false, mContentObserver, UserHandle.USER_ALL); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), - false, mContentObserver); + false, mContentObserver, UserHandle.USER_ALL); + mCurrentUserTracker.startTracking(); if (mDockManager == null) { mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class); } @@ -218,6 +231,7 @@ public final class ClockManager { private void unregister() { mPluginManager.removePluginListener(mPreviewClocks); mContentResolver.unregisterContentObserver(mContentObserver); + mCurrentUserTracker.stopTracking(); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); } @@ -334,7 +348,8 @@ public final class ClockManager { private ClockPlugin getClockPlugin() { ClockPlugin plugin = null; if (ClockManager.this.isDocked()) { - final String name = mSettingsWrapper.getDockedClockFace(); + final String name = mSettingsWrapper.getDockedClockFace( + mCurrentUserTracker.getCurrentUserId()); if (name != null) { plugin = mClocks.get(name); if (plugin != null) { @@ -342,7 +357,8 @@ public final class ClockManager { } } } - final String name = mSettingsWrapper.getLockScreenCustomClockFace(); + final String name = mSettingsWrapper.getLockScreenCustomClockFace( + mCurrentUserTracker.getCurrentUserId()); if (name != null) { plugin = mClocks.get(name); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java index 58e11553af9d..e1c658be4c26 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java @@ -34,15 +34,19 @@ public class SettingsWrapper { /** * Gets the value stored in settings for the custom clock face. + * + * @param userId ID of the user. */ - public String getLockScreenCustomClockFace() { - return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE); + public String getLockScreenCustomClockFace(int userId) { + return Settings.Secure.getStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, userId); } /** * Gets the value stored in settings for the clock face to use when docked. + * + * @param userId ID of the user. */ - public String getDockedClockFace() { - return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE); + public String getDockedClockFace(int userId) { + return Settings.Secure.getStringForUser(mContentResolver, DOCKED_CLOCK_FACE, userId); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 4fe09a92a1de..665df777c1c1 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -26,16 +26,41 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; */ class Bubble { + private final String mKey; + private final BubbleExpandedView.OnBubbleBlockedListener mListener; + + private boolean mInflated; + public BubbleView iconView; public BubbleExpandedView expandedView; - public String key; public NotificationEntry entry; + Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) { + entry = e; + mKey = e.key; + mListener = listener; + } + + /** @deprecated use the other constructor to defer View creation. */ + @Deprecated Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView, BubbleExpandedView.OnBubbleBlockedListener listener) { - entry = e; - key = entry.key; + this(e, listener); + inflate(inflater, stackView); + } + + public String getKey() { + return mKey; + } + + boolean isInflated() { + return mInflated; + } + void inflate(LayoutInflater inflater, BubbleStackView stackView) { + if (mInflated) { + return; + } iconView = (BubbleView) inflater.inflate( R.layout.bubble_view, stackView, false /* attachToRoot */); iconView.setNotif(entry); @@ -44,12 +69,14 @@ class Bubble { R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); expandedView.setEntry(entry, stackView); - expandedView.setOnBlockedListener(listener); + expandedView.setOnBlockedListener(mListener); + mInflated = true; } - public void setEntry(NotificationEntry entry) { - key = entry.key; - iconView.update(entry); - expandedView.update(entry); + void setEntry(NotificationEntry entry) { + if (mInflated) { + iconView.update(entry); + expandedView.update(entry); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 5acf3c24fd6a..418d052e1858 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -220,6 +220,26 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mSurfaceSynchronizer = synchronizer; } + /** + * BubbleStackView is lazily created by this method the first time a Bubble is added. This + * method initializes the stack view and adds it to the StatusBar just above the scrim. + */ + private void ensureStackViewCreated() { + if (mStackView == null) { + mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer); + ViewGroup sbv = mStatusBarWindowController.getStatusBarView(); + // TODO(b/130237686): When you expand the shade on top of expanded bubble, there is no + // scrim between bubble and the shade + int bubblePosition = sbv.indexOfChild(sbv.findViewById(R.id.scrim_behind)) + 1; + sbv.addView(mStackView, bubblePosition, + new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + if (mExpandListener != null) { + mStackView.setExpandListener(mExpandListener); + } + mStackView.setOnBlockedListener(this); + } + } + @Override public void onUiModeChanged() { if (mStackView != null) { @@ -325,27 +345,15 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe /** * Adds or updates a bubble associated with the provided notification entry. * - * @param notif the notification associated with this bubble. + * @param notif the notification associated with this bubble. */ void updateBubble(NotificationEntry notif) { if (mStackView != null && mBubbleData.getBubble(notif.key) != null) { // It's an update mStackView.updateBubble(notif); } else { - if (mStackView == null) { - mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer); - ViewGroup sbv = mStatusBarWindowController.getStatusBarView(); - // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim - // between bubble and the shade - int bubblePosition = sbv.indexOfChild(sbv.findViewById(R.id.scrim_behind)) + 1; - sbv.addView(mStackView, bubblePosition, - new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - if (mExpandListener != null) { - mStackView.setExpandListener(mExpandListener); - } - mStackView.setOnBlockedListener(this); - } // It's new + ensureStackViewCreated(); mStackView.addBubble(notif); } if (shouldAutoExpand(notif)) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index cf702870c4ff..fe3f9d192cd5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -106,7 +106,7 @@ public class BubbleData { } public void addBubble(Bubble b) { - mBubbles.put(b.key, b); + mBubbles.put(b.getKey(), b); } @Nullable diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java index d1939d0fddb0..477e7d7ebf72 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java @@ -85,9 +85,7 @@ class ImageProcessHelper { Bitmap bitmap = bitmaps[0]; if (bitmap != null) { int[] histogram = processHistogram(bitmap); - Float val = computePercentile85(bitmap, histogram); - bitmaps[0] = null; - return val; + return computePercentile85(bitmap, histogram); } Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); return DEFAULT_PER85; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index f2ad958c57ab..17fbe09e07b3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -18,12 +18,14 @@ package com.android.keyguard.clock; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.database.ContentObserver; +import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -52,6 +54,8 @@ public final class ClockManagerTest extends SysuiTestCase { private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; + private static final int USER_ID = 0; + private static final Uri SETTINGS_URI = null; private ClockManager mClockManager; private ContentObserver mContentObserver; @@ -106,10 +110,10 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_default() { // GIVEN that settings doesn't contain any values - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null); - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the result is null, indicated the default clock face should be used. assertThat(mClockManager.getCurrentClock()).isNull(); } @@ -117,9 +121,9 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_customClock() { // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the plugin is the bubble clock face. assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @@ -127,9 +131,9 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void onClockChanged_customClock() { // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the plugin is the bubble clock face. ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor.capture()); @@ -139,9 +143,9 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void onClockChanged_uniqueInstances() { // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the listeners receive separate instances of the Bubble clock plugin. ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); @@ -156,9 +160,9 @@ public final class ClockManagerTest extends SysuiTestCase { public void getCurrentClock_badSettingsValue() { // GIVEN that settings contains a value that doesn't correspond to a // custom clock face. - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value"); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value"); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the result is null. assertThat(mClockManager.getCurrentClock()).isNull(); } @@ -174,7 +178,7 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_dockedCustomClock() { // GIVEN settings is set to the bubble clock face - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN dock event fires mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); // THEN the plugin is the bubble clock face. @@ -184,7 +188,7 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_badDockedSettingsValue() { // GIVEN settings contains a value that doesn't correspond to an available clock face. - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); // WHEN dock event fires mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); // THEN the result is null. @@ -195,8 +199,8 @@ public final class ClockManagerTest extends SysuiTestCase { public void getCurrentClock_badDockedSettingsFallback() { // GIVEN settings contains a value that doesn't correspond to an available clock face, but // locked screen settings is set to bubble clock. - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN dock event is fired mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); // THEN the plugin is the bubble clock face. diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 2cfcecca5f99..2055b64483d9 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -208,6 +208,7 @@ public class IpSecService extends IIpSecService.Stub { mBinder.linkToDeath(this, 0); } catch (RemoteException e) { binderDied(); + e.rethrowFromSystemServer(); } } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 39dae0b139ad..d58888a7c67b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1960,7 +1960,7 @@ public class AudioService extends IAudioService.Stub return; } for (final int groupedStream : avg.getLegacyStreamTypes()) { - setStreamVolume(stream, index, flags, callingPackage, callingPackage, + setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage, Binder.getCallingUid()); } } diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 527539d8ce0d..7733d6779e48 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -50,7 +50,7 @@ import java.util.List; import java.util.Set; /** - * CameraServiceProxy is the system_server analog to the camera service running in mediaserver. + * CameraServiceProxy is the system_server analog to the camera service running in cameraserver. * * @hide */ @@ -74,6 +74,7 @@ public class CameraServiceProxy extends SystemService private static final int MSG_SWITCH_USER = 1; private static final int RETRY_DELAY_TIME = 20; //ms + private static final int RETRY_TIMES = 30; // Maximum entries to keep in usage history before dumping out private static final int MAX_USAGE_HISTORY = 100; @@ -171,7 +172,7 @@ public class CameraServiceProxy extends SystemService " camera service UID!"); return; } - notifySwitchWithRetries(30); + notifySwitchWithRetries(RETRY_TIMES); } @Override @@ -242,7 +243,8 @@ public class CameraServiceProxy extends SystemService public void onStartUser(int userHandle) { synchronized(mLock) { if (mEnabledCameraUsers == null) { - // Initialize mediaserver, or update mediaserver if we are recovering from a crash. + // Initialize cameraserver, or update cameraserver if we are recovering + // from a crash. switchUserLocked(userHandle); } } @@ -324,9 +326,9 @@ public class CameraServiceProxy extends SystemService Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle); mLastUser = userHandle; if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) { - // Some user handles have been added or removed, update mediaserver. + // Some user handles have been added or removed, update cameraserver. mEnabledCameraUsers = currentUserHandles; - notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles); + notifySwitchWithRetriesLocked(RETRY_TIMES); } } @@ -343,12 +345,16 @@ public class CameraServiceProxy extends SystemService private void notifySwitchWithRetries(int retries) { synchronized(mLock) { - if (mEnabledCameraUsers == null) { - return; - } - if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) { - retries = 0; - } + notifySwitchWithRetriesLocked(retries); + } + } + + private void notifySwitchWithRetriesLocked(int retries) { + if (mEnabledCameraUsers == null) { + return; + } + if (notifyCameraserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) { + retries = 0; } if (retries <= 0) { return; @@ -358,13 +364,13 @@ public class CameraServiceProxy extends SystemService RETRY_DELAY_TIME); } - private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) { - // Forward the user switch event to the native camera service running in the mediaserver + private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) { + // Forward the user switch event to the native camera service running in the cameraserver // process. if (mCameraServiceRaw == null) { IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME); if (cameraServiceBinder == null) { - Slog.w(TAG, "Could not notify mediaserver, camera service not available."); + Slog.w(TAG, "Could not notify cameraserver, camera service not available."); return false; // Camera service not active, cannot evict user clients. } try { @@ -380,7 +386,7 @@ public class CameraServiceProxy extends SystemService try { mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles)); } catch (RemoteException e) { - Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e); + Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e); // Not much we can do if camera service is dead. return false; } diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 9590f8143713..a7d0a5c706b6 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -461,7 +461,9 @@ public final class ColorDisplayService extends SystemService { .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); } - updateDisplayWhiteBalanceStatus(); + if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { + updateDisplayWhiteBalanceStatus(); + } final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); dtm.setColorMode(mode, mNightDisplayTintController.getMatrix()); @@ -624,7 +626,11 @@ public final class ColorDisplayService extends SystemService { return false; } return Secure.getIntForUser(getContext().getContentResolver(), - Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1; + Secure.DISPLAY_WHITE_BALANCE_ENABLED, + getContext().getResources() + .getBoolean(R.bool.config_displayWhiteBalanceEnabledDefault) ? 1 + : 0, + mCurrentUser) == 1; } private boolean isDeviceColorManagedInternal() { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 0488d3a822ad..4a6eb276bd02 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -79,7 +79,6 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.function.Predicate; /** * Manages the lifecycle of application-provided services bound by system server. @@ -1163,6 +1162,7 @@ abstract public class ManagedServices { @Override public void onNullBinding(ComponentName name) { Slog.v(TAG, "onNullBinding() called with: name = [" + name + "]"); + mServicesBound.remove(servicesBindingTag); } }; if (!mContext.bindServiceAsUser(intent, @@ -1180,6 +1180,11 @@ abstract public class ManagedServices { } } + boolean isBound(ComponentName cn, int userId) { + final Pair<ComponentName, Integer> servicesBindingTag = Pair.create(cn, userId); + return mServicesBound.contains(servicesBindingTag); + } + /** * Remove a service for the given user by ComponentName */ diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 944aef5ab223..21b6f12b6f8b 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -324,6 +324,8 @@ class ApexManager { ipw.println("State: ROLLBACK IN PROGRESS"); } else if (si.isRolledBack) { ipw.println("State: ROLLED BACK"); + } else if (si.isRollbackFailed) { + ipw.println("State: ROLLBACK FAILED"); } ipw.decreaseIndent(); } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index a0f0a3178d1b..1908b3f8b366 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -565,7 +565,8 @@ public class StagingManager { // isRollbackInProgress is included to cover the scenario, when a device is rebooted in // during the rollback, and apexd fails to resume the rollback after reboot. return apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown - || apexSessionInfo.isRolledBack || apexSessionInfo.isRollbackInProgress; + || apexSessionInfo.isRolledBack || apexSessionInfo.isRollbackInProgress + || apexSessionInfo.isRollbackFailed; } @GuardedBy("mStagedSessions") 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 5df2f86d6ad7..35fa9406eb79 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -302,6 +302,7 @@ public final class DefaultPermissionGrantPolicy { } public void grantDefaultPermissions(int userId) { + removeSystemFixedStorage(userId); grantPermissionsToSysComponentsAndPrivApps(userId); grantDefaultSystemHandlerPermissions(userId); grantDefaultPermissionExceptions(userId); @@ -310,6 +311,46 @@ public final class DefaultPermissionGrantPolicy { } } + // STOPSHIP: This is meant to fix the devices messed up by storage permission model 2 and + // should be removed once all devices were updated + private void removeSystemFixedStorage(int userId) { + List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser( + DEFAULT_PACKAGE_INFO_QUERY_FLAGS, userId); + + for (PackageInfo pkg : packages) { + if (pkg == null || pkg.requestedPermissions == null) { + continue; + } + + for (String permission : pkg.requestedPermissions) { + if (!(Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission) + || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission))) { + continue; + } + + int flags = mContext.getPackageManager().getPermissionFlags(permission, + pkg.packageName, UserHandle.of(userId)); + if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) == 0) { + continue; + } + + Log.v(TAG, "Removing system fixed " + pkg.packageName + "/" + permission); + mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName, + PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, UserHandle.of(userId)); + + if (!doesPackageSupportRuntimePermissions(pkg) + || (flags & (PackageManager.FLAG_PERMISSION_USER_SET + | PackageManager.FLAG_PERMISSION_POLICY_FIXED)) != 0) { + continue; + } + + Log.v(TAG, "Revoking " + pkg.packageName + "/" + permission); + mContext.getPackageManager().revokeRuntimePermission(pkg.packageName, permission, + UserHandle.of(userId)); + } + } + } + private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) { Set<String> permissions = new ArraySet<>(); for (String permission : pkg.requestedPermissions) { @@ -1322,6 +1363,9 @@ public final class DefaultPermissionGrantPolicy { private PackageInfo getPackageInfo(String pkg, @PackageManager.PackageInfoFlags int extraFlags) { + if (pkg == null) { + return null; + } try { return mContext.getPackageManager().getPackageInfo(pkg, DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 43fe674c5d78..8aaf29a11033 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -23,9 +23,11 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -36,6 +38,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -61,6 +64,7 @@ import com.google.android.collect.Lists; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; @@ -965,6 +969,62 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void testOnNullBinding() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + service.registerSystemService(cn, 0); + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onNullBinding(cn); + return true; + }); + + service.registerSystemService(cn, 0); + assertFalse(service.isBound(cn, 0)); + } + + @Test + public void testOnServiceConnected() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + service.registerSystemService(cn, 0); + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.registerSystemService(cn, 0); + assertTrue(service.isBound(cn, 0)); + } + + @Test public void testOnPackagesChanged_nullValuesPassed_noNullPointers() { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index b0788253d0a7..bafcd5f0f8bd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4289,6 +4289,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testFlagBubble() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Say we're foreground + when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifs.length); + assertTrue((notifs[0].getNotification().flags & FLAG_BUBBLE) != 0); + assertTrue(mService.getNotificationRecord( + nr.sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException { // Bubbles are allowed! mService.setPreferencesHelper(mPreferencesHelper); diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java index 68ff777a0160..22a2c94fc194 100644 --- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java @@ -18,6 +18,7 @@ package com.android.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; @@ -134,11 +135,11 @@ public class IpSecServiceRefcountedResourceTest { IBinder binderMock = mock(IBinder.class); doThrow(new RemoteException()).when(binderMock).linkToDeath(anyObject(), anyInt()); - RefcountedResource<IResource> refcountedResource = getTestRefcountedResource(binderMock); - - // Verify that cleanup is performed (Spy limitations prevent verification of method calls - // for binder death scenario; check refcount to determine if cleanup was performed.) - assertEquals(-1, refcountedResource.mRefCount); + try { + getTestRefcountedResource(binderMock); + fail("Expected exception to propogate when binder fails to link to death"); + } catch (RuntimeException expected) { + } } @Test |