diff options
376 files changed, 8036 insertions, 2873 deletions
diff --git a/Android.bp b/Android.bp index a1f72fef942b..82e1a867786f 100644 --- a/Android.bp +++ b/Android.bp @@ -451,6 +451,18 @@ filegroup { path: "core/java", } +filegroup { + name: "libpowermanager_aidl", + srcs: [ + "core/java/android/os/Temperature.aidl", + "core/java/android/os/CoolingDevice.aidl", + "core/java/android/os/IThermalEventListener.aidl", + "core/java/android/os/IThermalStatusListener.aidl", + "core/java/android/os/IThermalService.aidl", + ], + path: "core/java", +} + java_library { name: "framework-minus-apex", defaults: ["framework-defaults"], @@ -461,7 +473,6 @@ java_library { "framework-statsd-stubs-module_libs_api", "framework-permission-stubs-systemapi", "framework-wifi-stubs", - "ike-stubs", "framework-tethering-stubs", ], installable: true, @@ -512,7 +523,6 @@ java_library { "framework-sdkextensions-stubs-systemapi", "framework-statsd-stubs-module_libs_api", "framework-wifi-stubs", - "ike-stubs", "framework-tethering-stubs", // TODO (b/147688669) should be framework-telephony-stubs "framework-telephony", @@ -689,6 +699,7 @@ filegroup { "core/java/android/annotation/CurrentTimeMillisLong.java", "core/java/android/annotation/IntDef.java", "core/java/android/annotation/IntRange.java", + "core/java/android/annotation/LongDef.java", "core/java/android/annotation/NonNull.java", "core/java/android/annotation/Nullable.java", "core/java/android/annotation/RequiresPermission.java", @@ -1216,6 +1227,12 @@ java_library { visibility: ["//frameworks/base/wifi"], } +filegroup { + name: "framework-wifi-util-lib-aidls", + srcs: ["core/java/android/content/pm/ParceledListSlice.aidl"], + path: "core/java", +} + // utility classes statically linked into wifi-service filegroup { name: "framework-wifi-service-shared-srcs", diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index aae33d7b0a89..18b1108aab3a 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -858,12 +858,9 @@ public class BlobStoreManagerService extends SystemService { writeBlobsInfoAsync(); // Cleanup any stale sessions. - final ArrayList<Integer> indicesToRemove = new ArrayList<>(); for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); - indicesToRemove.clear(); - for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { - final BlobStoreSession blobStoreSession = userSessions.valueAt(j); + userSessions.removeIf((sessionId, blobStoreSession) -> { boolean shouldRemove = false; // Cleanup sessions which haven't been modified in a while. @@ -880,13 +877,10 @@ public class BlobStoreManagerService extends SystemService { if (shouldRemove) { blobStoreSession.getSessionFile().delete(); mActiveBlobIds.remove(blobStoreSession.getSessionId()); - indicesToRemove.add(j); deletedBlobIds.add(blobStoreSession.getSessionId()); } - } - for (int j = 0; j < indicesToRemove.size(); ++j) { - userSessions.removeAt(indicesToRemove.get(j)); - } + return shouldRemove; + }); } if (LOGV) { Slog.v(TAG, "Completed idle maintenance; deleted " diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index b96161aba758..4c98b5fd3b56 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -77,11 +77,11 @@ public class JobParameters implements Parcelable { /** * @hide - * @deprecated use {@link #getReasonCodeDescription(int)} */ - @Deprecated - public static String getReasonName(int reason) { - switch (reason) { + // TODO(142420609): make it @SystemApi for mainline + @NonNull + public static String getReasonCodeDescription(int reasonCode) { + switch (reasonCode) { case REASON_CANCELED: return "canceled"; case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints"; case REASON_PREEMPT: return "preempt"; @@ -89,7 +89,7 @@ public class JobParameters implements Parcelable { case REASON_DEVICE_IDLE: return "device_idle"; case REASON_DEVICE_THERMAL: return "thermal"; case REASON_RESTRAINED: return "restrained"; - default: return "unknown:" + reason; + default: return "unknown:" + reasonCode; } } @@ -100,13 +100,6 @@ public class JobParameters implements Parcelable { return JOB_STOP_REASON_CODES; } - /** @hide */ - // @SystemApi TODO make it a system api for mainline - @NonNull - public static String getReasonCodeDescription(int reasonCode) { - return getReasonName(reasonCode); - } - @UnsupportedAppUsage private final int jobId; private final PersistableBundle extras; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java index e28e5bd6c53d..d05034797f3d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java @@ -359,7 +359,8 @@ public final class JobPackageTracker { } pw.print(pe.stopReasons.valueAt(k)); pw.print("x "); - pw.print(JobParameters.getReasonName(pe.stopReasons.keyAt(k))); + pw.print(JobParameters + .getReasonCodeDescription(pe.stopReasons.keyAt(k))); } pw.println(); } @@ -606,8 +607,9 @@ public final class JobPackageTracker { if (reason != null) { pw.print(mEventReasons[index]); } else { - pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK) - >> EVENT_STOP_REASON_SHIFT)); + pw.print(JobParameters.getReasonCodeDescription( + (mEventCmds[index] & EVENT_STOP_REASON_MASK) + >> EVENT_STOP_REASON_SHIFT)); } } pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index ff7944d07310..c1e529f3f966 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1963,7 +1963,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (restriction != null) { final int reason = restriction.getReason(); serviceContext.cancelExecutingJobLocked(reason, - "restricted due to " + JobParameters.getReasonName(reason)); + "restricted due to " + JobParameters.getReasonCodeDescription(reason)); } } } @@ -3110,7 +3110,7 @@ public class JobSchedulerService extends com.android.server.SystemService final JobRestriction restriction = mJobRestrictions.get(i); if (restriction.isJobRestricted(job)) { final int reason = restriction.getReason(); - pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]"); + pw.print(" " + JobParameters.getReasonCodeDescription(reason)); } } } else { diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 1ae0c22541c2..11d3a6867ac3 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -56,6 +56,7 @@ import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -404,9 +405,141 @@ public final class MediaParser { } } + // Public constants. + + /** + * Sets whether constant bitrate seeking should be enabled for exo.AdtsParser. {@code boolean} + * expected. Default value is {@code false}. + */ + public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = + "exo.AdtsParser.enableCbrSeeking"; + /** + * Sets whether constant bitrate seeking should be enabled for exo.AmrParser. {@code boolean} + * expected. Default value is {@code false}. + */ + public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "exo.AmrParser.enableCbrSeeking"; + /** + * Sets whether the ID3 track should be disabled for exo.FlacParser. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_FLAC_DISABLE_ID3 = "exo.FlacParser.disableId3"; + /** + * Sets whether exo.FragmentedMp4Parser should ignore edit lists. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_FMP4_IGNORE_EDIT_LISTS = + "exo.FragmentedMp4Parser.ignoreEditLists"; + /** + * Sets whether exo.FragmentedMp4Parser should ignore the tfdt box. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_FMP4_IGNORE_TFDT_BOX = + "exo.FragmentedMp4Parser.ignoreTfdtBox"; + /** + * Sets whether exo.FragmentedMp4Parser should treat all video frames as key frames. {@code + * boolean} expected. Default value is {@code false}. + */ + public static final String PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = + "exo.FragmentedMp4Parser.treatVideoFramesAsKeyframes"; + /** + * Sets whether exo.MatroskaParser should avoid seeking to the cues element. {@code boolean} + * expected. Default value is {@code false}. + * + * <p>If this flag is enabled and the cues element occurs after the first cluster, then the + * media is treated as unseekable. + */ + public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = + "exo.MatroskaParser.disableCuesSeeking"; + /** + * Sets whether the ID3 track should be disabled for exo.Mp3Parser. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_MP3_DISABLE_ID3 = "exo.Mp3Parser.disableId3"; + /** + * Sets whether constant bitrate seeking should be enabled for exo.Mp3Parser. {@code boolean} + * expected. Default value is {@code false}. + */ + public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "exo.Mp3Parser.enableCbrSeeking"; + /** + * Sets whether exo.Mp3Parser should generate a time-to-byte mapping. {@code boolean} expected. + * Default value is {@code false}. + * + * <p>Enabling this flag may require to scan a significant portion of the file to compute a seek + * point. Therefore, it should only be used if: + * + * <ul> + * <li>the file is small, or + * <li>the bitrate is variable (or the type of bitrate is unknown) and the seeking metadata + * provided in the file is not precise enough (or is not present). + * </ul> + */ + public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = + "exo.Mp3Parser.enableIndexSeeking"; + /** + * Sets whether exo.Mp4Parser should ignore edit lists. {@code boolean} expected. Default value + * is {@code false}. + */ + public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "exo.Mp4Parser.ignoreEditLists"; + /** + * Sets the operation mode for exo.TsParser. {@code String} expected. Valid values are {@code + * "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}. + * + * <p>The operation modes alter the way exo.TsParser behaves so that it can handle certain kinds + * of commonly-occurring malformed media. + * + * <ul> + * <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if + * more PMTs are declared in the PAT. + * <li>{@code "multi_pmt"}: Behave as described in ISO/IEC 13818-1. + * <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters. + * </ul> + */ + public static final String PARAMETER_TS_MODE = "exo.TsParser.mode"; + /** + * Sets whether exo.TsParser should treat samples consisting of non-IDR I slices as + * synchronization samples (key-frames). {@code boolean} expected. Default value is {@code + * false}. + */ + public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = + "exo.TsParser.allowNonIdrAvcKeyframes"; + /** + * Sets whether exo.TsParser should ignore AAC elementary streams. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "exo.TsParser.ignoreAacStream"; + /** + * Sets whether exo.TsParser should ignore AVC elementary streams. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "exo.TsParser.ignoreAvcStream"; + /** + * Sets whether exo.TsParser should ignore splice information streams. {@code boolean} expected. + * Default value is {@code false}. + */ + public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = + "exo.TsParser.ignoreSpliceInfoStream"; + /** + * Sets whether exo.TsParser should split AVC stream into access units based on slice headers. + * {@code boolean} expected. Default value is {@code false}. + * + * <p>This flag should be left disabled if the stream contains access units delimiters in order + * to avoid unnecessary computational costs. + */ + public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = + "exo.TsParser.ignoreDetectAccessUnits"; + /** + * Sets whether exo.TsParser should handle HDMV DTS audio streams. {@code boolean} expected. + * Default value is {@code false}. + * + * <p>Enabling this flag will disable the detection of SCTE subtitles. + */ + public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = + "exo.TsParser.enableHdmvDtsAudioStreams"; + // Private constants. private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME; + private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME; // Instance creation methods. @@ -462,6 +595,7 @@ public final class MediaParser { // Private fields. + private final Map<String, Object> mParserParameters; private final OutputConsumer mOutputConsumer; private final String[] mParserNamesPool; private final PositionHolder mPositionHolder; @@ -477,6 +611,51 @@ public final class MediaParser { // Public methods. /** + * Sets parser-specific parameters which allow customizing behavior. + * + * <p>Must be called before the first call to {@link #advance}. + * + * @param parameterName The name of the parameter to set. See {@code PARAMETER_*} constants for + * documentation on possible values. + * @param value The value to set for the given {@code parameterName}. See {@code PARAMETER_*} + * constants for documentation on the expected types. + * @return This instance, for convenience. + * @throws IllegalStateException If called after calling {@link #advance} on the same instance. + */ + @NonNull + public MediaParser setParameter(@NonNull String parameterName, @NonNull Object value) { + if (mExtractor != null) { + throw new IllegalStateException( + "setParameters() must be called before the first advance() call."); + } + Class expectedType = EXPECTED_TYPE_BY_PARAMETER_NAME.get(parameterName); + // Ignore parameter names that are not contained in the map, in case the client is passing + // a parameter that is being added in a future version of this library. + if (expectedType != null && !expectedType.isInstance(value)) { + throw new IllegalArgumentException( + parameterName + + " expects a " + + expectedType.getSimpleName() + + " but a " + + value.getClass().getSimpleName() + + " was passed."); + } + mParserParameters.put(parameterName, value); + return this; + } + + /** + * Returns whether the given {@code parameterName} is supported by this parser. + * + * @param parameterName The parameter name to check support for. One of the {@code PARAMETER_*} + * constants. + * @return Whether the given {@code parameterName} is supported. + */ + public boolean supportsParameter(@NonNull String parameterName) { + return EXPECTED_TYPE_BY_PARAMETER_NAME.containsKey(parameterName); + } + + /** * Returns the name of the backing parser implementation. * * <p>If this instance was creating using {@link #createByName}, the provided name is returned. @@ -599,6 +778,7 @@ public final class MediaParser { // Private methods. private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) { + mParserParameters = new HashMap<>(); mOutputConsumer = outputConsumer; mParserNamesPool = parserNamesPool; if (!sniff) { @@ -949,5 +1129,27 @@ public final class MediaParser { extractorFactoriesByName.put("exo.TsParser", TsExtractor::new); extractorFactoriesByName.put("exo.WavParser", WavExtractor::new); EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); + + HashMap<String, Class> expectedTypeByParameterName = new HashMap<>(); + expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_FMP4_IGNORE_EDIT_LISTS, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_FMP4_IGNORE_TFDT_BOX, Boolean.class); + expectedTypeByParameterName.put( + PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class); + expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class); + expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class); + EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName); } } diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp index 4c5c2b2cfd4f..25765afb3ab9 100644 --- a/apex/sdkextensions/Android.bp +++ b/apex/sdkextensions/Android.bp @@ -28,7 +28,6 @@ apex_defaults { name: "com.android.sdkext-defaults", java_libs: [ "framework-sdkextensions" ], prebuilts: [ - "com.android.sdkext.ldconfig", "derive_sdk.rc", ], key: "com.android.sdkext.key", @@ -51,13 +50,6 @@ android_app_certificate { certificate: "com.android.sdkext", } -prebuilt_etc { - name: "com.android.sdkext.ldconfig", - src: "ld.config.txt", - filename: "ld.config.txt", - installable: false, -} - python_binary_host { name: "gen_sdkinfo", srcs: [ diff --git a/apex/sdkextensions/ld.config.txt b/apex/sdkextensions/ld.config.txt deleted file mode 100644 index dcc69b892760..000000000000 --- a/apex/sdkextensions/ld.config.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2019 The Android Open Source Project -# -# Bionic loader config file for the sdkextensions apex. - -dir.sdkextensions = /apex/com.android.sdkext/bin/ - -[sdkextensions] -additional.namespaces = platform - -namespace.default.isolated = true -namespace.default.links = platform -namespace.default.link.platform.allow_all_shared_libs = true - -############################################################################### -# "platform" namespace: used for NDK libraries -############################################################################### -namespace.platform.isolated = true -namespace.platform.search.paths = /system/${LIB} -namespace.platform.asan.search.paths = /data/asan/system/${LIB} - -# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc. -# Add /apex/... path to the permitted paths because linker uses realpath(3) -# to check the accessibility of the lib. We could add this to search.paths -# instead but that makes the resolution of bionic libs be dependent on -# the order of /system/lib and /apex/... in search.paths. If /apex/... -# is after /system/lib, then /apex/... is never tried because libc.so -# is always found in /system/lib but fails to pass the accessibility test -# because of its realpath. It's better to not depend on the ordering if -# possible. -namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic -namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java index 526d17ff0d71..e637187f23be 100644 --- a/apex/statsd/framework/java/android/app/StatsManager.java +++ b/apex/statsd/framework/java/android/app/StatsManager.java @@ -159,6 +159,9 @@ public final class StatsManager { throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to addConfig in statsmanager"); + throw new StatsUnavailableException(e.getMessage(), e); } } } @@ -195,6 +198,9 @@ public final class StatsManager { throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to removeConfig in statsmanager"); + throw new StatsUnavailableException(e.getMessage(), e); } } } @@ -391,6 +397,9 @@ public final class StatsManager { throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to getReports in statsmanager"); + throw new StatsUnavailableException(e.getMessage(), e); } } } @@ -428,6 +437,9 @@ public final class StatsManager { throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to getStatsMetadata in statsmanager"); + throw new StatsUnavailableException(e.getMessage(), e); } } } @@ -469,6 +481,9 @@ public final class StatsManager { + "registered experiment IDs"); } throw new StatsUnavailableException("could not connect", e); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to getRegisteredExperimentIds in statsmanager"); + throw new StatsUnavailableException(e.getMessage(), e); } } } diff --git a/api/current.txt b/api/current.txt index 1e65e7bdfc9e..0a7bac5183dc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -32,6 +32,7 @@ package android { field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE"; + field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS"; field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE"; field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE"; @@ -2991,7 +2992,7 @@ package android.accessibilityservice { method public int getNonInteractiveUiTimeoutMillis(); method public android.content.pm.ResolveInfo getResolveInfo(); method public String getSettingsActivityName(); - method @Nullable public android.graphics.drawable.Drawable loadAnimatedImage(@NonNull android.content.pm.PackageManager); + method @Nullable public android.graphics.drawable.Drawable loadAnimatedImage(@NonNull android.content.Context); method public String loadDescription(android.content.pm.PackageManager); method @Nullable public String loadHtmlDescription(@NonNull android.content.pm.PackageManager); method public CharSequence loadSummary(android.content.pm.PackageManager); @@ -6969,7 +6970,6 @@ package android.app.admin { method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int); method public boolean removeUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); method public boolean requestBugreport(@NonNull android.content.ComponentName); - method public void requestSetLocationProviderAllowed(@NonNull android.content.ComponentName, @NonNull String, boolean); method @Deprecated public boolean resetPassword(String, int); method public boolean resetPasswordWithToken(@NonNull android.content.ComponentName, String, byte[], int); method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long); @@ -25307,6 +25307,10 @@ package android.media { method public void recycle(); } + public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException { + ctor public MediaCodec.IncompatibleWithBlockModelException(); + } + public static final class MediaCodec.LinearBlock { method protected void finalize(); method public static boolean isCodecCopyFreeCompatible(@NonNull String[]); @@ -25334,23 +25338,24 @@ package android.media { } public static final class MediaCodec.OutputFrame { - method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>); method public int getFlags(); method @NonNull public android.media.MediaFormat getFormat(); method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock(); method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); method public long getPresentationTimeUs(); + method public void retrieveChangedKeys(@NonNull java.util.Set<java.lang.String>); } public final class MediaCodec.QueueRequest { method public void queue(); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); - method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int); method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float); - method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock); method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int); - method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo); method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long); + method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long); method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String); } @@ -26408,6 +26413,26 @@ package android.media { method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat); method public void release(); method public void seek(@NonNull android.media.MediaParser.SeekPoint); + method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object); + method public boolean supportsParameter(@NonNull String); + field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "exo.AdtsParser.enableCbrSeeking"; + field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "exo.AmrParser.enableCbrSeeking"; + field public static final String PARAMETER_FLAC_DISABLE_ID3 = "exo.FlacParser.disableId3"; + field public static final String PARAMETER_FMP4_IGNORE_EDIT_LISTS = "exo.FragmentedMp4Parser.ignoreEditLists"; + field public static final String PARAMETER_FMP4_IGNORE_TFDT_BOX = "exo.FragmentedMp4Parser.ignoreTfdtBox"; + field public static final String PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "exo.FragmentedMp4Parser.treatVideoFramesAsKeyframes"; + field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "exo.MatroskaParser.disableCuesSeeking"; + field public static final String PARAMETER_MP3_DISABLE_ID3 = "exo.Mp3Parser.disableId3"; + field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "exo.Mp3Parser.enableCbrSeeking"; + field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "exo.Mp3Parser.enableIndexSeeking"; + field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "exo.Mp4Parser.ignoreEditLists"; + field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "exo.TsParser.allowNonIdrAvcKeyframes"; + field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "exo.TsParser.ignoreDetectAccessUnits"; + field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "exo.TsParser.enableHdmvDtsAudioStreams"; + field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "exo.TsParser.ignoreAacStream"; + field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "exo.TsParser.ignoreAvcStream"; + field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "exo.TsParser.ignoreSpliceInfoStream"; + field public static final String PARAMETER_TS_MODE = "exo.TsParser.mode"; } public static interface MediaParser.InputReader { @@ -26874,21 +26899,27 @@ package android.media { ctor public MediaRoute2ProviderService(); method @NonNull public final java.util.List<android.media.RoutingSessionInfo> getAllSessionInfo(); method @Nullable public final android.media.RoutingSessionInfo getSessionInfo(@NonNull String); + method public final void notifyRequestFailed(long, int); method public final void notifyRoutes(@NonNull java.util.Collection<android.media.MediaRoute2Info>); method public final void notifySessionCreated(@NonNull android.media.RoutingSessionInfo, long); method public final void notifySessionCreationFailed(long); method public final void notifySessionReleased(@NonNull String); method public final void notifySessionUpdated(@NonNull android.media.RoutingSessionInfo); method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); - method public abstract void onCreateSession(@NonNull String, @NonNull String, long, @Nullable android.os.Bundle); - method public abstract void onDeselectRoute(@NonNull String, @NonNull String); + method public abstract void onCreateSession(long, @NonNull String, @NonNull String, @Nullable android.os.Bundle); + method public abstract void onDeselectRoute(long, @NonNull String, @NonNull String); method public void onDiscoveryPreferenceChanged(@NonNull android.media.RouteDiscoveryPreference); - method public abstract void onReleaseSession(@NonNull String); - method public abstract void onSelectRoute(@NonNull String, @NonNull String); - method public abstract void onSetRouteVolume(@NonNull String, int); - method public abstract void onSetSessionVolume(@NonNull String, int); - method public abstract void onTransferToRoute(@NonNull String, @NonNull String); - field public static final long REQUEST_ID_UNKNOWN = 0L; // 0x0L + method public abstract void onReleaseSession(long, @NonNull String); + method public abstract void onSelectRoute(long, @NonNull String, @NonNull String); + method public abstract void onSetRouteVolume(long, @NonNull String, int); + method public abstract void onSetSessionVolume(long, @NonNull String, int); + method public abstract void onTransferToRoute(long, @NonNull String, @NonNull String); + field public static final int REASON_INVALID_COMMAND = 4; // 0x4 + field public static final int REASON_NETWORK_ERROR = 2; // 0x2 + field public static final int REASON_REJECTED = 1; // 0x1 + field public static final int REASON_ROUTE_NOT_AVAILABLE = 3; // 0x3 + field public static final int REASON_UNKNOWN_ERROR = 0; // 0x0 + field public static final long REQUEST_ID_NONE = 0L; // 0x0L field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; } @@ -43401,6 +43432,7 @@ package android.service.controls { method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>); method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable(); method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested(); + method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control); field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService"; field @NonNull public static final String TAG = "ControlsProviderService"; } diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 7b66f735b5ad..6863221f7fda 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -30,7 +30,6 @@ package android.net { } public class TetheringConstants { - ctor public TetheringConstants(); field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt index 6e59596cb05f..56f7a02260a7 100644 --- a/api/module-lib-lint-baseline.txt +++ b/api/module-lib-lint-baseline.txt @@ -27,7 +27,3 @@ PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrecti Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections: Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections - - -StaticUtils: android.net.TetheringConstants: - Fully-static utility classes must not have constructor diff --git a/api/system-current.txt b/api/system-current.txt index 9663c0c53a39..2c6228c31932 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -630,10 +630,10 @@ package android.app { method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence); method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int); method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed(); + method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean isValidLockPasswordComplexity(int, @NonNull byte[], int); method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback); method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int); method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean); - method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean validateLockPasswordComplexity(boolean, @NonNull byte[], int); } public class Notification implements android.os.Parcelable { @@ -7200,12 +7200,12 @@ package android.net.wifi { public final class SoftApCapability implements android.os.Parcelable { method public int describeContents(); method public int getMaxSupportedClients(); - method public boolean isFeatureSupported(int); + method public boolean isFeatureSupported(long); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR; - field public static final int SOFTAP_FEATURE_ACS_OFFLOAD = 1; // 0x1 - field public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2; // 0x2 - field public static final int SOFTAP_FEATURE_WPA3_SAE = 4; // 0x4 + field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L + field public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2L; // 0x2L + field public static final long SOFTAP_FEATURE_WPA3_SAE = 4L; // 0x4L } public final class SoftApConfiguration implements android.os.Parcelable { @@ -8757,7 +8757,9 @@ package android.os { public class UserManager { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData(); - method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @Nullable String[]) throws android.os.UserManager.UserOperationException; + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException; + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles(); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); @@ -8765,7 +8767,6 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon(); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getUserProfiles(boolean); method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle); method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability(); diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 4899b4a5247c..dcfdfe3aae53 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -75,11 +75,11 @@ string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_ (long long)id); } -static const char* findTrainInfoFileNameLocked(const string& trainName) { +static string findTrainInfoFileNameLocked(const string& trainName) { unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); if (dir == NULL) { VLOG("Path %s does not exist", TRAIN_INFO_DIR); - return nullptr; + return ""; } dirent* de; while ((de = readdir(dir.get()))) { @@ -90,12 +90,12 @@ static const char* findTrainInfoFileNameLocked(const string& trainName) { if (fileNameLength >= trainName.length()) { if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(), trainName.length())) { - return fileName; + return string(fileName); } } } - return nullptr; + return ""; } // Returns array of int64_t which contains timestamp in seconds, uid, @@ -267,13 +267,13 @@ bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInf bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) { trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true); - const char* fileName = findTrainInfoFileNameLocked(trainName); - if (fileName == nullptr) { + string fileName = findTrainInfoFileNameLocked(trainName); + if (fileName.empty()) { return false; } - int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName).c_str(), O_RDONLY | O_CLOEXEC); + int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC); if (fd == -1) { - VLOG("Failed to open %s", fileName); + VLOG("Failed to open %s", fileName.c_str()); return false; } diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 97074050448e..fe270a480d83 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -46,6 +46,10 @@ public final class Telecom extends BaseCommand { * @param args The command-line arguments */ public static void main(String[] args) { + // Initialize the telephony module. + // TODO: Do it in zygote and RuntimeInit. b/148897549 + ActivityThread.initializeMainlineModules(); + (new Telecom()).run(args); } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index c37328454c7d..3b0667d1e377 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -803,11 +803,12 @@ public class AccessibilityServiceInfo implements Parcelable { * @return The animated image drawable. */ @Nullable - public Drawable loadAnimatedImage(@NonNull PackageManager packageManager) { + public Drawable loadAnimatedImage(@NonNull Context context) { if (mAnimatedImageRes == /* invalid */ 0) { return null; } + final PackageManager packageManager = context.getPackageManager(); final String packageName = mComponentName.getPackageName(); final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo; diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java index d537ce1253dc..62096792d764 100644 --- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java @@ -208,11 +208,12 @@ public final class AccessibilityShortcutInfo { * @return The animated image drawable. */ @Nullable - public Drawable loadAnimatedImage(@NonNull PackageManager packageManager) { + public Drawable loadAnimatedImage(@NonNull Context context) { if (mAnimatedImageRes == /* invalid */ 0) { return null; } + final PackageManager packageManager = context.getPackageManager(); final String packageName = mComponentName.getPackageName(); final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 8df26cbbb4f4..480ea8a25f91 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2409,17 +2409,35 @@ class ContextImpl extends Context { + "other visual contexts, such as Activity or one created with " + "Context#createDisplayContext(Display)"); } - return new WindowContext(this, null /* token */, type, options); + return new WindowContext(this, type, options); } ContextImpl createBaseWindowContext(IBinder token) { ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName, token, mUser, mFlags, mClassLoader, null); context.mIsUiContext = true; + context.mIsAssociatedWithDisplay = true; return context; } + Resources createWindowContextResources() { + final String resDir = mPackageInfo.getResDir(); + final String[] splitResDirs = mPackageInfo.getSplitResDirs(); + final String[] overlayDirs = mPackageInfo.getOverlayDirs(); + final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles; + final int displayId = getDisplayId(); + final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) + ? mPackageInfo.getCompatibilityInfo() + : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + final List<ResourcesLoader> loaders = mResources.getLoaders(); + + // TODO(b/128338354): Rename to createTokenResources + return mResourcesManager.createBaseActivityResources(mToken, resDir, splitResDirs, + overlayDirs, libDirs, displayId, null /* overrideConfig */, + compatInfo, mClassLoader, loaders); + } + @Override public @NonNull Context createFeatureContext(@Nullable String featureId) { return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName, diff --git a/core/java/android/app/IWindowToken.aidl b/core/java/android/app/IWindowToken.aidl new file mode 100644 index 000000000000..8ea881fba09c --- /dev/null +++ b/core/java/android/app/IWindowToken.aidl @@ -0,0 +1,33 @@ +/* + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +package android.app; + +import android.content.res.Configuration; +import android.view.IWindow; + +/** + * Callback to receive configuration changes from {@link com.android.server.WindowToken}. + * WindowToken can be regarded to as a group of {@link android.view.IWindow} added from the same + * visual context, such as {@link Activity} or one created with + * {@link android.content.Context#createWindowContext(int)}. When WindowToken receives configuration + * changes and/or when it is moved between displays, it will propagate the changes to client side + * via this interface. + * @see android.content.Context#createWindowContext(int) + * {@hide} + */ +oneway interface IWindowToken { + void onConfigurationChanged(in Configuration newConfig, int newDisplayId); +} diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 4b0cadbd9c65..2122e92ba5b5 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.app.admin.PasswordMetrics; import android.app.trust.ITrustManager; import android.compat.annotation.UnsupportedAppUsage; @@ -676,8 +677,9 @@ public class KeyguardManager { /** * Determine if a given password is valid based off its lock type and expected complexity level. * - * @param isPin - whether this is a PIN-type password (only digits) - * @param password - password to validate + * @param lockType - type of lock as specified in {@link LockTypes} + * @param password - password to validate; this has the same encoding + * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} * @return true if the password is valid, false otherwise @@ -685,8 +687,8 @@ public class KeyguardManager { */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @SystemApi - public boolean validateLockPasswordComplexity( - boolean isPin, @NonNull byte[] password, int complexity) { + public boolean isValidLockPasswordComplexity(@LockTypes int lockType, @NonNull byte[] password, + @PasswordComplexity int complexity) { if (!checkInitialLockMethodUsage()) { return false; } @@ -696,9 +698,11 @@ public class KeyguardManager { (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); PasswordMetrics adminMetrics = devicePolicyManager.getPasswordMinimumMetrics(mContext.getUserId()); + // Check if the password fits the mold of a pin or pattern. + boolean isPinOrPattern = lockType != LockTypes.PASSWORD; return PasswordMetrics.validatePassword( - adminMetrics, complexity, isPin, password).size() == 0; + adminMetrics, complexity, isPinOrPattern, password).size() == 0; } /** @@ -712,7 +716,7 @@ public class KeyguardManager { */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @SystemApi - public int getMinLockLength(boolean isPin, int complexity) { + public int getMinLockLength(boolean isPin, @PasswordComplexity int complexity) { if (!checkInitialLockMethodUsage()) { return -1; } @@ -731,7 +735,8 @@ public class KeyguardManager { * Set the lockscreen password after validating against its expected complexity level. * * @param lockType - type of lock as specified in {@link LockTypes} - * @param password - password to validate + * @param password - password to validate; this has the same encoding + * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} * @return true if the lock is successfully set, false otherwise @@ -739,7 +744,8 @@ public class KeyguardManager { */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @SystemApi - public boolean setLock(@LockTypes int lockType, @NonNull byte[] password, int complexity) { + public boolean setLock(@LockTypes int lockType, @NonNull byte[] password, + @PasswordComplexity int complexity) { if (!checkInitialLockMethodUsage()) { return false; } @@ -750,7 +756,7 @@ public class KeyguardManager { Log.e(TAG, "Password already set, rejecting call to setLock"); return false; } - if (!validateLockPasswordComplexity(lockType != LockTypes.PASSWORD, password, complexity)) { + if (!isValidLockPasswordComplexity(lockType, password, complexity)) { Log.e(TAG, "Password is not valid, rejecting call to setLock"); return false; } diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java index d279983a5793..568456270809 100644 --- a/core/java/android/app/WindowContext.java +++ b/core/java/android/app/WindowContext.java @@ -15,10 +15,12 @@ */ package android.app; +import static android.view.WindowManagerGlobal.ADD_OKAY; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.ContextWrapper; -import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -26,73 +28,62 @@ import android.view.IWindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerImpl; +import java.lang.ref.Reference; + /** * {@link WindowContext} is a context for non-activity windows such as * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} windows or system * windows. Its resources and configuration are adjusted to the area of the display that will be - * used when a new window is added via {@link android.view.WindowManager.addView}. + * used when a new window is added via {@link android.view.WindowManager#addView}. * * @see Context#createWindowContext(int, Bundle) * @hide */ -// TODO(b/128338354): Handle config/display changes from server side. public class WindowContext extends ContextWrapper { private final WindowManagerImpl mWindowManager; private final IWindowManager mWms; - private final IBinder mToken; - private final int mDisplayId; + private final WindowTokenClient mToken; private boolean mOwnsToken; /** - * Default constructor. Can either accept an existing token or generate one and registers it - * with the server if necessary. + * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to + * the token. * * @param base Base {@link Context} for this new instance. - * @param token A valid {@link com.android.server.wm.WindowToken}. Pass {@code null} to generate - * one. * @param type Window type to be used with this context. * @hide */ - public WindowContext(Context base, IBinder token, int type, Bundle options) { + public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) { + // Correct base context will be built once the token is resolved, so passing 'null' here. super(null /* base */); mWms = WindowManagerGlobal.getWindowManagerService(); - if (token != null && !isWindowToken(token)) { - throw new IllegalArgumentException("Token must be registered to server."); - } - mToken = token != null ? token : new Binder(); + mToken = new WindowTokenClient(); + final ContextImpl contextImpl = createBaseWindowContext(base, mToken); attachBaseContext(contextImpl); contextImpl.setOuterContext(this); - mDisplayId = getDisplayId(); + mToken.attachContext(this); + mWindowManager = new WindowManagerImpl(this); mWindowManager.setDefaultToken(mToken); - // TODO(b/128338354): Obtain the correct config from WM and adjust resources. - if (token != null) { - mOwnsToken = false; - return; - } + int result; try { - mWms.addWindowTokenWithOptions(mToken, type, mDisplayId, options, getPackageName()); + // Register the token with WindowManager. This will also call back with the current + // config back to the client. + result = mWms.addWindowTokenWithOptions( + mToken, type, getDisplayId(), options, getPackageName()); + // TODO(window-context): remove token with a DeathObserver } catch (RemoteException e) { mOwnsToken = false; throw e.rethrowFromSystemServer(); } - mOwnsToken = true; - } - - /** Check if the passed window token is registered with the server. */ - private boolean isWindowToken(@NonNull IBinder token) { - try { - return mWms.isWindowToken(token); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - return false; + mOwnsToken = result == ADD_OKAY; + Reference.reachabilityFence(this); } private static ContextImpl createBaseWindowContext(Context outer, IBinder token) { @@ -112,7 +103,7 @@ public class WindowContext extends ContextWrapper { protected void finalize() throws Throwable { if (mOwnsToken) { try { - mWms.removeWindowToken(mToken, mDisplayId); + mWms.removeWindowToken(mToken, getDisplayId()); mOwnsToken = false; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java new file mode 100644 index 000000000000..ed0179bb9839 --- /dev/null +++ b/core/java/android/app/WindowTokenClient.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.IBinder; + +/** + * Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from + * server when window token config is updated or when it is moved between displays, and update the + * resources associated with this token on the client side. This will make sure that + * {@link WindowContext} instances will have updated resources and configuration. + * @hide + */ +public class WindowTokenClient extends IWindowToken.Stub { + /** + * Attached {@link Context} for this window token to update configuration and resources. + * Initialized by {@link #attachContext(Context)}. + */ + private Context mContext = null; + + private final ResourcesManager mResourcesManager = ResourcesManager.getInstance(); + + /** + * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} + * can only attach one {@link Context}. + * <p>This method must be called before invoking + * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, + * String)}.<p/> + * + * @param context context to be attached + * @throws IllegalStateException if attached context has already existed. + */ + void attachContext(@NonNull Context context) { + if (mContext != null) { + throw new IllegalStateException("Context is already attached."); + } + mContext = context; + ContextImpl impl = ContextImpl.getImpl(mContext); + impl.setResources(impl.createWindowContextResources()); + } + + @Override + public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { + final int currentDisplayId = mContext.getDisplayId(); + final boolean displayChanged = newDisplayId != currentDisplayId; + final Configuration config = new Configuration(mContext.getResources() + .getConfiguration()); + final boolean configChanged = config.isOtherSeqNewer(newConfig) + && config.updateFrom(newConfig) != 0; + if (displayChanged || configChanged) { + // TODO(ag/9789103): update resource manager logic to track non-activity tokens + mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId, + displayChanged); + } + if (displayChanged) { + mContext.updateDisplay(newDisplayId); + } + } +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 546fef913192..b219394ddfa9 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8989,49 +8989,6 @@ public class DevicePolicyManager { } /** - * Called by device owners to request a location provider to change its allowed state. For a - * provider to be enabled requires both that the master location setting is enabled, and that - * the provider itself is allowed. Most location providers are always allowed. Some location - * providers may have user consents or terms and conditions that must be accepted, or some other - * type of blocker before they are allowed however. Every location provider is responsible for - * its own allowed state. - * - * <p>This method requests that a location provider change its allowed state. For providers that - * are always allowed and have no state to change, this will have no effect. If the provider - * does require some consent, terms and conditions, or other blocking state, using this API - * implies that the device owner is agreeing/disagreeing to any consents, terms and conditions, - * etc, and the provider should make a best effort to adjust it's allowed state accordingly. - * - * <p>Location providers are generally only responsible for the current user, and callers must - * assume that this method will only affect provider state for the current user. Callers are - * responsible for tracking current user changes and re-updating provider state as necessary. - * - * <p>While providers are expected to make a best effort to honor this request, it is not a - * given that all providers will support such a request. If a provider does change its state as - * a result of this request, that may happen asynchronously after some delay. Test location - * providers set through {@link android.location.LocationManager#addTestProvider} will respond - * to this request to aide in testing. - * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with - * @param provider A location provider as listed by - * {@link android.location.LocationManager#getAllProviders()} - * @param providerAllowed Whether the location provider is being requested to enable or disable - * itself - * @throws SecurityException if {@code admin} is not a device owner. - */ - public void requestSetLocationProviderAllowed(@NonNull ComponentName admin, - @NonNull String provider, boolean providerAllowed) { - throwIfParentInstance("requestSetLocationProviderAllowed"); - if (mService != null) { - try { - mService.requestSetLocationProviderAllowed(admin, provider, providerAllowed); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - - /** * Called by profile or device owners to update {@link android.provider.Settings.Secure} * settings. Validation that the value of the setting is in the correct form for the setting * type should be performed by the caller. @@ -9975,20 +9932,27 @@ public class DevicePolicyManager { } /** - * Called by device owner to control the security logging feature. + * Called by device owner or a profile owner of an organization-owned managed profile to + * control the security logging feature. * * <p> Security logs contain various information intended for security auditing purposes. - * See {@link SecurityEvent} for details. + * When security logging is enabled by a profile owner of + * an organization-owned managed profile, certain security logs are not visible (for example + * personal app launch events) or they will be redacted (for example, details of the physical + * volume mount events). Please see {@link SecurityEvent} for details. * * <p><strong>Note:</strong> The device owner won't be able to retrieve security logs if there * are unaffiliated secondary users or profiles on the device, regardless of whether the * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for * all users to become affiliated. Therefore it's recommended that affiliation ids are set for - * new users as soon as possible after provisioning via {@link #setAffiliationIds}. + * new users as soon as possible after provisioning via {@link #setAffiliationIds}. Profile + * owner of organization-owned managed profile is not subject to this restriction since all + * privacy-sensitive events happening outside the managed profile would have been redacted + * already. * - * @param admin Which device owner this request is associated with. + * @param admin Which device admin this request is associated with. * @param enabled whether security logging should be enabled or not. - * @throws SecurityException if {@code admin} is not a device owner. + * @throws SecurityException if {@code admin} is not allowed to control security logging. * @see #setAffiliationIds * @see #retrieveSecurityLogs */ @@ -10002,14 +9966,14 @@ public class DevicePolicyManager { } /** - * Return whether security logging is enabled or not by the device owner. + * Return whether security logging is enabled or not by the admin. * - * <p>Can only be called by the device owner, otherwise a {@link SecurityException} will be - * thrown. + * <p>Can only be called by the device owner or a profile owner of an organization-owned + * managed profile, otherwise a {@link SecurityException} will be thrown. * - * @param admin Which device owner this request is associated with. + * @param admin Which device admin this request is associated with. * @return {@code true} if security logging is enabled by device owner, {@code false} otherwise. - * @throws SecurityException if {@code admin} is not a device owner. + * @throws SecurityException if {@code admin} is not allowed to control security logging. */ public boolean isSecurityLoggingEnabled(@Nullable ComponentName admin) { throwIfParentInstance("isSecurityLoggingEnabled"); @@ -10021,20 +9985,21 @@ public class DevicePolicyManager { } /** - * Called by device owner to retrieve all new security logging entries since the last call to - * this API after device boots. + * Called by device owner or profile owner of an organization-owned managed profile to retrieve + * all new security logging entries since the last call to this API after device boots. * * <p> Access to the logs is rate limited and it will only return new logs after the device * owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}. * - * <p>If there is any other user or profile on the device, it must be affiliated with the - * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}. + * <p> When called by a device owner, if there is any other user or profile on the device, + * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. + * See {@link #isAffiliatedUser}. * - * @param admin Which device owner this request is associated with. + * @param admin Which device admin this request is associated with. * @return the new batch of security logs which is a list of {@link SecurityEvent}, * or {@code null} if rate limitation is exceeded or if logging is currently disabled. - * @throws SecurityException if {@code admin} is not a device owner, or there is at least one - * profile or secondary user that is not affiliated with the device. + * @throws SecurityException if {@code admin} is not allowed to access security logging, + * or there is at least one profile or secondary user that is not affiliated with the device. * @see #isAffiliatedUser * @see DeviceAdminReceiver#onSecurityLogsAvailable */ @@ -10167,21 +10132,23 @@ public class DevicePolicyManager { } /** - * Called by device owners to retrieve device logs from before the device's last reboot. + * Called by device owner or profile owner of an organization-owned managed profile to retrieve + * device logs from before the device's last reboot. * <p> * <strong> This API is not supported on all devices. Calling this API on unsupported devices * will result in {@code null} being returned. The device logs are retrieved from a RAM region * which is not guaranteed to be corruption-free during power cycles, as a result be cautious * about data corruption when parsing. </strong> * - * <p>If there is any other user or profile on the device, it must be affiliated with the - * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}. + * <p> When called by a device owner, if there is any other user or profile on the device, + * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. + * See {@link #isAffiliatedUser}. * - * @param admin Which device owner this request is associated with. + * @param admin Which device admin this request is associated with. * @return Device logs from before the latest reboot of the system, or {@code null} if this API * is not supported on the device. - * @throws SecurityException if {@code admin} is not a device owner, or there is at least one - * profile or secondary user that is not affiliated with the device. + * @throws SecurityException if {@code admin} is not allowed to access security logging, or + * there is at least one profile or secondary user that is not affiliated with the device. * @see #isAffiliatedUser * @see #retrieveSecurityLogs */ diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d161e06d354d..da48663145e1 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -272,7 +272,6 @@ interface IDevicePolicyManager { boolean hasLockdownAdminConfiguredNetworks(in ComponentName who); void setLocationEnabled(in ComponentName who, boolean locationEnabled); - void requestSetLocationProviderAllowed(in ComponentName who, in String provider, boolean providerAllowed); boolean setTime(in ComponentName who, long millis); boolean setTimeZone(in ComponentName who, String timeZone); diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index 91cf120032b5..fb7f573d5535 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -23,11 +23,13 @@ import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; +import android.os.UserHandle; import android.util.EventLog.Event; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Collection; import java.util.Objects; @@ -104,7 +106,8 @@ public class SecurityLog { /** * Indicates that a shell command was issued over ADB via {@code adb shell <command>} * The log entry contains a {@code String} payload containing the shell command, accessible - * via {@link SecurityEvent#getData()}. + * via {@link SecurityEvent#getData()}. If security logging is enabled on organization-owned + * managed profile devices, the shell command will be redacted to an empty string. */ public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND; @@ -133,6 +136,8 @@ public class SecurityLog { * <li> [3] app pid ({@code Integer}) * <li> [4] seinfo tag ({@code String}) * <li> [5] SHA-256 hash of the base APK in hexadecimal ({@code String}) + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START; @@ -205,7 +210,8 @@ public class SecurityLog { * following information about the event, encapsulated in an {@link Object} array and * accessible via {@link SecurityEvent#getData()}: * <li> [0] mount point ({@code String}) - * <li> [1] volume label ({@code String}). + * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned + * managed profile devices. */ public static final int TAG_MEDIA_MOUNT = SecurityLogTags.SECURITY_MEDIA_MOUNTED; @@ -214,7 +220,8 @@ public class SecurityLog { * following information about the event, encapsulated in an {@link Object} array and * accessible via {@link SecurityEvent#getData()}: * <li> [0] mount point ({@code String}) - * <li> [1] volume label ({@code String}). + * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned + * managed profile devices. */ public static final int TAG_MEDIA_UNMOUNT = SecurityLogTags.SECURITY_MEDIA_UNMOUNTED; @@ -340,6 +347,9 @@ public class SecurityLog { * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded) * <li> [1] alias of the key ({@code String}) * <li> [2] requesting process uid ({@code Integer}). + * + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_KEY_GENERATED = SecurityLogTags.SECURITY_KEY_GENERATED; @@ -351,6 +361,9 @@ public class SecurityLog { * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded) * <li> [1] alias of the key ({@code String}) * <li> [2] requesting process uid ({@code Integer}). + * + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_KEY_IMPORT = SecurityLogTags.SECURITY_KEY_IMPORTED; @@ -361,6 +374,9 @@ public class SecurityLog { * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded) * <li> [1] alias of the key ({@code String}) * <li> [2] requesting process uid ({@code Integer}). + * + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_KEY_DESTRUCTION = SecurityLogTags.SECURITY_KEY_DESTROYED; @@ -370,6 +386,11 @@ public class SecurityLog { * {@link Object} array and accessible via {@link SecurityEvent#getData()}: * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded) * <li> [1] subject of the certificate ({@code String}). + * <li> [2] which user the certificate is installed for ({@code Integer}), only available from + * version {@link android.os.Build.VERSION_CODES#R}. + * + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_CERT_AUTHORITY_INSTALLED = SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED; @@ -380,6 +401,11 @@ public class SecurityLog { * {@link Object} array and accessible via {@link SecurityEvent#getData()}: * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded) * <li> [1] subject of the certificate ({@code String}). + * <li> [2] which user the certificate is removed from ({@code Integer}), only available from + * version {@link android.os.Build.VERSION_CODES#R}. + * + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_CERT_AUTHORITY_REMOVED = SecurityLogTags.SECURITY_CERT_AUTHORITY_REMOVED; @@ -422,6 +448,9 @@ public class SecurityLog { * {@link SecurityEvent#getData()}: * <li> [0] alias of the key ({@code String}) * <li> [1] owner application uid ({@code Integer}). + * + * If security logging is enabled on organization-owned managed profile devices, only events + * happening inside the managed profile will be visible. */ public static final int TAG_KEY_INTEGRITY_VIOLATION = SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION; @@ -535,6 +564,16 @@ public class SecurityLog { return mEvent.getData(); } + /** @hide */ + public int getIntegerData(int index) { + return (Integer) ((Object[]) mEvent.getData())[index]; + } + + /** @hide */ + public String getStringData(int index) { + return (String) ((Object[]) mEvent.getData())[index]; + } + /** * @hide */ @@ -554,7 +593,7 @@ public class SecurityLog { * Returns severity level for the event. */ public @SecurityLogLevel int getLogLevel() { - switch (mEvent.getTag()) { + switch (getTag()) { case TAG_ADB_SHELL_INTERACTIVE: case TAG_ADB_SHELL_CMD: case TAG_SYNC_RECV_FILE: @@ -608,6 +647,75 @@ public class SecurityLog { return array.length >= 1 && array[0] instanceof Integer && (Integer) array[0] != 0; } + /** + * Returns a copy of the security event suitable to be consumed by the provided user. + * This method will either return the original event itself if the event does not contain + * any sensitive data; or a copy of itself but with sensitive information redacted; or + * {@code null} if the entire event should not be accessed by the given user. + * + * @param accessingUser which user this security event is to be accessed, must be a + * concrete user id. + * @hide + */ + public SecurityEvent redact(int accessingUser) { + // Which user the event is associated with, for the purpose of log redaction. + final int userId; + switch (getTag()) { + case SecurityLog.TAG_ADB_SHELL_CMD: + return new SecurityEvent(getId(), mEvent.withNewData("").getBytes()); + case SecurityLog.TAG_MEDIA_MOUNT: + case SecurityLog.TAG_MEDIA_UNMOUNT: + // Partial redaction + String mountPoint; + try { + mountPoint = getStringData(0); + } catch (Exception e) { + return null; + } + return new SecurityEvent(getId(), + mEvent.withNewData(new Object[] {mountPoint, ""}).getBytes()); + case SecurityLog.TAG_APP_PROCESS_START: + try { + userId = UserHandle.getUserId(getIntegerData(2)); + } catch (Exception e) { + return null; + } + break; + case SecurityLog.TAG_CERT_AUTHORITY_INSTALLED: + case SecurityLog.TAG_CERT_AUTHORITY_REMOVED: + try { + userId = getIntegerData(2); + } catch (Exception e) { + return null; + } + break; + case SecurityLog.TAG_KEY_GENERATED: + case SecurityLog.TAG_KEY_IMPORT: + case SecurityLog.TAG_KEY_DESTRUCTION: + try { + userId = UserHandle.getUserId(getIntegerData(2)); + } catch (Exception e) { + return null; + } + break; + case SecurityLog.TAG_KEY_INTEGRITY_VIOLATION: + try { + userId = UserHandle.getUserId(getIntegerData(1)); + } catch (Exception e) { + return null; + } + break; + default: + userId = UserHandle.USER_NULL; + } + // If the event is not user-specific, or matches the accessing user, return it + // unmodified, else redact by returning null + if (userId == UserHandle.USER_NULL || accessingUser == userId) { + return this; + } else { + return null; + } + } @Override public int describeContents() { @@ -657,6 +765,30 @@ public class SecurityLog { return other != null && mEvent.equals(other.mEvent); } } + + /** + * Redacts events in-place according to which user will consume the events. + * + * @param accessingUser which user will consume the redacted events, or UserHandle.USER_ALL if + * redaction should be skipped. + * @hide + */ + public static void redactEvents(ArrayList<SecurityEvent> logList, int accessingUser) { + if (accessingUser == UserHandle.USER_ALL) return; + int end = 0; + for (int i = 0; i < logList.size(); i++) { + SecurityEvent event = logList.get(i); + event = event.redact(accessingUser); + if (event != null) { + logList.set(end, event); + end++; + } + } + for (int i = logList.size() - 1; i >= end; i--) { + logList.remove(i); + } + } + /** * Retrieve all security logs and return immediately. * @hide diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags index 4e67fe253715..100fd4cbd40f 100644 --- a/core/java/android/app/admin/SecurityLogTags.logtags +++ b/core/java/android/app/admin/SecurityLogTags.logtags @@ -33,8 +33,8 @@ option java_package android.app.admin 210026 security_key_destroyed (success|1),(key_id|3),(uid|1) 210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3) 210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3) -210029 security_cert_authority_installed (success|1),(subject|3) -210030 security_cert_authority_removed (success|1),(subject|3) +210029 security_cert_authority_installed (success|1),(subject|3),(target_user|1) +210030 security_cert_authority_removed (success|1),(subject|3),(target_user|1) 210031 security_crypto_self_test_completed (success|1) 210032 security_key_integrity_violation (key_id|3),(uid|1) 210033 security_cert_validation_failure (reason|3) diff --git a/core/java/android/app/trust/IStrongAuthTracker.aidl b/core/java/android/app/trust/IStrongAuthTracker.aidl index 36c71bff7682..6d543963fe2a 100644 --- a/core/java/android/app/trust/IStrongAuthTracker.aidl +++ b/core/java/android/app/trust/IStrongAuthTracker.aidl @@ -23,4 +23,5 @@ package android.app.trust; */ oneway interface IStrongAuthTracker { void onStrongAuthRequiredChanged(int strongAuthRequired, int userId); + void onIsNonStrongBiometricAllowedChanged(boolean allowed, int userId); }
\ No newline at end of file diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index e1942da8ac7f..bd3298c79fff 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -580,6 +580,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override + public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri, + RemoteCallback callback) { + final Bundle result = new Bundle(); + result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + canonicalize(callingPkg, featureId, uri)); + callback.sendResult(result); + } + + @Override public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 0f1442d864ba..7bc59013bcfe 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -359,6 +359,16 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } + case CANONICALIZE_ASYNC_TRANSACTION: { + data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); + String featureId = data.readString(); + Uri uri = Uri.CREATOR.createFromParcel(data); + RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data); + canonicalizeAsync(callingPkg, featureId, uri, callback); + return true; + } + case UNCANONICALIZE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); @@ -823,6 +833,25 @@ final class ContentProviderProxy implements IContentProvider } @Override + /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId, + Uri uri, RemoteCallback callback) throws RemoteException { + Parcel data = Parcel.obtain(); + try { + data.writeInterfaceToken(IContentProvider.descriptor); + + data.writeString(callingPkg); + data.writeString(featureId); + uri.writeToParcel(data, 0); + callback.writeToParcel(data, 0); + + mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null, + Binder.FLAG_ONEWAY); + } finally { + data.recycle(); + } + } + + @Override public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 0e0161ff4e9f..b748cfa775ed 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -712,14 +712,17 @@ public abstract class ContentResolver implements ContentInterface { * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}. * @hide */ - public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS = + public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS = CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000; + // Timeout given a ContentProvider that has already been started and connected to. + private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000; + // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how // long ActivityManagerService is giving a content provider to get published if a new process // needs to be started for that. - private static final int GET_TYPE_TIMEOUT_MILLIS = - CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000; + private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS = + CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS; public ContentResolver(@Nullable Context context) { this(context, null); @@ -833,10 +836,10 @@ public abstract class ContentResolver implements ContentInterface { IContentProvider provider = acquireExistingProvider(url); if (provider != null) { try { - final GetTypeResultListener resultListener = new GetTypeResultListener(); + final StringResultListener resultListener = new StringResultListener(); provider.getTypeAsync(url, new RemoteCallback(resultListener)); - resultListener.waitForResult(); - return resultListener.type; + resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -854,13 +857,13 @@ public abstract class ContentResolver implements ContentInterface { } try { - GetTypeResultListener resultListener = new GetTypeResultListener(); + final StringResultListener resultListener = new StringResultListener(); ActivityManager.getService().getProviderMimeTypeAsync( ContentProvider.getUriWithoutUserId(url), resolveUserId(url), new RemoteCallback(resultListener)); - resultListener.waitForResult(); - return resultListener.type; + resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS); + return resultListener.result; } catch (RemoteException e) { // We just failed to send a oneway request to the System Server. Nothing to do. return null; @@ -870,27 +873,29 @@ public abstract class ContentResolver implements ContentInterface { } } - private static class GetTypeResultListener implements RemoteCallback.OnResultListener { + private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener { @GuardedBy("this") public boolean done; @GuardedBy("this") - public String type; + public T result; @Override public void onResult(Bundle result) { synchronized (this) { - type = result.getString(REMOTE_CALLBACK_RESULT); + this.result = getResultFromBundle(result); done = true; notifyAll(); } } - public void waitForResult() { + protected abstract T getResultFromBundle(Bundle result); + + public void waitForResult(long timeout) { synchronized (this) { if (!done) { try { - wait(GET_TYPE_TIMEOUT_MILLIS); + wait(timeout); } catch (InterruptedException e) { // Ignore } @@ -899,6 +904,20 @@ public abstract class ContentResolver implements ContentInterface { } } + private static class StringResultListener extends ResultListener<String> { + @Override + protected String getResultFromBundle(Bundle result) { + return result.getString(REMOTE_CALLBACK_RESULT); + } + } + + private static class UriResultListener extends ResultListener<Uri> { + @Override + protected Uri getResultFromBundle(Bundle result) { + return result.getParcelable(REMOTE_CALLBACK_RESULT); + } + } + /** * Query for the possible MIME types for the representations the given * content URL can be returned when opened as as stream with @@ -1192,7 +1211,11 @@ public abstract class ContentResolver implements ContentInterface { } try { - return provider.canonicalize(mPackageName, mFeatureId, url); + final UriResultListener resultListener = new UriResultListener(); + provider.canonicalizeAsync(mPackageName, mFeatureId, url, + new RemoteCallback(resultListener)); + resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 4658ba109d5f..37643da375df 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -45,7 +45,7 @@ public interface IContentProvider extends IInterface { public String getType(Uri url) throws RemoteException; /** - * An oneway version of getType. The functionality is exactly the same, except that the + * A oneway version of getType. The functionality is exactly the same, except that the * call returns immediately, and the resulting type is returned when available via * a binder callback. */ @@ -126,6 +126,14 @@ public interface IContentProvider extends IInterface { public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException; + /** + * A oneway version of canonicalize. The functionality is exactly the same, except that the + * call returns immediately, and the resulting type is returned when available via + * a binder callback. + */ + void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri, + RemoteCallback callback) throws RemoteException; + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException; @@ -162,4 +170,5 @@ public interface IContentProvider extends IInterface { static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26; static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27; int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28; + int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29; } diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index b5f4f806d244..8a89840876d9 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -95,9 +95,9 @@ interface ILauncherApps { void registerShortcutChangeCallback(String callingPackage, long changedSince, String packageName, in List shortcutIds, in List<LocusId> locusIds, - in ComponentName componentName, int flags, in IShortcutChangeCallback callback, - int callbackId); - void unregisterShortcutChangeCallback(String callingPackage, int callbackId); + in ComponentName componentName, int flags, in IShortcutChangeCallback callback); + void unregisterShortcutChangeCallback(String callingPackage, + in IShortcutChangeCallback callback); void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 5aa0208480c1..86242fd6f82f 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -17,6 +17,7 @@ package android.content.pm; import static android.Manifest.permission; + import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; @@ -161,7 +162,7 @@ public class LauncherApps { private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>(); private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>(); - private final Map<Integer, Pair<Executor, ShortcutChangeCallback>> + private final Map<ShortcutChangeCallback, Pair<Executor, IShortcutChangeCallback>> mShortcutChangeCallbacks = new HashMap<>(); /** @@ -549,8 +550,8 @@ public class LauncherApps { android.content.pm.IShortcutChangeCallback.Stub { private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences; - ShortcutChangeCallbackProxy(Pair<Executor, ShortcutChangeCallback> remoteReferences) { - mRemoteReferences = new WeakReference<>(remoteReferences); + ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback) { + mRemoteReferences = new WeakReference<>(new Pair<>(executor, callback)); } @Override @@ -1753,14 +1754,12 @@ public class LauncherApps { Objects.requireNonNull(executor, "Executor cannot be null"); synchronized (mShortcutChangeCallbacks) { - final int callbackId = callback.hashCode(); - final Pair<Executor, ShortcutChangeCallback> state = new Pair<>(executor, callback); - mShortcutChangeCallbacks.put(callbackId, state); + IShortcutChangeCallback proxy = new ShortcutChangeCallbackProxy(executor, callback); + mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy)); try { mService.registerShortcutChangeCallback(mContext.getPackageName(), query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds, - query.mActivity, query.mQueryFlags, new ShortcutChangeCallbackProxy(state), - callbackId); + query.mActivity, query.mQueryFlags, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1779,12 +1778,10 @@ public class LauncherApps { Objects.requireNonNull(callback, "Callback cannot be null"); synchronized (mShortcutChangeCallbacks) { - final int callbackId = callback.hashCode(); - if (mShortcutChangeCallbacks.containsKey(callbackId)) { - mShortcutChangeCallbacks.remove(callbackId); + if (mShortcutChangeCallbacks.containsKey(callback)) { + IShortcutChangeCallback proxy = mShortcutChangeCallbacks.remove(callback).second; try { - mService.unregisterShortcutChangeCallback(mContext.getPackageName(), - callbackId); + mService.unregisterShortcutChangeCallback(mContext.getPackageName(), proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 3a934211c7e5..a50ce92a70bf 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -65,6 +65,9 @@ public abstract class ShortcutServiceInternal { public abstract void addListener(@NonNull ShortcutChangeListener listener); + public abstract void addShortcutChangeCallback( + @NonNull LauncherApps.ShortcutChangeCallback callback); + public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId); diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java index 6ad7faed16d8..ce26cb0d7adc 100644 --- a/core/java/android/hardware/display/DeviceProductInfo.java +++ b/core/java/android/hardware/display/DeviceProductInfo.java @@ -28,21 +28,21 @@ import java.util.Objects; * @hide */ public final class DeviceProductInfo implements Parcelable { - final private String mName; - final private String mManufacturerPnpId; - final private String mProductId; - final private Integer mModelYear; - final private ManufactureDate mManufactureDate; + private final String mName; + private final String mManufacturerPnpId; + private final String mProductId; + private final Integer mModelYear; + private final ManufactureDate mManufactureDate; public DeviceProductInfo( String name, String manufacturerPnpId, - String productCode, + String productId, Integer modelYear, ManufactureDate manufactureDate) { this.mName = name; this.mManufacturerPnpId = manufacturerPnpId; - this.mProductId = productCode; + this.mProductId = productId; this.mModelYear = modelYear; this.mManufactureDate = manufactureDate; } @@ -158,8 +158,8 @@ public final class DeviceProductInfo implements Parcelable { * @hide */ public static class ManufactureDate implements Parcelable { - final private Integer mWeek; - final private Integer mYear; + private final Integer mWeek; + private final Integer mYear; public ManufactureDate(Integer week, Integer year) { mWeek = week; diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 3a0660db2b05..9b9889ed5cb2 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -97,8 +97,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } @Override // binder call - public void onAuthenticationSucceeded(long deviceId, Face face, int userId) { - mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget(); + public void onAuthenticationSucceeded(long deviceId, Face face, int userId, + boolean isStrongBiometric) { + mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0, + face).sendToTarget(); } @Override // binder call @@ -814,6 +816,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private Face mFace; private CryptoObject mCryptoObject; private int mUserId; + private boolean mIsStrongBiometric; /** * Authentication result @@ -822,10 +825,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @param face the recognized face data, if allowed. * @hide */ - public AuthenticationResult(CryptoObject crypto, Face face, int userId) { + public AuthenticationResult(CryptoObject crypto, Face face, int userId, + boolean isStrongBiometric) { mCryptoObject = crypto; mFace = face; mUserId = userId; + mIsStrongBiometric = isStrongBiometric; } /** @@ -857,6 +862,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public int getUserId() { return mUserId; } + + /** + * Check whether the strength of the face modality associated with this operation is strong + * (i.e. not weak or convenience). + * + * @hide + */ + public boolean isStrongBiometric() { + return mIsStrongBiometric; + } } /** @@ -1056,7 +1071,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan msg.arg2 /* vendorCode */); break; case MSG_AUTHENTICATION_SUCCEEDED: - sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */); + sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */, + msg.arg2 == 1 /* isStrongBiometric */); break; case MSG_AUTHENTICATION_FAILED: sendAuthenticatedFailed(); @@ -1133,10 +1149,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } - private void sendAuthenticatedSucceeded(Face face, int userId) { + private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) { if (mAuthenticationCallback != null) { final AuthenticationResult result = - new AuthenticationResult(mCryptoObject, face, userId); + new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric); mAuthenticationCallback.onAuthenticationSucceeded(result); } } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 8ba24735c039..63182744eb0a 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -110,4 +110,7 @@ interface IFaceService { void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName); void userActivity(); + + // Initialize the OEM configured biometric strength + void initConfiguredStrength(int strength); } diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index 10f9c435c415..7582308e8016 100644 --- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl +++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl @@ -24,7 +24,8 @@ import android.hardware.face.Face; oneway interface IFaceServiceReceiver { void onEnrollResult(long deviceId, int faceId, int remaining); void onAcquired(long deviceId, int acquiredInfo, int vendorCode); - void onAuthenticationSucceeded(long deviceId, in Face face, int userId); + void onAuthenticationSucceeded(long deviceId, in Face face, int userId, + boolean isStrongBiometric); void onAuthenticationFailed(long deviceId); void onError(long deviceId, int error, int vendorCode); void onRemoved(long deviceId, int faceId, int remaining); diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index f301a5cddc9c..dc8ea5efcc17 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -176,6 +176,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private Fingerprint mFingerprint; private CryptoObject mCryptoObject; private int mUserId; + private boolean mIsStrongBiometric; /** * Authentication result @@ -184,10 +185,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @param fingerprint the recognized fingerprint data, if allowed. * @hide */ - public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId) { + public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId, + boolean isStrongBiometric) { mCryptoObject = crypto; mFingerprint = fingerprint; mUserId = userId; + mIsStrongBiometric = isStrongBiometric; } /** @@ -211,6 +214,15 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ public int getUserId() { return mUserId; } + + /** + * Check whether the strength of the fingerprint modality associated with this operation is + * strong (i.e. not weak or convenience). + * @hide + */ + public boolean isStrongBiometric() { + return mIsStrongBiometric; + } }; /** @@ -833,7 +845,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing msg.arg2 /* vendorCode */); break; case MSG_AUTHENTICATION_SUCCEEDED: - sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */); + sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */, + msg.arg2 == 1 /* isStrongBiometric */); break; case MSG_AUTHENTICATION_FAILED: sendAuthenticatedFailed(); @@ -890,10 +903,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) { + private void sendAuthenticatedSucceeded(Fingerprint fp, int userId, boolean isStrongBiometric) { if (mAuthenticationCallback != null) { final AuthenticationResult result = - new AuthenticationResult(mCryptoObject, fp, userId); + new AuthenticationResult(mCryptoObject, fp, userId, isStrongBiometric); mAuthenticationCallback.onAuthenticationSucceeded(result); } } @@ -1078,8 +1091,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } @Override // binder call - public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) { - mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget(); + public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId, + boolean isStrongBiometric) { + mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0, + fp).sendToTarget(); } @Override // binder call diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index f2ffd08d5bc8..028544854235 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -113,4 +113,7 @@ interface IFingerprintService { // Removes a callback set by addClientActiveCallback void removeClientActiveCallback(IFingerprintClientActiveCallback callback); + + // Initialize the OEM configured biometric strength + void initConfiguredStrength(int strength); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index cf1c94ea5346..4412cee31bb0 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -24,7 +24,8 @@ import android.hardware.fingerprint.Fingerprint; oneway interface IFingerprintServiceReceiver { void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining); void onAcquired(long deviceId, int acquiredInfo, int vendorCode); - void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId); + void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId, + boolean isStrongBiometric); void onAuthenticationFailed(long deviceId); void onError(long deviceId, int error, int vendorCode); void onRemoved(long deviceId, int fingerId, int groupId, int remaining); diff --git a/core/java/android/hardware/iris/IIrisService.aidl b/core/java/android/hardware/iris/IIrisService.aidl index 8cf3c13b1465..5ef0a0c55a18 100644 --- a/core/java/android/hardware/iris/IIrisService.aidl +++ b/core/java/android/hardware/iris/IIrisService.aidl @@ -21,4 +21,6 @@ package android.hardware.iris; * @hide */ interface IIrisService { -}
\ No newline at end of file + // Initialize the OEM configured biometric strength + void initConfiguredStrength(int strength); +} diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java index d207e7960d34..ae17378cfc4c 100644 --- a/core/java/android/net/NetworkScore.java +++ b/core/java/android/net/NetworkScore.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -83,69 +84,10 @@ public final class NetworkScore implements Parcelable { uplinkBandwidthKBps = uplinkBandwidth; } - /** - * Evaluate whether a metrics codes for faster network is faster than another. - * - * This is a simple comparison of expected speeds. If either of the tested attributes - * are unknown, this returns zero. This implementation just assumes downlink bandwidth - * is more important than uplink bandwidth, which is more important than latency. This - * is not a very good way of evaluating network speed, but it's a start. - * TODO : do something more representative of how fast the network feels - * - * @param other the Metrics to evaluate against - * @return a negative integer, zero, or a positive integer as this metrics is worse than, - * equally good as (or unknown), or better than the passed Metrics. - * @see #compareToPreferringKnown(Metrics) - * @hide - */ - // Can't implement Comparable<Metrics> because this is @hide. - public int compareTo(@NonNull final Metrics other) { - if (downlinkBandwidthKBps != BANDWIDTH_UNKNOWN - && other.downlinkBandwidthKBps != BANDWIDTH_UNKNOWN) { - if (downlinkBandwidthKBps > other.downlinkBandwidthKBps) return 1; - if (downlinkBandwidthKBps < other.downlinkBandwidthKBps) return -1; - } - if (uplinkBandwidthKBps != BANDWIDTH_UNKNOWN - && other.uplinkBandwidthKBps != BANDWIDTH_UNKNOWN) { - if (uplinkBandwidthKBps > other.uplinkBandwidthKBps) return 1; - if (uplinkBandwidthKBps < other.uplinkBandwidthKBps) return -1; - } - if (latencyMs != LATENCY_UNKNOWN && other.latencyMs != LATENCY_UNKNOWN) { - // Latency : lower is better - if (latencyMs > other.latencyMs) return -1; - if (latencyMs < other.latencyMs) return 1; - } - return 0; - } - - /** - * Evaluate whether a metrics codes for faster network is faster than another. - * - * This is a simple comparison of expected speeds. If either of the tested attributes - * are unknown, this prefers the known attributes. This implementation just assumes - * downlink bandwidth is more important than uplink bandwidth, which is more important than - * latency. This is not a very good way of evaluating network speed, but it's a start. - * TODO : do something more representative of how fast the network feels - * - * @param other the Metrics to evaluate against - * @return a negative integer, zero, or a positive integer as this metrics is worse than, - * equally good as (or unknown), or better than the passed Metrics. - * @see #compareTo(Metrics) - * @hide - */ - public int compareToPreferringKnown(@NonNull final Metrics other) { - if (downlinkBandwidthKBps > other.downlinkBandwidthKBps) return 1; - if (downlinkBandwidthKBps < other.downlinkBandwidthKBps) return -1; - if (uplinkBandwidthKBps > other.uplinkBandwidthKBps) return 1; - if (uplinkBandwidthKBps < other.uplinkBandwidthKBps) return -1; - // Latency : lower is better - return -Integer.compare(latencyMs, other.latencyMs); - } - /** toString */ public String toString() { return "latency = " + latencyMs + " downlinkBandwidth = " + downlinkBandwidthKBps - + " uplinkBandwidth = " + uplinkBandwidthKBps; + + "uplinkBandwidth = " + uplinkBandwidthKBps; } @NonNull @@ -405,33 +347,6 @@ public final class NetworkScore implements Parcelable { return mLegacyScore; } - /** - * Use the metrics to evaluate whether a network is faster than another. - * - * This is purely based on the metrics, and explicitly ignores policy or exiting. It's - * provided to get a decision on two networks when policy can not decide, or to evaluate - * how a network is expected to compare to another if it should validate. - * - * @param other the score to evaluate against - * @return whether this network is probably faster than the other - * @hide - */ - public boolean probablyFasterThan(@NonNull final NetworkScore other) { - if (mLegacyScore > other.mLegacyScore) return true; - final int atEndToEnd = mEndToEndMetrics.compareTo(other.mEndToEndMetrics); - if (atEndToEnd > 0) return true; - if (atEndToEnd < 0) return false; - final int atLinkLayer = mLinkLayerMetrics.compareTo(other.mLinkLayerMetrics); - if (atLinkLayer > 0) return true; - if (atLinkLayer < 0) return false; - final int atEndToEndPreferringKnown = - mEndToEndMetrics.compareToPreferringKnown(other.mEndToEndMetrics); - if (atEndToEndPreferringKnown > 0) return true; - if (atEndToEndPreferringKnown < 0) return false; - // If this returns 0, neither is "probably faster" so just return false. - return mLinkLayerMetrics.compareToPreferringKnown(other.mLinkLayerMetrics) > 0; - } - /** Builder for NetworkScore. */ public static class Builder { private int mPolicy = 0; @@ -439,27 +354,17 @@ public final class NetworkScore implements Parcelable { private Metrics mLinkLayerMetrics = new Metrics(Metrics.LATENCY_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN); @NonNull - private Metrics mEndToEndMetrics = new Metrics(Metrics.LATENCY_UNKNOWN, + private Metrics mEndToMetrics = new Metrics(Metrics.LATENCY_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN); private int mSignalStrength = UNKNOWN_SIGNAL_STRENGTH; private int mRange = RANGE_UNKNOWN; private boolean mExiting = false; private int mLegacyScore = 0; + @NonNull private Bundle mExtensions = new Bundle(); /** Create a new builder. */ public Builder() { } - /** @hide */ - public Builder(@NonNull final NetworkScore source) { - mPolicy = source.mPolicy; - mLinkLayerMetrics = source.mLinkLayerMetrics; - mEndToEndMetrics = source.mEndToEndMetrics; - mSignalStrength = source.mSignalStrength; - mRange = source.mRange; - mExiting = source.mExiting; - mLegacyScore = source.mLegacyScore; - } - /** Add a policy flag. */ @NonNull public Builder addPolicy(@Policy final int policy) { mPolicy |= policy; @@ -480,7 +385,7 @@ public final class NetworkScore implements Parcelable { /** Set the end-to-end metrics. */ @NonNull public Builder setEndToEndMetrics(@NonNull final Metrics endToEndMetrics) { - mEndToEndMetrics = endToEndMetrics; + mEndToMetrics = endToEndMetrics; return this; } @@ -512,7 +417,7 @@ public final class NetworkScore implements Parcelable { /** Build the NetworkScore object represented by this builder. */ @NonNull public NetworkScore build() { - return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToEndMetrics, + return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToMetrics, mSignalStrength, mRange, mExiting, mLegacyScore); } } diff --git a/core/java/android/os/CoolingDevice.aidl b/core/java/android/os/CoolingDevice.aidl index 478e4bd71e6d..c6432fd31d20 100644 --- a/core/java/android/os/CoolingDevice.aidl +++ b/core/java/android/os/CoolingDevice.aidl @@ -16,4 +16,4 @@ package android.os; -parcelable CoolingDevice; +parcelable CoolingDevice cpp_header "android/CoolingDevice.h"; diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl index ad002335a010..c6c8adc4d8a9 100644 --- a/core/java/android/os/IThermalService.aidl +++ b/core/java/android/os/IThermalService.aidl @@ -56,7 +56,7 @@ interface IThermalService { * @return list of {@link android.os.Temperature}. * {@hide} */ - List<Temperature> getCurrentTemperatures(); + Temperature[] getCurrentTemperatures(); /** * Get current temperature with its throttling status on given temperature type. @@ -64,7 +64,7 @@ interface IThermalService { * @return list of {@link android.os.Temperature}. * {@hide} */ - List<Temperature> getCurrentTemperaturesWithType(in int type); + Temperature[] getCurrentTemperaturesWithType(in int type); /** * Register a listener for thermal status change. @@ -94,7 +94,7 @@ interface IThermalService { * @return list of {@link android.os.CoolingDevice}. * {@hide} */ - List<CoolingDevice> getCurrentCoolingDevices(); + CoolingDevice[] getCurrentCoolingDevices(); /** * Get current cooling devices on given type. @@ -102,7 +102,8 @@ interface IThermalService { * @return list of {@link android.os.CoolingDevice}. * {@hide} */ - List<CoolingDevice> getCurrentCoolingDevicesWithType(in int type); + + CoolingDevice[] getCurrentCoolingDevicesWithType(in int type); /** * @param forecastSeconds how many seconds ahead to forecast the provided headroom diff --git a/core/java/android/os/Temperature.aidl b/core/java/android/os/Temperature.aidl index 708c08fbe8b0..5268dd5441e3 100644 --- a/core/java/android/os/Temperature.aidl +++ b/core/java/android/os/Temperature.aidl @@ -16,4 +16,4 @@ package android.os; -parcelable Temperature; +parcelable Temperature cpp_header "android/Temperature.h"; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 84fd58063d39..0f2060a36ac9 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -63,6 +63,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Manages users and user details on a multi-user system. There are two major categories of @@ -2717,10 +2718,11 @@ public class UserManager { Manifest.permission.CREATE_USERS}) @UserHandleAware public @Nullable UserHandle createProfile(@NonNull String name, @NonNull String userType, - @Nullable String[] disallowedPackages) throws UserOperationException { + @NonNull Set<String> disallowedPackages) throws UserOperationException { try { return mService.createProfileForUserWithThrow(name, userType, 0, - mUserId, disallowedPackages).getUserHandle(); + mUserId, disallowedPackages.toArray( + new String[disallowedPackages.size()])).getUserHandle(); } catch (ServiceSpecificException e) { return returnNullOrThrowUserOperationException(e, mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R); @@ -3354,19 +3356,46 @@ public class UserManager { } /** - * Returns a list of ids for profiles associated with the context user including the user - * itself. + * Returns a list of ids for enabled profiles associated with the context user including the + * user itself. * - * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles * @return A non-empty list of UserHandles associated with the calling user. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}, conditional = true) + @UserHandleAware + public @NonNull List<UserHandle> getEnabledProfiles() { + return getProfiles(true); + } + + /** + * Returns a list of ids for all profiles associated with the context user including the user + * itself. * + * @return A non-empty list of UserHandles associated with the calling user. * @hide */ @SystemApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}, conditional = true) @UserHandleAware - public @NonNull List<UserHandle> getUserProfiles(boolean enabledOnly) { + public @NonNull List<UserHandle> getAllProfiles() { + return getProfiles(false); + } + + /** + * Returns a list of ids for profiles associated with the context user including the user + * itself. + * + * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles + * @return A non-empty list of UserHandles associated with the calling user. + */ + @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}, conditional = true) + @UserHandleAware + private @NonNull List<UserHandle> getProfiles(boolean enabledOnly) { final int[] userIds = getProfileIds(mUserId, enabledOnly); final List<UserHandle> result = new ArrayList<>(userIds.length); for (int userId : userIds) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b9abdf83e260..b89827140da5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9275,11 +9275,17 @@ public final class Settings { public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user"; /** - * Whether ADB is enabled. + * Whether ADB over USB is enabled. */ public static final String ADB_ENABLED = "adb_enabled"; /** + * Whether ADB over Wifi is enabled. + * @hide + */ + public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled"; + + /** * Whether Views are allowed to save their attribute data. * @hide */ diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index cb20db90f549..b23d0cd4bd93 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -15,11 +15,14 @@ */ package android.service.controls; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Service; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; @@ -51,6 +54,20 @@ public abstract class ControlsProviderService extends Service { @SdkConstant(SdkConstantType.SERVICE_ACTION) public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService"; + + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ADD_CONTROL = + "android.service.controls.action.ADD_CONTROL"; + + /** + * @hide + */ + public static final String EXTRA_CONTROL = + "android.service.controls.extra.CONTROL"; + /** * @hide */ @@ -325,6 +342,33 @@ public abstract class ControlsProviderService extends Service { } } + /** + * Request SystemUI to prompt the user to add a control to favorites. + * + * @param context A context + * @param componentName Component name of the {@link ControlsProviderService} + * @param control A stateless control to show to the user + */ + public static void requestAddControl(@NonNull Context context, + @NonNull ComponentName componentName, + @NonNull Control control) { + Preconditions.checkNotNull(context); + Preconditions.checkNotNull(componentName); + Preconditions.checkNotNull(control); + final ComponentName sysuiComponent = ComponentName.unflattenFromString( + context.getResources().getString( + com.android.internal.R.string.config_systemUIServiceComponent)); + Intent intent = new Intent(ACTION_ADD_CONTROL); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName); + intent.setPackage(sysuiComponent.getPackageName()); + if (isStatelessControl(control)) { + intent.putExtra(EXTRA_CONTROL, control); + } else { + intent.putExtra(EXTRA_CONTROL, new Control.StatelessBuilder(control).build()); + } + context.sendBroadcast(intent, Manifest.permission.BIND_CONTROLS); + } + private static class SubscriptionAdapter extends IControlsSubscription.Stub { final Subscription mSubscription; diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index c9dc05b5aeb5..ead4e46cd28b 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -16,6 +16,8 @@ package android.util; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -62,7 +64,7 @@ public class EventLog { private Exception mLastWtf; // Layout of event log entry received from Android logger. - // see system/core/include/log/log.h + // see system/core/liblog/include/log/log_read.h private static final int LENGTH_OFFSET = 0; private static final int HEADER_SIZE_OFFSET = 2; private static final int PROCESS_OFFSET = 4; @@ -73,7 +75,7 @@ public class EventLog { // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET private static final int V1_PAYLOAD_START = 20; - private static final int DATA_OFFSET = 4; + private static final int TAG_LENGTH = 4; // Value types private static final byte INT_TYPE = 0; @@ -121,26 +123,26 @@ public class EventLog { /** @return the type tag code of the entry */ public int getTag() { - int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); - if (offset == 0) { - offset = V1_PAYLOAD_START; - } - return mBuffer.getInt(offset); + return mBuffer.getInt(getHeaderSize()); } + private int getHeaderSize() { + int length = mBuffer.getShort(HEADER_SIZE_OFFSET); + if (length != 0) { + return length; + } + return V1_PAYLOAD_START; + } /** @return one of Integer, Long, Float, String, null, or Object[] of same. */ public synchronized Object getData() { try { - int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); - if (offset == 0) { - offset = V1_PAYLOAD_START; - } + int offset = getHeaderSize(); mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET)); - if ((offset + DATA_OFFSET) >= mBuffer.limit()) { + if ((offset + TAG_LENGTH) >= mBuffer.limit()) { // no payload return null; } - mBuffer.position(offset + DATA_OFFSET); // Just after the tag. + mBuffer.position(offset + TAG_LENGTH); // Just after the tag. return decodeObject(); } catch (IllegalArgumentException e) { Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); @@ -153,6 +155,28 @@ public class EventLog { } } + /** + * Construct a new EventLog object from the current object, copying all log metadata + * but replacing the actual payload with the content provided. + * @hide + */ + public Event withNewData(@Nullable Object object) { + byte[] payload = encodeObject(object); + if (payload.length > 65535 - TAG_LENGTH) { + throw new IllegalArgumentException("Payload too long"); + } + int headerLength = getHeaderSize(); + byte[] newBytes = new byte[headerLength + TAG_LENGTH + payload.length]; + // Copy header (including the 4 bytes of tag integer at the beginning of payload) + System.arraycopy(mBuffer.array(), 0, newBytes, 0, headerLength + TAG_LENGTH); + // Fill in encoded objects + System.arraycopy(payload, 0, newBytes, headerLength + TAG_LENGTH, payload.length); + Event result = new Event(newBytes); + // Patch payload length in header + result.mBuffer.putShort(LENGTH_OFFSET, (short) (payload.length + TAG_LENGTH)); + return result; + } + /** @return the loggable item at the current position in mBuffer. */ private Object decodeObject() { byte type = mBuffer.get(); @@ -190,6 +214,66 @@ public class EventLog { } } + private static @NonNull byte[] encodeObject(@Nullable Object object) { + if (object == null) { + return new byte[0]; + } + if (object instanceof Integer) { + return ByteBuffer.allocate(1 + 4) + .order(ByteOrder.nativeOrder()) + .put(INT_TYPE) + .putInt((Integer) object) + .array(); + } else if (object instanceof Long) { + return ByteBuffer.allocate(1 + 8) + .order(ByteOrder.nativeOrder()) + .put(LONG_TYPE) + .putLong((Long) object) + .array(); + } else if (object instanceof Float) { + return ByteBuffer.allocate(1 + 4) + .order(ByteOrder.nativeOrder()) + .put(FLOAT_TYPE) + .putFloat((Float) object) + .array(); + } else if (object instanceof String) { + String string = (String) object; + byte[] bytes; + try { + bytes = string.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + bytes = new byte[0]; + } + return ByteBuffer.allocate(1 + 4 + bytes.length) + .order(ByteOrder.nativeOrder()) + .put(STRING_TYPE) + .putInt(bytes.length) + .put(bytes) + .array(); + } else if (object instanceof Object[]) { + Object[] objects = (Object[]) object; + if (objects.length > 255) { + throw new IllegalArgumentException("Object array too long"); + } + byte[][] bytes = new byte[objects.length][]; + int totalLength = 0; + for (int i = 0; i < objects.length; i++) { + bytes[i] = encodeObject(objects[i]); + totalLength += bytes[i].length; + } + ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + totalLength) + .order(ByteOrder.nativeOrder()) + .put(LIST_TYPE) + .put((byte) objects.length); + for (int i = 0; i < objects.length; i++) { + buffer.put(bytes[i]); + } + return buffer.array(); + } else { + throw new IllegalArgumentException("Unknown object type " + object); + } + } + /** @hide */ public static Event fromBytes(byte[] data) { return new Event(data); diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index c60f7141f287..4196b5512dac 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -45,9 +45,6 @@ public class FeatureFlagUtils { public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ = "settings_notif_convo_bypass_shortcut_req"; /** @hide */ - public static final String BACKUP_NO_KV_DATA_CHANGE_CALLS = - "backup_enable_no_data_notification_calls"; - /** @hide */ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED = "settings_do_not_restore_preserved"; /** @hide */ @@ -70,9 +67,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false"); DEFAULT_FLAGS.put("settings_conditionals", "false"); DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); - - // Disabled until backup transports support it. - DEFAULT_FLAGS.put(BACKUP_NO_KV_DATA_CHANGE_CALLS, "false"); // Disabled by default until b/148278926 is resolved. This flags guards a feature // introduced in R and will be removed in the next release (b/148367230). DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false"); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 204d2c8bc2ce..148b32744e30 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -31,6 +31,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; @@ -39,11 +40,14 @@ import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED; @@ -2067,9 +2071,12 @@ public final class ViewRootImpl implements ViewParent, } final int sysUiVis = params.systemUiVisibility | params.subtreeSystemUiVisibility; final int flags = params.flags; + final boolean matchParent = params.width == MATCH_PARENT && params.height == MATCH_PARENT; + final boolean nonAttachedAppWindow = params.type >= FIRST_APPLICATION_WINDOW + && params.type <= LAST_APPLICATION_WINDOW; final boolean statusWasHiddenByFlags = (mTypesHiddenByFlags & Type.statusBars()) != 0; final boolean statusIsHiddenByFlags = (sysUiVis & SYSTEM_UI_FLAG_FULLSCREEN) != 0 - || (flags & FLAG_FULLSCREEN) != 0; + || ((flags & FLAG_FULLSCREEN) != 0 && matchParent && nonAttachedAppWindow); final boolean navWasHiddenByFlags = (mTypesHiddenByFlags & Type.navigationBars()) != 0; final boolean navIsHiddenByFlags = (sysUiVis & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; @@ -2231,9 +2238,7 @@ public final class ViewRootImpl implements ViewParent, Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets"); mApplyInsetsRequested = false; WindowInsets insets = getWindowInsets(true /* forceConstruct */); - final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode - == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS); - if (!dispatchCutout) { + if (!shouldDispatchCutout()) { // Window is either not laid out in cutout or the status bar inset takes care of // clearing the cutout, so we don't need to dispatch the cutout to the hierarchy. insets = insets.consumeDisplayCutout(); @@ -2242,6 +2247,13 @@ public final class ViewRootImpl implements ViewParent, Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + private boolean shouldDispatchCutout() { + return mWindowAttributes.layoutInDisplayCutoutMode + == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + || mWindowAttributes.layoutInDisplayCutoutMode + == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + private void updateVisibleInsets() { Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets, mWindowAttributes.softInputMode); @@ -2434,8 +2446,6 @@ public final class ViewRootImpl implements ViewParent, params = lp; } if (sNewInsetsMode == NEW_INSETS_MODE_FULL) { - adjustLayoutParamsForCompatibility(lp); - controlInsetsForCompatibility(lp); handleDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo); } @@ -2530,6 +2540,8 @@ public final class ViewRootImpl implements ViewParent, && !PixelFormat.formatHasAlpha(params.format)) { params.format = PixelFormat.TRANSLUCENT; } + adjustLayoutParamsForCompatibility(params); + controlInsetsForCompatibility(params); } if (mFirst || windowShouldResize || insetsChanged || diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e731323845d3..b0d0a14c4d41 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -458,7 +458,7 @@ public interface WindowManager extends ViewManager { } /** - * Returns the largets {@link WindowMetrics} an app may expect in the current system state. + * Returns the largest {@link WindowMetrics} an app may expect in the current system state. * <p> * The metrics describe the size of the largest potential area the window might occupy with * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets} diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java index 84fed9c5ee90..823901896bb2 100644 --- a/core/java/com/android/internal/app/ResolverViewPager.java +++ b/core/java/com/android/internal/app/ResolverViewPager.java @@ -24,10 +24,10 @@ import android.view.View; import com.android.internal.widget.ViewPager; /** - * A {@link ViewPager} which wraps around its first child's height and has swiping disabled. + * A {@link ViewPager} which wraps around its first child's height. * <p>Normally {@link ViewPager} instances expand their height to cover all remaining space in * the layout. - * <p>This class is used for the intent resolver and share sheet's tabbed view. + * <p>This class is used for the intent resolver's tabbed view. */ public class ResolverViewPager extends ViewPager { @@ -70,14 +70,4 @@ public class ResolverViewPager extends ViewPager { heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - return false; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return false; - } } diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index ffa347c168f6..72f16e4e4a82 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -186,13 +186,6 @@ public class OverlayConfig { */ @NonNull public static OverlayConfig getZygoteInstance() { - if (Process.myUid() != Process.ROOT_UID) { - // Scan the overlays in the zygote process to generate configuration settings for - // overlays on the system image. Do not cache this instance so OverlayConfig will not - // be present in applications by default. - throw new IllegalStateException("Can only be invoked in the root process"); - } - Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#getZygoteInstance"); try { return new OverlayConfig(null /* rootDirectory */, OverlayScanner::new, @@ -209,13 +202,12 @@ public class OverlayConfig { */ @NonNull public static OverlayConfig initializeSystemInstance(PackageProvider packageProvider) { - if (Process.myUid() != Process.SYSTEM_UID) { - throw new IllegalStateException("Can only be invoked in the system process"); - } - Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#initializeSystemInstance"); - sInstance = new OverlayConfig(null, null, packageProvider); - Trace.traceEnd(Trace.TRACE_TAG_RRO); + try { + sInstance = new OverlayConfig(null, null, packageProvider); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_RRO); + } return sInstance; } @@ -373,10 +365,6 @@ public class OverlayConfig { */ @NonNull public String[] createImmutableFrameworkIdmapsInZygote() { - if (Process.myUid() != Process.ROOT_UID) { - throw new IllegalStateException("This method can only be called from the root process"); - } - final String targetPath = "/system/framework/framework-res.apk"; final ArrayList<String> idmapPaths = new ArrayList<>(); final ArrayList<IdmapInvocation> idmapInvocations = diff --git a/core/java/com/android/internal/content/om/TEST_MAPPING b/core/java/com/android/internal/content/om/TEST_MAPPING new file mode 100644 index 000000000000..4cb595b01681 --- /dev/null +++ b/core/java/com/android/internal/content/om/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "com.android.internal.content." + } + ] + } + ] +}
\ No newline at end of file diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java index da1b72f05a98..99b4b5fb7707 100644 --- a/core/java/com/android/internal/policy/DecorContext.java +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -46,9 +46,10 @@ public class DecorContext extends ContextThemeWrapper { private WeakReference<Context> mActivityContext; + // TODO(b/149928768): Non-activity context can be passed. @VisibleForTesting public DecorContext(Context context, Context activityContext) { - super(context.createDisplayContext(activityContext.getDisplay()), null); + super(context.createDisplayContext(activityContext.getDisplayNoVerify()), null); mActivityContext = new WeakReference<>(activityContext); mActivityResources = activityContext.getResources(); } diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java index 720f4feeaebe..3c9791791a68 100644 --- a/core/java/com/android/internal/util/FunctionalUtils.java +++ b/core/java/com/android/internal/util/FunctionalUtils.java @@ -16,16 +16,12 @@ package com.android.internal.util; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.RemoteException; import android.util.ExceptionUtils; -import java.util.Collection; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Predicate; import java.util.function.Supplier; /** @@ -222,20 +218,4 @@ public class FunctionalUtils { } } } - - /** - * Find the first element in the list that matches the predicate. - * - * The standard Java way of doing this is to use streams, which is very expensive. - * - * @return the first matching element, or null if none. - */ - @Nullable - public static <T> T findFirst(@NonNull final Collection<T> haystack, - @NonNull final Predicate<T> p) { - for (final T needle : haystack) { - if (p.test(needle)) return needle; - } - return null; - } } diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index e24e982bc9a8..e35fda1ee76d 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -57,6 +57,8 @@ interface ILockSettings { void registerStrongAuthTracker(in IStrongAuthTracker tracker); void unregisterStrongAuthTracker(in IStrongAuthTracker tracker); void requireStrongAuth(int strongAuthReason, int userId); + void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId); + void scheduleNonStrongBiometricIdleTimeout(int userId); void systemReady(); void userPresent(int userId); int getStrongAuthForUser(int userId); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 864429c98989..d9b290267c96 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -49,6 +49,7 @@ import android.os.storage.StorageManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; @@ -1382,6 +1383,22 @@ public class LockPatternUtils { } } + public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) { + try { + getLockSettings().reportSuccessfulBiometricUnlock(isStrongBiometric, userId); + } catch (RemoteException e) { + Log.e(TAG, "Could not report successful biometric unlock", e); + } + } + + public void scheduleNonStrongBiometricIdleTimeout(int userId) { + try { + getLockSettings().scheduleNonStrongBiometricIdleTimeout(userId); + } catch (RemoteException e) { + Log.e(TAG, "Could not schedule non-strong biometric idle timeout", e); + } + } + /** * @see StrongAuthTracker#getStrongAuthForUser */ @@ -1557,7 +1574,8 @@ public class LockPatternUtils { SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN}) + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, + STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1604,6 +1622,12 @@ public class LockPatternUtils { public static final int STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE = 0x40; /** + * Strong authentication is required because it hasn't been used for a time after a + * non-strong biometric (i.e. weak or convenience biometric) is used to unlock device. + */ + public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80; + + /** * Strong auth flags that do not prevent biometric methods from being accepted as auth. * If any other flags are set, biometric authentication is disabled. */ @@ -1614,6 +1638,10 @@ public class LockPatternUtils { private final H mHandler; private final int mDefaultStrongAuthFlags; + private final SparseBooleanArray mIsNonStrongBiometricAllowedForUser = + new SparseBooleanArray(); + private final boolean mDefaultIsNonStrongBiometricAllowed = true; + public StrongAuthTracker(Context context) { this(context, Looper.myLooper()); } @@ -1657,8 +1685,21 @@ public class LockPatternUtils { * @return true if unlocking with a biometric method alone is allowed for {@code userId} * by the current strong authentication requirements. */ - public boolean isBiometricAllowedForUser(int userId) { - return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0; + public boolean isBiometricAllowedForUser(boolean isStrongBiometric, int userId) { + boolean allowed = ((getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0); + if (!isStrongBiometric) { + allowed &= isNonStrongBiometricAllowedAfterIdleTimeout(userId); + } + return allowed; + } + + /** + * @return true if unlocking with a non-strong (i.e. weak or convenience) biometric method + * alone is allowed for {@code userId}, otherwise returns false. + */ + public boolean isNonStrongBiometricAllowedAfterIdleTimeout(int userId) { + return mIsNonStrongBiometricAllowedForUser.get(userId, + mDefaultIsNonStrongBiometricAllowed); } /** @@ -1667,6 +1708,12 @@ public class LockPatternUtils { public void onStrongAuthRequiredChanged(int userId) { } + /** + * Called when whether non-strong biometric is allowed for {@code userId} changed. + */ + public void onIsNonStrongBiometricAllowedChanged(int userId) { + } + protected void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags, int userId) { int oldValue = getStrongAuthForUser(userId); @@ -1680,6 +1727,18 @@ public class LockPatternUtils { } } + protected void handleIsNonStrongBiometricAllowedChanged(boolean allowed, + int userId) { + boolean oldValue = isNonStrongBiometricAllowedAfterIdleTimeout(userId); + if (allowed != oldValue) { + if (allowed == mDefaultIsNonStrongBiometricAllowed) { + mIsNonStrongBiometricAllowedForUser.delete(userId); + } else { + mIsNonStrongBiometricAllowedForUser.put(userId, allowed); + } + onIsNonStrongBiometricAllowedChanged(userId); + } + } protected final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() { @Override @@ -1688,10 +1747,17 @@ public class LockPatternUtils { mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED, strongAuthFlags, userId).sendToTarget(); } + + @Override + public void onIsNonStrongBiometricAllowedChanged(boolean allowed, int userId) { + mHandler.obtainMessage(H.MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED, + allowed ? 1 : 0, userId).sendToTarget(); + } }; private class H extends Handler { static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1; + static final int MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED = 2; public H(Looper looper) { super(looper); @@ -1703,6 +1769,10 @@ public class LockPatternUtils { case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED: handleStrongAuthRequiredChanged(msg.arg1, msg.arg2); break; + case MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED: + handleIsNonStrongBiometricAllowedChanged(msg.arg1 == 1 /* allowed */, + msg.arg2); + break; } } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 5912f4059548..d27be275f312 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -52,20 +52,11 @@ cc_library_shared { "system/media/private/camera/include", ], - static_libs: [ - "libandroid_graphics", - ], - - whole_static_libs: ["libandroid_graphics"], - - export_static_lib_headers: ["libandroid_graphics"], - shared_libs: [ "libbase", "libcutils", "libharfbuzz_ng", "libhwui", - "libjpeg", "liblog", "libminikin", "libnativehelper", @@ -107,6 +98,7 @@ cc_library_shared { "android_database_SQLiteGlobal.cpp", "android_database_SQLiteDebug.cpp", "android_graphics_GraphicBuffer.cpp", + "android_graphics_SurfaceTexture.cpp", "android_view_CompositionSamplingListener.cpp", "android_view_DisplayEventReceiver.cpp", "android_view_InputChannel.cpp", @@ -182,8 +174,6 @@ cc_library_shared { "android_hardware_UsbRequest.cpp", "android_hardware_location_ActivityRecognitionHardware.cpp", "android_util_FileObserver.cpp", - "android/graphics/GraphicsStatsService.cpp", - "android/graphics/SurfaceTexture.cpp", "android/opengl/poly_clip.cpp", // TODO: .arm "android/opengl/util.cpp", "android_server_NetworkManagementSocketTagger.cpp", @@ -324,153 +314,3 @@ cc_library_shared { }, }, } - -cc_library_static { - name: "libandroid_graphics", - host_supported: true, - cflags: [ - "-Wno-unused-parameter", - "-Wno-non-virtual-dtor", - "-Wno-maybe-uninitialized", - "-Wno-parentheses", - - "-DGL_GLEXT_PROTOTYPES", - "-DEGL_EGLEXT_PROTOTYPES", - - "-DU_USING_ICU_NAMESPACE=0", - - "-Wall", - "-Werror", - "-Wno-error=deprecated-declarations", - "-Wunused", - "-Wunreachable-code", - ], - - cppflags: ["-Wno-conversion-null"], - - srcs: [ - "android/graphics/apex/android_matrix.cpp", - "android/graphics/apex/android_paint.cpp", - "android/graphics/apex/android_region.cpp", - - "android_graphics_animation_NativeInterpolatorFactory.cpp", - "android_graphics_animation_RenderNodeAnimator.cpp", - "android_graphics_Canvas.cpp", - "android_graphics_ColorSpace.cpp", - "android_graphics_drawable_AnimatedVectorDrawable.cpp", - "android_graphics_drawable_VectorDrawable.cpp", - "android_graphics_HardwareRendererObserver.cpp", - "android_graphics_Picture.cpp", - "android_nio_utils.cpp", - "android_view_DisplayListCanvas.cpp", - "android_view_RenderNode.cpp", - "android_util_PathParser.cpp", - - "android/graphics/Bitmap.cpp", - "android/graphics/BitmapFactory.cpp", - "android/graphics/ByteBufferStreamAdaptor.cpp", - "android/graphics/Camera.cpp", - "android/graphics/CanvasProperty.cpp", - "android/graphics/ColorFilter.cpp", - "android/graphics/CreateJavaOutputStreamAdaptor.cpp", - "android/graphics/FontFamily.cpp", - "android/graphics/FontUtils.cpp", - "android/graphics/Graphics.cpp", - "android/graphics/ImageDecoder.cpp", - "android/graphics/Interpolator.cpp", - "android/graphics/MaskFilter.cpp", - "android/graphics/Matrix.cpp", - "android/graphics/NinePatch.cpp", - "android/graphics/NinePatchPeeker.cpp", - "android/graphics/Paint.cpp", - "android/graphics/PaintFilter.cpp", - "android/graphics/Path.cpp", - "android/graphics/PathEffect.cpp", - "android/graphics/PathMeasure.cpp", - "android/graphics/Picture.cpp", - "android/graphics/Region.cpp", - "android/graphics/Shader.cpp", - "android/graphics/Typeface.cpp", - "android/graphics/Utils.cpp", - "android/graphics/YuvToJpegEncoder.cpp", - "android/graphics/fonts/Font.cpp", - "android/graphics/fonts/FontFamily.cpp", - "android/graphics/text/LineBreaker.cpp", - "android/graphics/text/MeasuredText.cpp", - ], - - local_include_dirs: [ - "include", // NEEDED FOR ANDROID RUNTIME - "android/graphics", - "android/graphics/apex/include", - ], - - export_include_dirs: [ - "android/graphics/apex/include", - ], - - include_dirs: [ - "external/skia/include/private", - "external/skia/src/codec", - "external/skia/src/core", - "external/skia/src/effects", - "external/skia/src/image", - "external/skia/src/images", - ], - - shared_libs: [ - "libbase", - "libcutils", - "libharfbuzz_ng", - "libhwui", - "liblog", - "libminikin", - "libnativehelper", - "libz", - "libziparchive", - "libjpeg", - ], - - target: { - android: { - srcs: [ // sources that depend on android only libraries - "android/graphics/apex/android_canvas.cpp", - "android/graphics/apex/android_bitmap.cpp", - "android/graphics/apex/renderthread.cpp", - "android/graphics/apex/jni_runtime.cpp", - - "android_view_TextureLayer.cpp", - "android_view_ThreadedRenderer.cpp", - "android/graphics/AnimatedImageDrawable.cpp", - "android/graphics/BitmapRegionDecoder.cpp", - "android/graphics/GIFMovie.cpp", - "android/graphics/Movie.cpp", - "android/graphics/MovieImpl.cpp", - "android/graphics/pdf/PdfDocument.cpp", - "android/graphics/pdf/PdfEditor.cpp", - "android/graphics/pdf/PdfRenderer.cpp", - "android/graphics/pdf/PdfUtils.cpp", - ], - shared_libs: [ - "libandroidfw", - "libEGL", - "libmediandk", - "libnativedisplay", - "libnativewindow", - "libpdfium", - ], - static_libs: [ - "libgif", - ], - }, - host: { - cflags: [ - "-Wno-unused-const-variable", - "-Wno-unused-function", - ], - static_libs: [ - "libandroidfw", - ], - } - }, -} diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 4879478733e6..c41398c97ca0 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -115,6 +115,7 @@ extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env); +extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_view_DisplayEventReceiver(JNIEnv* env); extern int register_android_view_InputApplicationHandle(JNIEnv* env); extern int register_android_view_InputWindowHandle(JNIEnv* env); @@ -1484,6 +1485,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_classes), REG_JNI(register_android_graphics_BLASTBufferQueue), REG_JNI(register_android_graphics_GraphicBuffer), + REG_JNI(register_android_graphics_SurfaceTexture), REG_JNI(register_android_database_CursorWindow), REG_JNI(register_android_database_SQLiteConnection), REG_JNI(register_android_database_SQLiteGlobal), diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp index 7ee509b4ecb4..77c1a1097240 100644 --- a/core/jni/LayoutlibLoader.cpp +++ b/core/jni/LayoutlibLoader.cpp @@ -34,19 +34,6 @@ using namespace std; static JavaVM* javaVM; -extern int register_android_graphics_Bitmap(JNIEnv*); -extern int register_android_graphics_BitmapFactory(JNIEnv*); -extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); -extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); -extern int register_android_graphics_Graphics(JNIEnv* env); -extern int register_android_graphics_ImageDecoder(JNIEnv*); -extern int register_android_graphics_Interpolator(JNIEnv* env); -extern int register_android_graphics_MaskFilter(JNIEnv* env); -extern int register_android_graphics_NinePatch(JNIEnv*); -extern int register_android_graphics_PathEffect(JNIEnv* env); -extern int register_android_graphics_Shader(JNIEnv* env); -extern int register_android_graphics_Typeface(JNIEnv* env); - namespace android { extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); @@ -54,25 +41,6 @@ extern int register_android_content_AssetManager(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); extern int register_android_content_res_ApkAssets(JNIEnv* env); -extern int register_android_graphics_Canvas(JNIEnv* env); -extern int register_android_graphics_ColorFilter(JNIEnv* env); -extern int register_android_graphics_ColorSpace(JNIEnv* env); -extern int register_android_graphics_DrawFilter(JNIEnv* env); -extern int register_android_graphics_FontFamily(JNIEnv* env); -extern int register_android_graphics_Matrix(JNIEnv* env); -extern int register_android_graphics_Paint(JNIEnv* env); -extern int register_android_graphics_Path(JNIEnv* env); -extern int register_android_graphics_PathMeasure(JNIEnv* env); -extern int register_android_graphics_Picture(JNIEnv* env); -extern int register_android_graphics_Region(JNIEnv* env); -extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); -extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); -extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); -extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); -extern int register_android_graphics_fonts_Font(JNIEnv* env); -extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); -extern int register_android_graphics_text_LineBreaker(JNIEnv* env); -extern int register_android_graphics_text_MeasuredText(JNIEnv* env); extern int register_android_os_FileObserver(JNIEnv* env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_SystemClock(JNIEnv* env); @@ -81,10 +49,7 @@ extern int register_android_os_Trace(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv* env); extern int register_android_util_EventLog(JNIEnv* env); extern int register_android_util_Log(JNIEnv* env); -extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_util_jar_StrictJarFile(JNIEnv* env); -extern int register_android_view_RenderNode(JNIEnv* env); -extern int register_android_view_DisplayListCanvas(JNIEnv* env); extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); #define REG_JNI(name) { name } @@ -103,46 +68,6 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { #endif {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)}, {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)}, - {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)}, - {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)}, - {"android.graphics.ByteBufferStreamAdaptor", - REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)}, - {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)}, - {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)}, - {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)}, - {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)}, - {"android.graphics.CreateJavaOutputStreamAdaptor", - REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)}, - {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)}, - {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)}, - {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)}, - {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)}, - {"android.graphics.Interpolator", REG_JNI(register_android_graphics_Interpolator)}, - {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)}, - {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)}, - {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)}, - {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)}, - {"android.graphics.Path", REG_JNI(register_android_graphics_Path)}, - {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)}, - {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)}, - {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)}, - {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)}, - {"android.graphics.Region", REG_JNI(register_android_graphics_Region)}, - {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)}, - {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)}, - {"android.graphics.animation.NativeInterpolatorFactory", - REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)}, - {"android.graphics.animation.RenderNodeAnimator", - REG_JNI(register_android_graphics_animation_RenderNodeAnimator)}, - {"android.graphics.drawable.AnimatedVectorDrawable", - REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)}, - {"android.graphics.drawable.VectorDrawable", - REG_JNI(register_android_graphics_drawable_VectorDrawable)}, - {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)}, - {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)}, - {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)}, - {"android.graphics.text.MeasuredText", - REG_JNI(register_android_graphics_text_MeasuredText)}, #ifdef __linux__ {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)}, {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)}, @@ -153,7 +78,6 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { {"android.text.AndroidCharacter", REG_JNI(register_android_text_AndroidCharacter)}, {"android.util.EventLog", REG_JNI(register_android_util_EventLog)}, {"android.util.Log", REG_JNI(register_android_util_Log)}, - {"android.util.PathParser", REG_JNI(register_android_util_PathParser)}, {"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)}, {"com.android.internal.util.VirtualRefBasePtr", REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)}, diff --git a/core/jni/android_graphics_GraphicBuffer.cpp b/core/jni/android_graphics_GraphicBuffer.cpp index b6d50898a057..25a733234313 100644 --- a/core/jni/android_graphics_GraphicBuffer.cpp +++ b/core/jni/android_graphics_GraphicBuffer.cpp @@ -31,6 +31,7 @@ #include <android/native_window.h> #include <android/graphics/canvas.h> #include <android_runtime/android_graphics_GraphicBuffer.h> +#include <android_runtime/android_hardware_HardwareBuffer.h> #include <private/android/AHardwareBufferHelpers.h> #include <private/gui/ComposerService.h> @@ -260,6 +261,16 @@ jobject android_graphics_GraphicBuffer_createFromAHardwareBuffer(JNIEnv* env, return obj; } +// ---------------------------------------------------------------------------- +// AHB to GraphicBuffer Converter +// ---------------------------------------------------------------------------- + +static jobject android_graphics_GraphicBuffer_createFromHardwareBuffer(JNIEnv* env, jobject clazz, + jobject hb) { + AHardwareBuffer* ahb = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, hb); + return android_graphics_GraphicBuffer_createFromAHardwareBuffer(env, ahb); +} + }; using namespace android; @@ -283,7 +294,10 @@ static const JNINativeMethod gMethods[] = { { "nUnlockCanvasAndPost", "(JLandroid/graphics/Canvas;)Z", (void*) android_graphics_GraphicBuffer_unlockCanvasAndPost }, { "nWrapGraphicBuffer", "(J)J", - (void*) android_graphics_GraphicBuffer_wrap } + (void*) android_graphics_GraphicBuffer_wrap }, + { "nCreateFromHardwareBuffer", + "(Landroid/hardware/HardwareBuffer;)Landroid/graphics/GraphicBuffer;", + (void*) android_graphics_GraphicBuffer_createFromHardwareBuffer } }; int register_android_graphics_GraphicBuffer(JNIEnv* env) { diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp index 2aca31733599..0909ce7ff23b 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android_graphics_SurfaceTexture.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "SurfaceTexture" #include <stdio.h> diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index f03f42737134..8bb4d503cc82 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -47,28 +47,32 @@ static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, const char* field_signature) { jfieldID res = env->GetFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, + field_signature); return res; } static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, const char* method_signature) { jmethodID res = env->GetMethodID(clazz, method_name, method_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name, + method_signature); return res; } static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, const char* field_signature) { jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, + field_signature); return res; } static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, const char* method_signature) { jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s", + method_name, method_signature); return res; } diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto index e476c523607c..e65a2abce4f0 100644 --- a/core/proto/android/server/peopleservice.proto +++ b/core/proto/android/server/peopleservice.proto @@ -50,6 +50,9 @@ message ConversationInfoProto { // Integer representation of conversation bit flags. optional int32 conversation_flags = 6; + + // The phone number of the contact. + optional string contact_phone_number = 7; } // On disk data of events. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ddfc4b8e94a4..814b8acd63ac 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3084,8 +3084,7 @@ <!-- Allows SystemUI to request third party controls. <p>Should only be requested by the System and required by - ControlsService declarations. - @hide + {@link android.service.controls.ControlsProviderService} declarations. --> <permission android:name="android.permission.BIND_CONTROLS" android:protectionLevel="signature" /> diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index c0de6936dd12..5676049fb940 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -90,7 +90,7 @@ android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="wrap_content"> - <com.android.internal.app.ResolverViewPager + <com.android.internal.widget.ViewPager android:id="@+id/profile_pager" android:layout_width="match_parent" android:layout_height="wrap_content"/> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 68c86b263a75..482a41647d59 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1278,20 +1278,21 @@ <integer name="config_screenBrightnessSettingDefault">102</integer> <!-- Minimum screen brightness setting allowed by power manager. - The user is forbidden from setting the brightness below this level. - Equivalent to 10/255. --> - <item name="config_screenBrightnessSettingMinimumFloat" format="float" type="dimen">0.035433073</item> + -2 is invalid so setting will resort to int value specified above. + Set this to 0.0 to allow screen to go to minimal brightness. + The user is forbidden from setting the brightness below this level. --> + <item name="config_screenBrightnessSettingMinimumFloat" format="float" type="dimen">-2</item> <!-- Maximum screen brightness allowed by the power manager. - The user is forbidden from setting the brightness above this level. - This value is a fraction between 3.5% and 100%. --> - <item name="config_screenBrightnessSettingMaximumFloat" format="float" type="dimen">1.0</item> + -2 is invalid so setting will resort to int value specified above. + Set this to 1.0 for maximum brightness range. + The user is forbidden from setting the brightness above this level. --> + <item name="config_screenBrightnessSettingMaximumFloat" format="float" type="dimen">-2</item> - <!-- Default screen brightness setting. - Must be in the range specified by minimum and maximum. - This value is a fraction between 3.5% and 100%. - Equivalent to 102/255 (default for this device) --> - <item name="config_screenBrightnessSettingDefaultFloat" format="float" type="dimen">0.397637795276</item> + <!-- Default screen brightness setting set. + -2 is invalid so setting will resort to int value specified above. + Must be in the range specified by minimum and maximum. --> + <item name="config_screenBrightnessSettingDefaultFloat" format="float" type="dimen">-2</item> <!-- Note: This setting is deprecated, please use config_screenBrightnessSettingForVrDefaultFloat instead --> @@ -2588,6 +2589,18 @@ <string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" >com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string> + <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist + a wireless network for wireless debugging. + Can be customized for other product types --> + <string name="config_customAdbWifiNetworkConfirmationComponent" + >com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string> + + <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to + switch to the primary user to enable wireless debugging. + Can be customized for other product types --> + <string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent" + >com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string> + <!-- Component name of the activity that shows the usb containment status. --> <string name="config_usbContaminantActivity" translatable="false" >com.android.systemui/com.android.systemui.usb.UsbContaminantActivity</string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 48049b44dd29..4f221d0d85fd 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -781,9 +781,6 @@ <dimen name="chooser_action_button_icon_size">18dp</dimen> - <!-- Assistant handles --> - <dimen name="assist_handle_shadow_radius">2dp</dimen> - <!-- For Waterfall Display --> <dimen name="waterfall_display_left_edge_size">0px</dimen> <dimen name="waterfall_display_top_edge_size">0px</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d513e2b5a9e5..144573641654 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3527,6 +3527,12 @@ <!-- Message of notification shown when ADB is actively connected to the phone. --> <string name="adb_active_notification_message">Tap to turn off USB debugging</string> <string name="adb_active_notification_message" product="tv">Select to disable USB debugging.</string> + <!-- Title of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] --> + <string name="adbwifi_active_notification_title">Wireless debugging connected</string> + <!-- Message of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] --> + <string name="adbwifi_active_notification_message">Tap to turn off wireless debugging</string> + <!-- Message of notification shown when ADB Wireless is actively connected to the TV. [CHAR LIMIT=NONE] --> + <string name="adbwifi_active_notification_message" product="tv">Select to disable wireless debugging.</string> <!-- Title of notification shown when Test Harness Mode is enabled. [CHAR LIMIT=NONE] --> <string name="test_harness_mode_notification_title">Test Harness Mode enabled</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1c9e2cd72f28..a3da41101ad3 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2009,6 +2009,8 @@ <java-symbol type="string" name="accessibility_binding_label" /> <java-symbol type="string" name="adb_active_notification_message" /> <java-symbol type="string" name="adb_active_notification_title" /> + <java-symbol type="string" name="adbwifi_active_notification_message" /> + <java-symbol type="string" name="adbwifi_active_notification_title" /> <java-symbol type="string" name="test_harness_mode_notification_title" /> <java-symbol type="string" name="test_harness_mode_notification_message" /> <java-symbol type="string" name="console_running_notification_title" /> @@ -2154,6 +2156,8 @@ <java-symbol type="integer" name="config_attentiveWarningDuration" /> <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" /> <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" /> + <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationComponent" /> + <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent" /> <java-symbol type="string" name="config_customVpnConfirmDialogComponent" /> <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" /> <java-symbol type="string" name="config_platformVpnConfirmDialogComponent" /> @@ -3840,9 +3844,6 @@ <!-- For App Standby --> <java-symbol type="string" name="as_app_forced_to_restricted_bucket" /> - <!-- Assistant handles --> - <java-symbol type="dimen" name="assist_handle_shadow_radius" /> - <!-- For Waterfall Display --> <java-symbol type="dimen" name="waterfall_display_left_edge_size" /> <java-symbol type="dimen" name="waterfall_display_top_edge_size" /> diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java index 9f0af60f33b7..f0e70a53b572 100644 --- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java @@ -92,7 +92,7 @@ public class AccessibilityShortcutInfoTest { @Test public void testLoadAnimatedImage() { assertNotNull("Can't find animated image", - mShortcutInfo.loadAnimatedImage(mPackageManager)); + mShortcutInfo.loadAnimatedImage(mTargetContext)); } @Test diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 78c4420b9496..1737bd0fa20b 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -234,4 +234,12 @@ public class ContentResolverTest { assertThat(type).isNull(); assertThat(end).isLessThan(start + 5000); } + + @Test + public void testCanonicalize() { + Uri canonical = mResolver.canonicalize( + Uri.parse("content://android.content.FakeProviderRemote/something")); + assertThat(canonical).isEqualTo( + Uri.parse("content://android.content.FakeProviderRemote/canonical")); + } } diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 7b9bdbcd7a17..1d7ba5d9be46 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -54,4 +54,10 @@ public class FakeProviderRemote extends ContentProvider { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } + + @Override + public Uri canonicalize(Uri uri) { + return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority()) + .appendPath("canonical").build(); + } } diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 78c88d71d953..4c2ca7eefcc1 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -25,9 +25,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; import android.content.IIntentSender; import android.content.Intent; +import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -44,6 +48,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -59,6 +64,11 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ControlProviderServiceTest { + private static final ComponentName TEST_SYSUI_COMPONENT = + ComponentName.unflattenFromString("sysui/.test.cls"); + private static final ComponentName TEST_COMPONENT = + ComponentName.unflattenFromString("test.pkg/.test.cls"); + private IBinder mToken = new Binder(); @Mock private IControlsActionCallback.Stub mActionCallback; @@ -66,6 +76,12 @@ public class ControlProviderServiceTest { private IControlsSubscriber.Stub mSubscriber; @Mock private IIntentSender mIIntentSender; + @Mock + private Resources mResources; + @Mock + private Context mContext; + @Captor + private ArgumentCaptor<Intent> mIntentArgumentCaptor; private PendingIntent mPendingIntent; private FakeControlsProviderService mControlsProviderService; @@ -81,6 +97,10 @@ public class ControlProviderServiceTest { when(mSubscriber.asBinder()).thenCallRealMethod(); when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber); + when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent)) + .thenReturn(TEST_SYSUI_COMPONENT.flattenToString()); + when(mContext.getResources()).thenReturn(mResources); + Bundle b = new Bundle(); b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken); Intent intent = new Intent(); @@ -223,6 +243,21 @@ public class ControlProviderServiceTest { ControlAction.RESPONSE_OK); } + @Test + public void testRequestAdd() { + Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build(); + ControlsProviderService.requestAddControl(mContext, TEST_COMPONENT, control); + + verify(mContext).sendBroadcast(mIntentArgumentCaptor.capture(), + eq(Manifest.permission.BIND_CONTROLS)); + Intent intent = mIntentArgumentCaptor.getValue(); + assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction()); + assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage()); + assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME)); + assertTrue(equals(control, + intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))); + } + private static boolean equals(Control c1, Control c2) { if (c1 == c2) return true; if (c1 == null || c2 == null) return false; diff --git a/core/tests/coretests/src/android/util/EventLogTest.java b/core/tests/coretests/src/android/util/EventLogTest.java new file mode 100644 index 000000000000..94e72c4a8d52 --- /dev/null +++ b/core/tests/coretests/src/android/util/EventLogTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.util.EventLog.Event; + +import junit.framework.AssertionFailedError; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** Unit tests for {@link android.util.EventLog} */ +public class EventLogTest { + + @Test + public void testWithNewData() throws Throwable { + Event event = createEvent(() -> { + EventLog.writeEvent(314, 123); + }, 314); + + assertTrue(event.withNewData(12345678L).getData().equals(12345678L)); + assertTrue(event.withNewData(2.718f).getData().equals(2.718f)); + assertTrue(event.withNewData("test string").getData().equals("test string")); + + Object[] objects = ((Object[]) event.withNewData( + new Object[] {111, 2.22f, 333L, "444"}).getData()); + assertEquals(4, objects.length); + assertTrue(objects[0].equals(111)); + assertTrue(objects[1].equals(2.22f)); + assertTrue(objects[2].equals(333L)); + assertTrue(objects[3].equals("444")); + } + + /** + * Creates an Event object. Only the native code has the serialization and deserialization logic + * so need to actually emit a real log in order to generate the object. + */ + private Event createEvent(Runnable generator, int expectedTag) throws Exception { + Long markerData = System.currentTimeMillis(); + EventLog.writeEvent(expectedTag, markerData); + generator.run(); + + List<Event> events = new ArrayList<>(); + // Give the message some time to show up in the log + Thread.sleep(20); + EventLog.readEvents(new int[] {expectedTag}, events); + for (int i = 0; i < events.size() - 1; i++) { + if (markerData.equals(events.get(i).getData())) { + return events.get(i + 1); + } + } + throw new AssertionFailedError("Unable to locate marker event"); + } +} diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 5b23dc0481cb..145fd8b824cf 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -709,6 +709,18 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-650040763": { + "message": "rotationForOrientation(orient=%d, last=%d); user=%d %s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotation.java" + }, + "-639305784": { + "message": "Could not report config changes to the window token client.", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowToken.java" + }, "-635082269": { "message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b", "level": "INFO", diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index c1e7a360fcc5..f7877590869a 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -2240,7 +2240,7 @@ public final class Bitmap implements Parcelable { */ @UnsupportedAppUsage public GraphicBuffer createGraphicBufferHandle() { - return nativeCreateGraphicBufferHandle(mNativePtr); + return GraphicBuffer.createFromHardwareBuffer(getHardwareBuffer()); } /** @@ -2320,7 +2320,6 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap); private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace); - private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap); private static native HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap); private static native ColorSpace nativeComputeColorSpace(long nativePtr); private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace); diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java index 99fa5eef7bbd..0430847857b6 100644 --- a/graphics/java/android/graphics/GraphicBuffer.java +++ b/graphics/java/android/graphics/GraphicBuffer.java @@ -17,6 +17,7 @@ package android.graphics; import android.compat.annotation.UnsupportedAppUsage; +import android.hardware.HardwareBuffer; import android.os.Parcel; import android.os.Parcelable; @@ -110,6 +111,14 @@ public class GraphicBuffer implements Parcelable { } /** + * For Bitmap until all usages are updated to AHB + * @hide + */ + public static final GraphicBuffer createFromHardwareBuffer(HardwareBuffer buffer) { + return nCreateFromHardwareBuffer(buffer); + } + + /** * Returns the width of this buffer in pixels. */ public int getWidth() { @@ -305,4 +314,5 @@ public class GraphicBuffer implements Parcelable { private static native boolean nLockCanvas(long nativeObject, Canvas canvas, Rect dirty); private static native boolean nUnlockCanvasAndPost(long nativeObject, Canvas canvas); private static native long nWrapGraphicBuffer(long nativeObject); + private static native GraphicBuffer nCreateFromHardwareBuffer(HardwareBuffer buffer); } diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java index c12159cfd7a4..5858e3988486 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -280,7 +280,10 @@ public final class Outline { * {@link android.os.Build.VERSION_CODES#Q}, it is no longer required to be * convex. * - * @deprecated The path is no longer required to be convex. Use {@link #setPath} instead. + * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, the restriction + * that the path must be convex is removed. However, the API is misnamed until + * {@link android.os.Build.VERSION_CODES#R}, when {@link #setPath} is + * introduced. Use {@link #setPath} instead. */ @Deprecated public void setConvexPath(@NonNull Path convexPath) { diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index a7e17d13c9e1..acd90a7f10cf 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -34,6 +34,7 @@ import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; @@ -811,6 +812,10 @@ public final class KeyChain { throw new NullPointerException("context == null"); } ensureNotOnMainThread(context); + if (!UserManager.get(context).isUserUnlocked(user)) { + throw new IllegalStateException("User must be unlocked"); + } + final CountDownLatch countDownLatch = new CountDownLatch(1); final AtomicReference<IKeyChainService> keyChainService = new AtomicReference<>(); ServiceConnection keyChainServiceConnection = new ServiceConnection() { diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 81dedda5341d..ac2fd98248d0 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -152,9 +152,235 @@ cc_defaults { } // ------------------------ +// APEX +// ------------------------ + +cc_library_headers { + name: "android_graphics_apex_headers", + + host_supported: true, + export_include_dirs: [ + "apex/include", + ], + target: { + windows: { + enabled: true, + }, + } +} + +cc_defaults { + name: "android_graphics_apex", + host_supported: true, + cflags: [ + "-Wno-unused-parameter", + "-Wno-non-virtual-dtor", + "-Wno-maybe-uninitialized", + "-Wno-parentheses", + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], + + cppflags: ["-Wno-conversion-null"], + + srcs: [ + "apex/android_matrix.cpp", + "apex/android_paint.cpp", + "apex/android_region.cpp", + ], + + header_libs: [ "android_graphics_apex_headers" ], + + target: { + android: { + srcs: [ // sources that depend on android only libraries + "apex/android_bitmap.cpp", + "apex/android_canvas.cpp", + "apex/jni_runtime.cpp", + "apex/renderthread.cpp", + ], + }, + host: { + srcs: [ + "apex/LayoutlibLoader.cpp", + ], + } + }, +} + +// ------------------------ +// Android Graphics JNI +// ------------------------ + +cc_library_headers { + name: "android_graphics_jni_headers", + + host_supported: true, + export_include_dirs: [ + "jni", + ], + target: { + windows: { + enabled: true, + }, + } +} + +cc_defaults { + name: "android_graphics_jni", + host_supported: true, + cflags: [ + "-Wno-unused-parameter", + "-Wno-non-virtual-dtor", + "-Wno-maybe-uninitialized", + "-Wno-parentheses", + + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + + "-DU_USING_ICU_NAMESPACE=0", + + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], + + cppflags: ["-Wno-conversion-null"], + + srcs: [ + "jni/android_graphics_animation_NativeInterpolatorFactory.cpp", + "jni/android_graphics_animation_RenderNodeAnimator.cpp", + "jni/android_graphics_Canvas.cpp", + "jni/android_graphics_ColorSpace.cpp", + "jni/android_graphics_drawable_AnimatedVectorDrawable.cpp", + "jni/android_graphics_drawable_VectorDrawable.cpp", + "jni/android_graphics_HardwareRendererObserver.cpp", + "jni/android_graphics_Matrix.cpp", + "jni/android_graphics_Picture.cpp", + "jni/android_graphics_DisplayListCanvas.cpp", + "jni/android_graphics_RenderNode.cpp", + "jni/android_nio_utils.cpp", + "jni/android_util_PathParser.cpp", + + "jni/Bitmap.cpp", + "jni/BitmapFactory.cpp", + "jni/ByteBufferStreamAdaptor.cpp", + "jni/Camera.cpp", + "jni/CanvasProperty.cpp", + "jni/ColorFilter.cpp", + "jni/CreateJavaOutputStreamAdaptor.cpp", + "jni/FontFamily.cpp", + "jni/FontUtils.cpp", + "jni/Graphics.cpp", + "jni/ImageDecoder.cpp", + "jni/Interpolator.cpp", + "jni/MaskFilter.cpp", + "jni/NinePatch.cpp", + "jni/NinePatchPeeker.cpp", + "jni/Paint.cpp", + "jni/PaintFilter.cpp", + "jni/Path.cpp", + "jni/PathEffect.cpp", + "jni/PathMeasure.cpp", + "jni/Picture.cpp", + "jni/Shader.cpp", + "jni/Typeface.cpp", + "jni/Utils.cpp", + "jni/YuvToJpegEncoder.cpp", + "jni/fonts/Font.cpp", + "jni/fonts/FontFamily.cpp", + "jni/text/LineBreaker.cpp", + "jni/text/MeasuredText.cpp", + ], + + header_libs: [ "android_graphics_jni_headers" ], + + include_dirs: [ + "external/skia/include/private", + "external/skia/src/codec", + "external/skia/src/core", + "external/skia/src/effects", + "external/skia/src/image", + "external/skia/src/images", + ], + + shared_libs: [ + "libbase", + "libcutils", + "libharfbuzz_ng", + "liblog", + "libminikin", + "libnativehelper", + "libz", + "libziparchive", + "libjpeg", + ], + + target: { + android: { + srcs: [ // sources that depend on android only libraries + "jni/AnimatedImageDrawable.cpp", + "jni/android_graphics_TextureLayer.cpp", + "jni/android_graphics_HardwareRenderer.cpp", + "jni/BitmapRegionDecoder.cpp", + "jni/GIFMovie.cpp", + "jni/GraphicsStatsService.cpp", + "jni/Movie.cpp", + "jni/MovieImpl.cpp", + "jni/Region.cpp", // requires libbinder_ndk + "jni/pdf/PdfDocument.cpp", + "jni/pdf/PdfEditor.cpp", + "jni/pdf/PdfRenderer.cpp", + "jni/pdf/PdfUtils.cpp", + ], + shared_libs: [ + "libandroidfw", + "libbinder", + "libbinder_ndk", + "libmediandk", + "libnativedisplay", + "libnativewindow", + "libstatspull", + "libstatssocket", + "libpdfium", + ], + static_libs: [ + "libgif", + "libstatslog", + ], + }, + host: { + cflags: [ + "-Wno-unused-const-variable", + "-Wno-unused-function", + ], + static_libs: [ + "libandroidfw", + ], + } + }, +} + +// ------------------------ // library // ------------------------ +cc_library_headers { + name: "libhwui_internal_headers", + + host_supported: true, + export_include_dirs: [ + ".", + ], + header_libs: [ "android_graphics_jni_headers" ], + export_header_lib_headers: [ "android_graphics_jni_headers" ], +} + cc_defaults { name: "libhwui_defaults", defaults: ["hwui_defaults"], @@ -205,11 +431,8 @@ cc_defaults { export_proto_headers: true, }, - export_include_dirs: ["."], - target: { android: { - srcs: [ "hwui/AnimatedImageThread.cpp", "pipeline/skia/ATraceMemoryDump.cpp", @@ -274,7 +497,10 @@ cc_library { host_supported: true, defaults: [ "libhwui_defaults", + "android_graphics_apex", + "android_graphics_jni", ], + export_header_lib_headers: ["android_graphics_apex_headers"], } cc_library_static { diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 6d4a0c6421d9..c24224cbbd67 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -18,9 +18,6 @@ #include <log/log.h> #include <utils/Errors.h> -#include <mutex> -#include <thread> - #include "Properties.h" namespace android { diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp new file mode 100644 index 000000000000..4bbf1214bdcf --- /dev/null +++ b/libs/hwui/apex/LayoutlibLoader.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "graphics_jni_helpers.h" + +#include <GraphicsJNI.h> +#include <SkGraphics.h> + +#include <sstream> +#include <iostream> +#include <unicode/putil.h> +#include <unordered_map> +#include <vector> + +using namespace std; + +/* + * This is responsible for setting up the JNI environment for communication between + * the Java and native parts of layoutlib, including registering native methods. + * This is mostly achieved by copying the way it is done in the platform + * (see AndroidRuntime.cpp). + */ + +static JavaVM* javaVM; + +extern int register_android_graphics_Bitmap(JNIEnv*); +extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Graphics(JNIEnv* env); +extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_Interpolator(JNIEnv* env); +extern int register_android_graphics_MaskFilter(JNIEnv* env); +extern int register_android_graphics_NinePatch(JNIEnv*); +extern int register_android_graphics_PathEffect(JNIEnv* env); +extern int register_android_graphics_Shader(JNIEnv* env); +extern int register_android_graphics_Typeface(JNIEnv* env); + +namespace android { + +extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_ColorSpace(JNIEnv* env); +extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_Matrix(JNIEnv* env); +extern int register_android_graphics_Paint(JNIEnv* env); +extern int register_android_graphics_Path(JNIEnv* env); +extern int register_android_graphics_PathMeasure(JNIEnv* env); +extern int register_android_graphics_Picture(JNIEnv* env); +//extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); +extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); +extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); +extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); +extern int register_android_graphics_fonts_Font(JNIEnv* env); +extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_text_LineBreaker(JNIEnv* env); +extern int register_android_graphics_text_MeasuredText(JNIEnv* env); +extern int register_android_util_PathParser(JNIEnv* env); +extern int register_android_view_RenderNode(JNIEnv* env); +extern int register_android_view_DisplayListCanvas(JNIEnv* env); + +#define REG_JNI(name) { name } +struct RegJNIRec { + int (*mProc)(JNIEnv*); +}; + +// Map of all possible class names to register to their corresponding JNI registration function pointer +// The actual list of registered classes will be determined at runtime via the 'native_classes' System property +static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { + {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)}, + {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)}, + {"android.graphics.ByteBufferStreamAdaptor", + REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)}, + {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)}, + {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)}, + {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)}, + {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)}, + {"android.graphics.CreateJavaOutputStreamAdaptor", + REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)}, + {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)}, + {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)}, + {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)}, + {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)}, + {"android.graphics.Interpolator", REG_JNI(register_android_graphics_Interpolator)}, + {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)}, + {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)}, + {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)}, + {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)}, + {"android.graphics.Path", REG_JNI(register_android_graphics_Path)}, + {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)}, + {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)}, + {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)}, + {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)}, +// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)}, + {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)}, + {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)}, + {"android.graphics.animation.NativeInterpolatorFactory", + REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)}, + {"android.graphics.animation.RenderNodeAnimator", + REG_JNI(register_android_graphics_animation_RenderNodeAnimator)}, + {"android.graphics.drawable.AnimatedVectorDrawable", + REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)}, + {"android.graphics.drawable.VectorDrawable", + REG_JNI(register_android_graphics_drawable_VectorDrawable)}, + {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)}, + {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)}, + {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)}, + {"android.graphics.text.MeasuredText", + REG_JNI(register_android_graphics_text_MeasuredText)}, + {"android.util.PathParser", REG_JNI(register_android_util_PathParser)}, +}; + +static int register_jni_procs(const std::unordered_map<std::string, RegJNIRec>& jniRegMap, + const vector<string>& classesToRegister, JNIEnv* env) { + + for (const string& className : classesToRegister) { + if (jniRegMap.at(className).mProc(env) < 0) { + return -1; + } + } + return 0; +} + +static vector<string> parseCsv(const string& csvString) { + vector<string> result; + istringstream stream(csvString); + string segment; + while(getline(stream, segment, ',')) + { + result.push_back(segment); + } + return result; +} + +static vector<string> parseCsv(JNIEnv* env, jstring csvJString) { + const char* charArray = env->GetStringUTFChars(csvJString, 0); + string csvString(charArray); + vector<string> result = parseCsv(csvString); + env->ReleaseStringUTFChars(csvJString, charArray); + return result; +} + +} // namespace android + +using namespace android; + +void init_android_graphics() { + SkGraphics::Init(); +} + +int register_android_graphics_classes(JNIEnv *env) { + JavaVM* vm = nullptr; + env->GetJavaVM(&vm); + GraphicsJNI::setJavaVM(vm); + + // Configuration is stored as java System properties. + // Get a reference to System.getProperty + jclass system = FindClassOrDie(env, "java/lang/System"); + jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + + // Get the names of classes that need to register their native methods + auto nativesClassesJString = + (jstring) env->CallStaticObjectMethod(system, + getPropertyMethod, env->NewStringUTF("native_classes"), + env->NewStringUTF("")); + vector<string> classesToRegister = parseCsv(env, nativesClassesJString); + + if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) { + return JNI_ERR; + } + + return 0; +} + +void zygote_preload_graphics() { } diff --git a/core/jni/android/graphics/apex/TypeCast.h b/libs/hwui/apex/TypeCast.h index 96721d007951..96721d007951 100644 --- a/core/jni/android/graphics/apex/TypeCast.h +++ b/libs/hwui/apex/TypeCast.h diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp index decd19071944..b56a619b00aa 100644 --- a/core/jni/android/graphics/apex/android_bitmap.cpp +++ b/libs/hwui/apex/android_bitmap.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Bitmap" #include <log/log.h> diff --git a/core/jni/android/graphics/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp index 2a939efed9bb..2a939efed9bb 100644 --- a/core/jni/android/graphics/apex/android_canvas.cpp +++ b/libs/hwui/apex/android_canvas.cpp diff --git a/core/jni/android/graphics/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp index 309360d9b0b3..693b22b62663 100644 --- a/core/jni/android/graphics/apex/android_matrix.cpp +++ b/libs/hwui/apex/android_matrix.cpp @@ -15,7 +15,7 @@ */ #include "android/graphics/matrix.h" -#include "Matrix.h" +#include "android_graphics_Matrix.h" bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) { static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index"); diff --git a/core/jni/android/graphics/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp index 70bd085343ce..70bd085343ce 100644 --- a/core/jni/android/graphics/apex/android_paint.cpp +++ b/libs/hwui/apex/android_paint.cpp diff --git a/core/jni/android/graphics/apex/android_region.cpp b/libs/hwui/apex/android_region.cpp index 2030e7e69861..2030e7e69861 100644 --- a/core/jni/android/graphics/apex/android_region.cpp +++ b/libs/hwui/apex/android_region.cpp diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/libs/hwui/apex/include/android/graphics/bitmap.h index 45fec2ab7b43..8c4b439d2a2b 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h +++ b/libs/hwui/apex/include/android/graphics/bitmap.h @@ -18,6 +18,7 @@ #include <android/bitmap.h> #include <android/data_space.h> +#include <cutils/compiler.h> #include <jni.h> #include <sys/cdefs.h> @@ -37,31 +38,31 @@ typedef struct ABitmap ABitmap; * NOTE: This API does not need to remain as an APEX API if/when we pull libjnigraphics into the * UI module. */ -AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj); +ANDROID_API AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj); /** * * @return ptr to an opaque handle to the native bitmap or null if the java bitmap has been recycled * or does not exist. */ -ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj); +ANDROID_API ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj); -ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat); +ANDROID_API ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat); -void ABitmap_acquireRef(ABitmap* bitmap); -void ABitmap_releaseRef(ABitmap* bitmap); +ANDROID_API void ABitmap_acquireRef(ABitmap* bitmap); +ANDROID_API void ABitmap_releaseRef(ABitmap* bitmap); -AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); -ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); +ANDROID_API AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); +ANDROID_API ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); -void* ABitmap_getPixels(ABitmap* bitmap); -void ABitmap_notifyPixelsChanged(ABitmap* bitmap); +ANDROID_API void* ABitmap_getPixels(ABitmap* bitmap); +ANDROID_API void ABitmap_notifyPixelsChanged(ABitmap* bitmap); -AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); -jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); +ANDROID_API AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); +ANDROID_API jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); // NDK access -int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, +ANDROID_API int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, AndroidBitmapCompressFormat format, int32_t quality, void* userContext, AndroidBitmap_CompressWriteFunc); /** @@ -75,7 +76,7 @@ int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const * a reference on the buffer, and the client must call * AHardwareBuffer_release when finished with it. */ -AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); +ANDROID_API AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); __END_DECLS diff --git a/core/jni/android/graphics/apex/include/android/graphics/canvas.h b/libs/hwui/apex/include/android/graphics/canvas.h index 6fd6b0693b37..a0cecc04a7e5 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/canvas.h +++ b/libs/hwui/apex/include/android/graphics/canvas.h @@ -20,6 +20,7 @@ #include <android/graphics/paint.h> #include <android/native_window.h> #include <android/rect.h> +#include <cutils/compiler.h> #include <jni.h> __BEGIN_DECLS @@ -30,24 +31,24 @@ __BEGIN_DECLS typedef struct ACanvas ACanvas; // One of AHardwareBuffer_Format. -bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat); +ANDROID_API bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat); /** * Returns a native handle to a Java android.graphics.Canvas * * @return ACanvas* that is only valid for the life of the jobject. */ -ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas); +ANDROID_API ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas); /** * Creates a canvas that wraps the buffer * * @param buffer is a required param. If no buffer is provided a nullptr will be returned. */ -ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, +ANDROID_API ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, int32_t /*android_dataspace_t*/ dataspace); -void ACanvas_destroyCanvas(ACanvas* canvas); +ANDROID_API void ACanvas_destroyCanvas(ACanvas* canvas); /** * Updates the canvas to render into the pixels in the provided buffer @@ -60,7 +61,7 @@ void ACanvas_destroyCanvas(ACanvas* canvas); * method will behave as if nullptr were passed as the input buffer and the previous buffer * will still be released. */ -bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, +ANDROID_API bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, int32_t /*android_dataspace_t*/ dataspace); /** @@ -68,21 +69,21 @@ bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, * * @param clipRect required */ -void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); +ANDROID_API void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); /** * Clips operations on the canvas to the difference of the current clip and the provided clipRect. * * @param clipRect required */ -void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); +ANDROID_API void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); /** * * @param rect required * @param paint required */ -void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); +ANDROID_API void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); /** * @@ -91,7 +92,7 @@ void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); * @param top * @param paint */ -void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, +ANDROID_API void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, const APaint* paint); __END_DECLS diff --git a/core/jni/android/graphics/apex/include/android/graphics/jni_runtime.h b/libs/hwui/apex/include/android/graphics/jni_runtime.h index 872a9497ab90..487383ed50d5 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/jni_runtime.h +++ b/libs/hwui/apex/include/android/graphics/jni_runtime.h @@ -16,15 +16,18 @@ #ifndef ANDROID_GRAPHICS_JNI_RUNTIME_H #define ANDROID_GRAPHICS_JNI_RUNTIME_H +#include <cutils/compiler.h> #include <jni.h> __BEGIN_DECLS -void init_android_graphics(); +ANDROID_API void init_android_graphics(); -int register_android_graphics_classes(JNIEnv* env); +ANDROID_API int register_android_graphics_classes(JNIEnv* env); -void zygote_preload_graphics(); +ANDROID_API int register_android_graphics_GraphicsStatsService(JNIEnv* env); + +ANDROID_API void zygote_preload_graphics(); __END_DECLS diff --git a/core/jni/android/graphics/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h index 4039cd1b8f74..987ad13f7635 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/matrix.h +++ b/libs/hwui/apex/include/android/graphics/matrix.h @@ -18,6 +18,7 @@ #define ANDROID_GRAPHICS_MATRIX_H #include <jni.h> +#include <cutils/compiler.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -31,7 +32,7 @@ __BEGIN_DECLS * @return true if the values param was populated and false otherwise. */ -bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); +ANDROID_API bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); __END_DECLS diff --git a/core/jni/android/graphics/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h index 5895e006bf93..058db8d37619 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/paint.h +++ b/libs/hwui/apex/include/android/graphics/paint.h @@ -16,6 +16,7 @@ #ifndef ANDROID_GRAPHICS_PAINT_H #define ANDROID_GRAPHICS_PAINT_H +#include <cutils/compiler.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -35,11 +36,11 @@ enum ABlendMode { ABLEND_MODE_SRC = 2, }; -APaint* APaint_createPaint(); +ANDROID_API APaint* APaint_createPaint(); -void APaint_destroyPaint(APaint* paint); +ANDROID_API void APaint_destroyPaint(APaint* paint); -void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); +ANDROID_API void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); __END_DECLS diff --git a/core/jni/android/graphics/apex/include/android/graphics/region.h b/libs/hwui/apex/include/android/graphics/region.h index 961067a8e2db..0756d6dd63f6 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/region.h +++ b/libs/hwui/apex/include/android/graphics/region.h @@ -16,6 +16,7 @@ #ifndef ANDROID_GRAPHICS_REGION_H #define ANDROID_GRAPHICS_REGION_H +#include <cutils/compiler.h> #include <android/rect.h> #include <sys/cdefs.h> #include <jni.h> @@ -35,19 +36,19 @@ typedef struct ARegionIterator ARegionIterator; * @return ARegionIterator that must be closed and must not live longer than the life * of the jobject. It returns nullptr if the region is not a valid object. */ -ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region); +ANDROID_API ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region); -void ARegionIterator_releaseIterator(ARegionIterator* iterator); +ANDROID_API void ARegionIterator_releaseIterator(ARegionIterator* iterator); -bool ARegionIterator_isComplex(ARegionIterator* iterator); +ANDROID_API bool ARegionIterator_isComplex(ARegionIterator* iterator); -bool ARegionIterator_isDone(ARegionIterator* iterator); +ANDROID_API bool ARegionIterator_isDone(ARegionIterator* iterator); -void ARegionIterator_next(ARegionIterator* iterator); +ANDROID_API void ARegionIterator_next(ARegionIterator* iterator); -ARect ARegionIterator_getRect(ARegionIterator* iterator); +ANDROID_API ARect ARegionIterator_getRect(ARegionIterator* iterator); -ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator); +ANDROID_API ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator); __END_DECLS diff --git a/core/jni/android/graphics/apex/include/android/graphics/renderthread.h b/libs/hwui/apex/include/android/graphics/renderthread.h index 0a790af731a9..50280a6dd1fb 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/renderthread.h +++ b/libs/hwui/apex/include/android/graphics/renderthread.h @@ -16,6 +16,7 @@ #ifndef ANDROID_GRAPHICS_RENDERTHREAD_H #define ANDROID_GRAPHICS_RENDERTHREAD_H +#include <cutils/compiler.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -26,7 +27,7 @@ __BEGIN_DECLS * function requires a valid fd, but does not persist or assume ownership of the fd * outside the scope of this function. */ -void ARenderThread_dumpGraphicsMemory(int fd); +ANDROID_API void ARenderThread_dumpGraphicsMemory(int fd); __END_DECLS diff --git a/core/jni/android/graphics/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index 35c997d80b61..a114e2f42157 100644 --- a/core/jni/android/graphics/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -21,9 +21,11 @@ #include <sys/cdefs.h> #include <EGL/egl.h> +#include <GraphicsJNI.h> #include <Properties.h> #include <SkGraphics.h> +#undef LOG_TAG #define LOG_TAG "AndroidGraphicsJNI" extern int register_android_graphics_Bitmap(JNIEnv*); @@ -59,7 +61,6 @@ extern int register_android_graphics_Path(JNIEnv* env); extern int register_android_graphics_PathMeasure(JNIEnv* env); extern int register_android_graphics_Picture(JNIEnv*); extern int register_android_graphics_Region(JNIEnv* env); -extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); @@ -122,7 +123,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Picture), REG_JNI(register_android_graphics_Region), REG_JNI(register_android_graphics_Shader), - REG_JNI(register_android_graphics_SurfaceTexture), REG_JNI(register_android_graphics_Typeface), REG_JNI(register_android_graphics_YuvImage), REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory), @@ -151,6 +151,10 @@ void init_android_graphics() { } int register_android_graphics_classes(JNIEnv *env) { + JavaVM* vm = nullptr; + env->GetJavaVM(&vm); + GraphicsJNI::setJavaVM(vm); + for (size_t i = 0; i < NELEM(android::gRegJNI); i++) { if (android::gRegJNI[i].mProc(env) < 0) { #ifndef NDEBUG diff --git a/core/jni/android/graphics/apex/renderthread.cpp b/libs/hwui/apex/renderthread.cpp index 5d26afe7a2ab..5d26afe7a2ab 100644 --- a/core/jni/android/graphics/apex/renderthread.cpp +++ b/libs/hwui/apex/renderthread.cpp diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 914c04645289..56d951cdb338 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -150,11 +150,7 @@ sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorS AHardwareBuffer_Desc bufferDesc; AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace); - - // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) - const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; - const size_t rowBytes = info.bytesPerPixel() * bufferStride; - return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette)); + return createFrom(hardwareBuffer, info, bufferDesc, palette); } sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType, @@ -164,8 +160,14 @@ sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType co AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); + return createFrom(hardwareBuffer, info, bufferDesc, palette); +} - const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride; +sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) { + // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) + const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; + const size_t rowBytes = info.bytesPerPixel() * bufferStride; return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette)); } #endif diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 3bfb7800f735..b8b59947a57b 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -169,6 +169,12 @@ private: #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, BitmapPalette palette); + + // Common code for the two public facing createFrom(AHardwareBuffer*, ...) + // methods. + // bufferDesc is only used to compute rowBytes. + static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette); #endif virtual ~Bitmap(); diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 6c2a5a3f3fcc..055075d0c42a 100644 --- a/core/jni/android/graphics/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -17,7 +17,6 @@ #include "GraphicsJNI.h" #include "ImageDecoder.h" #include "Utils.h" -#include "core_jni_helpers.h" #include <SkAndroidCodec.h> #include <SkAnimatedImage.h> diff --git a/core/jni/android/graphics/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 130322aaaa45..ba669053ed63 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -1,3 +1,4 @@ +#undef LOG_TAG #define LOG_TAG "Bitmap" #include "Bitmap.h" @@ -11,7 +12,6 @@ #include "SkStream.h" #include "SkWebpEncoder.h" -#include "android_os_Parcel.h" #include "android_nio_utils.h" #include "CreateJavaOutputStreamAdaptor.h" #include <hwui/Paint.h> @@ -19,15 +19,12 @@ #include <utils/Color.h> #ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread -#include <android_runtime/android_graphics_GraphicBuffer.h> +#include <private/android/AHardwareBufferHelpers.h> #include <binder/Parcel.h> #include <dlfcn.h> #include <renderthread/RenderProxy.h> #endif -#include "core_jni_helpers.h" - -#include <jni.h> #include <string.h> #include <memory> #include <string> @@ -570,6 +567,25 @@ static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, /////////////////////////////////////////////////////////////////////////////// +#ifdef __ANDROID__ // Layoutlib does not support parcel +static struct parcel_offsets_t +{ + jclass clazz; + jfieldID mNativePtr; +} gParcelOffsets; + +static Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { + if (obj) { + Parcel* p = (Parcel*)env->GetLongField(obj, gParcelOffsets.mNativePtr); + if (p != NULL) { + return p; + } + jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); + } + return NULL; +} +#endif + // This is the maximum possible size because the SkColorSpace must be // representable (and therefore serializable) using a matrix and numerical // transfer function. If we allow more color space representations in the @@ -583,7 +599,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { return NULL; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); + android::Parcel* p = parcelForJavaObject(env, parcel); const SkColorType colorType = (SkColorType)p->readInt32(); const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); @@ -706,7 +722,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, return JNI_FALSE; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); + android::Parcel* p = parcelForJavaObject(env, parcel); SkBitmap bitmap; auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle); @@ -1050,19 +1066,6 @@ static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject har #endif } -static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) { -#ifdef __ANDROID__ // Layoutlib does not support graphic buffer - LocalScopedBitmap bitmapHandle(bitmapPtr); - LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), - "Hardware config is only supported config in Bitmap_getGraphicBuffer"); - - Bitmap& bitmap = bitmapHandle->bitmap(); - return android_graphics_GraphicBuffer_createFromAHardwareBuffer(env, bitmap.hardwareBuffer()); -#else - return NULL; -#endif -} - static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) { #ifdef __ANDROID__ // Layoutlib does not support graphic buffer LocalScopedBitmap bitmapHandle(bitmapPtr); @@ -1140,8 +1143,6 @@ static const JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copyPreserveInternalConfig }, { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", (void*) Bitmap_wrapHardwareBufferBitmap }, - { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", - (void*) Bitmap_createGraphicBufferHandle }, { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;", (void*) Bitmap_getHardwareBuffer }, { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, @@ -1155,6 +1156,8 @@ static const JNINativeMethod gBitmapMethods[] = { }; +const char* const kParcelPathName = "android/os/Parcel"; + int register_android_graphics_Bitmap(JNIEnv* env) { gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); @@ -1162,7 +1165,7 @@ int register_android_graphics_Bitmap(JNIEnv* env) gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); -#ifdef __ANDROID__ // Layoutlib does not support graphic buffer +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); AHardwareBuffer_fromHardwareBuffer = (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer"); @@ -1172,6 +1175,9 @@ int register_android_graphics_Bitmap(JNIEnv* env) AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr, " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); + + gParcelOffsets.clazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, kParcelPathName)); + gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, gParcelOffsets.clazz, "mNativePtr", "J"); #endif return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, NELEM(gBitmapMethods)); diff --git a/core/jni/android/graphics/Bitmap.h b/libs/hwui/jni/Bitmap.h index 73eca3aa8ef8..73eca3aa8ef8 100644 --- a/core/jni/android/graphics/Bitmap.h +++ b/libs/hwui/jni/Bitmap.h diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index adedffdd731c..d4e27d812500 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -1,8 +1,10 @@ +#undef LOG_TAG #define LOG_TAG "BitmapFactory" #include "BitmapFactory.h" #include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" +#include "MimeType.h" #include "NinePatchPeeker.h" #include "SkAndroidCodec.h" #include "SkBRDAllocator.h" @@ -12,17 +14,15 @@ #include "SkStream.h" #include "SkUtils.h" #include "Utils.h" -#include "core_jni_helpers.h" #include <HardwareBitmapUploader.h> #include <nativehelper/JNIHelp.h> #include <androidfw/Asset.h> #include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> +#include <fcntl.h> #include <memory> -#include <netinet/in.h> #include <stdio.h> -#include <sys/mman.h> #include <sys/stat.h> jfieldID gOptions_justBoundsFieldID; @@ -521,7 +521,9 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) { - +#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return nullObjectReturn("Not supported on Windows"); +#else NPE_CHECK_RETURN_ZERO(env, fileDescriptor); int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -568,6 +570,7 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle, colorSpaceHandle); +#endif } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, diff --git a/core/jni/android/graphics/BitmapFactory.h b/libs/hwui/jni/BitmapFactory.h index 45bffc44967d..45bffc44967d 100644 --- a/core/jni/android/graphics/BitmapFactory.h +++ b/libs/hwui/jni/BitmapFactory.h diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 06b4ff849097..712351382d97 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "BitmapRegionDecoder" #include "BitmapFactory.h" @@ -27,12 +28,8 @@ #include "SkData.h" #include "SkStream.h" -#include "core_jni_helpers.h" - #include <HardwareBitmapUploader.h> -#include <nativehelper/JNIHelp.h> #include <androidfw/Asset.h> -#include <jni.h> #include <sys/stat.h> #include <memory> diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp index d443fd8cdf14..db5f6f6c684f 100644 --- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -1,7 +1,6 @@ #include "ByteBufferStreamAdaptor.h" -#include "core_jni_helpers.h" +#include "GraphicsJNI.h" #include "Utils.h" -#include <jni.h> #include <SkStream.h> diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.h b/libs/hwui/jni/ByteBufferStreamAdaptor.h index 367a48fad9b9..367a48fad9b9 100644 --- a/core/jni/android/graphics/ByteBufferStreamAdaptor.h +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.h diff --git a/core/jni/android/graphics/Camera.cpp b/libs/hwui/jni/Camera.cpp index da954972ab57..a5e1adf26861 100644 --- a/core/jni/android/graphics/Camera.cpp +++ b/libs/hwui/jni/Camera.cpp @@ -1,6 +1,3 @@ -#include "jni.h" -#include "core_jni_helpers.h" - #include "SkCamera.h" #include "GraphicsJNI.h" diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/libs/hwui/jni/CanvasProperty.cpp index c841d6a5125a..684ee23b9fca 100644 --- a/core/jni/android/graphics/CanvasProperty.cpp +++ b/libs/hwui/jni/CanvasProperty.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include <core_jni_helpers.h> #include <hwui/Paint.h> #include <utils/RefBase.h> diff --git a/core/jni/android/graphics/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp index 164d35f46a47..cef21f91f3c1 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/libs/hwui/jni/ColorFilter.cpp @@ -15,9 +15,7 @@ ** limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp index 39483b55992b..39483b55992b 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h index fccd4717c4b7..849418da01a1 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h @@ -1,7 +1,6 @@ #ifndef _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ #define _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ -//#include <android_runtime/AndroidRuntime.h> #include "jni.h" class SkMemoryStream; diff --git a/core/jni/android/graphics/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index 0fd9cc7e9989..a2fef1e19328 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -14,11 +14,9 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Minikin" -#include <nativehelper/JNIHelp.h> -#include <core_jni_helpers.h> - #include "SkData.h" #include "SkFontMgr.h" #include "SkRefCnt.h" @@ -26,7 +24,6 @@ #include "GraphicsJNI.h" #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> -#include <android_runtime/AndroidRuntime.h> #include "Utils.h" #include "FontUtils.h" @@ -144,15 +141,11 @@ static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, in } static void release_global_ref(const void* /*data*/, void* context) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = GraphicsJNI::getJNIEnv(); bool needToAttach = (env == NULL); if (needToAttach) { - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_4; - args.name = "release_font_data"; - args.group = NULL; - jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); - if (result != JNI_OK) { + env = GraphicsJNI::attachJNIEnv("release_font_data"); + if (env == nullptr) { ALOGE("failed to attach to thread to release global ref."); return; } @@ -162,7 +155,7 @@ static void release_global_ref(const void* /*data*/, void* context) { env->DeleteGlobalRef(obj); if (needToAttach) { - AndroidRuntime::getJavaVM()->DetachCurrentThread(); + GraphicsJNI::detachJNIEnv(); } } diff --git a/core/jni/android/graphics/FontUtils.cpp b/libs/hwui/jni/FontUtils.cpp index 0cf61b9ade89..654c5fdf6528 100644 --- a/core/jni/android/graphics/FontUtils.cpp +++ b/libs/hwui/jni/FontUtils.cpp @@ -16,8 +16,7 @@ #include "FontUtils.h" -#include <nativehelper/JNIHelp.h> -#include <core_jni_helpers.h> +#include "graphics_jni_helpers.h" namespace android { namespace { diff --git a/core/jni/android/graphics/FontUtils.h b/libs/hwui/jni/FontUtils.h index b36b4e60e33a..b36b4e60e33a 100644 --- a/core/jni/android/graphics/FontUtils.h +++ b/libs/hwui/jni/FontUtils.h diff --git a/core/jni/android/graphics/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp index f84a4bd09073..f84a4bd09073 100644 --- a/core/jni/android/graphics/GIFMovie.cpp +++ b/libs/hwui/jni/GIFMovie.cpp diff --git a/core/jni/android/graphics/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 38fb8bdc1f7a..f76ecb4c9c8a 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -1,22 +1,55 @@ +#undef LOG_TAG #define LOG_TAG "GraphicsJNI" +#include <assert.h> #include <unistd.h> -#include <sys/mman.h> #include "jni.h" #include <nativehelper/JNIHelp.h> #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkCanvas.h" #include "SkMath.h" #include "SkRegion.h" -#include <android_runtime/AndroidRuntime.h> #include <cutils/ashmem.h> #include <hwui/Canvas.h> using namespace android; +/*static*/ JavaVM* GraphicsJNI::mJavaVM = nullptr; + +void GraphicsJNI::setJavaVM(JavaVM* javaVM) { + mJavaVM = javaVM; +} + +/** return a pointer to the JNIEnv for this thread */ +JNIEnv* GraphicsJNI::getJNIEnv() { + assert(mJavaVM != nullptr); + JNIEnv* env; + if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + return nullptr; + } + return env; +} + +/** create a JNIEnv* for this thread or assert if one already exists */ +JNIEnv* GraphicsJNI::attachJNIEnv(const char* envName) { + assert(getJNIEnv() == nullptr); + JNIEnv* env = nullptr; + JavaVMAttachArgs args = { JNI_VERSION_1_4, envName, NULL }; + int result = mJavaVM->AttachCurrentThread(&env, (void*) &args); + if (result != JNI_OK) { + ALOGE("thread attach failed: %#x", result); + } + return env; +} + +/** detach the current thread from the JavaVM */ +void GraphicsJNI::detachJNIEnv() { + assert(mJavaVM != nullptr); + mJavaVM->DetachCurrentThread(); +} + void doThrowNPE(JNIEnv* env) { jniThrowNullPointerException(env, NULL); } diff --git a/core/jni/android/graphics/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 1e497654f18d..b58a740a4c27 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -1,6 +1,8 @@ #ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ +#include <cutils/compiler.h> + #include "Bitmap.h" #include "SkBitmap.h" #include "SkBRDAllocator.h" @@ -10,10 +12,11 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkColorSpace.h" -#include <jni.h> #include <hwui/Canvas.h> #include <hwui/Bitmap.h> +#include "graphics_jni_helpers.h" + class SkBitmapRegionDecoder; class SkCanvas; @@ -39,6 +42,20 @@ public: kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig }; + static void setJavaVM(JavaVM* javaVM); + + /** returns a pointer to the JavaVM provided when we initialized the module */ + static JavaVM* getJavaVM() { return mJavaVM; } + + /** return a pointer to the JNIEnv for this thread */ + static JNIEnv* getJNIEnv(); + + /** create a JNIEnv* for this thread or assert if one already exists */ + static JNIEnv* attachJNIEnv(const char* envName); + + /** detach the current thread from the JavaVM */ + static void detachJNIEnv(); + // returns true if an exception is set (and dumps it out to the Log) static bool hasException(JNIEnv*); @@ -60,7 +77,7 @@ public: static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); - static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); + ANDROID_API static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap); static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes, bool* isHardware); @@ -131,6 +148,10 @@ public: * above. */ static SkColor4f convertColorLong(jlong color); + +private: + /* JNI JavaVM pointer */ + static JavaVM* mJavaVM; }; class HeapAllocator : public SkBRDAllocator { diff --git a/core/jni/android/graphics/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index ef0aacc4d9ec..6076552fc094 100644 --- a/core/jni/android/graphics/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -14,19 +14,20 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "GraphicsStatsService" #include <JankTracker.h> -#include <jni.h> #include <log/log.h> -#include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> #include <service/GraphicsStatsService.h> #include <stats_event.h> #include <stats_pull_atom_callback.h> #include <statslog.h> -#include "core_jni_helpers.h" + +#include "android/graphics/jni_runtime.h" +#include "GraphicsJNI.h" namespace android { @@ -115,7 +116,7 @@ static jobject gGraphicsStatsServiceObject = nullptr; static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID; static JNIEnv* getJNIEnv() { - JavaVM* vm = AndroidRuntime::getJavaVM(); + JavaVM* vm = GraphicsJNI::getJavaVM(); JNIEnv* env = nullptr; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { @@ -172,6 +173,9 @@ static void nativeDestructor(JNIEnv* env, jobject javaObject) { gGraphicsStatsServiceObject = nullptr; } +} // namespace android +using namespace android; + static const JNINativeMethod sMethods[] = {{"nGetAshmemSize", "()I", (void*)getAshmemSize}, {"nCreateDump", "(IZ)J", (void*)createDump}, @@ -191,5 +195,3 @@ int register_android_graphics_GraphicsStatsService(JNIEnv* env) { return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods, NELEM(sMethods)); } - -} // namespace android diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index e17e057d75c7..b6b378539bd0 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -22,7 +22,6 @@ #include "ImageDecoder.h" #include "NinePatchPeeker.h" #include "Utils.h" -#include "core_jni_helpers.h" #include <hwui/Bitmap.h> #include <hwui/ImageDecoder.h> @@ -34,7 +33,7 @@ #include <SkStream.h> #include <androidfw/Asset.h> -#include <jni.h> +#include <fcntl.h> #include <sys/stat.h> using namespace android; @@ -154,6 +153,9 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor, jboolean preferAnimation, jobject source) { +#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source); +#else int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); struct stat fdStat; @@ -172,6 +174,7 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); return native_create(env, std::move(fileStream), source, preferAnimation); +#endif } static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, diff --git a/core/jni/android/graphics/ImageDecoder.h b/libs/hwui/jni/ImageDecoder.h index 8a7fa79503ba..8a7fa79503ba 100644 --- a/core/jni/android/graphics/ImageDecoder.h +++ b/libs/hwui/jni/ImageDecoder.h diff --git a/core/jni/android/graphics/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp index fa28359281db..146d634a297c 100644 --- a/core/jni/android/graphics/Interpolator.cpp +++ b/libs/hwui/jni/Interpolator.cpp @@ -1,8 +1,5 @@ #include "GraphicsJNI.h" #include "SkInterpolator.h" -#include "core_jni_helpers.h" - -#include <jni.h> static jlong Interpolator_constructor(JNIEnv* env, jobject clazz, jint valueCount, jint frameCount) { diff --git a/core/jni/android/graphics/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp index 33d346f5d379..5383032e0f77 100644 --- a/core/jni/android/graphics/MaskFilter.cpp +++ b/libs/hwui/jni/MaskFilter.cpp @@ -4,10 +4,6 @@ #include "SkBlurMaskFilter.h" #include "SkTableMaskFilter.h" -#include "core_jni_helpers.h" - -#include <jni.h> - static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { if (NULL == ptr) { doThrowIAE(env); diff --git a/core/jni/android/graphics/MimeType.h b/libs/hwui/jni/MimeType.h index 38a579c595e4..fdd510cfeb79 100644 --- a/core/jni/android/graphics/MimeType.h +++ b/libs/hwui/jni/MimeType.h @@ -16,6 +16,7 @@ #pragma once +#include <cutils/compiler.h> #include "SkEncodedImageFormat.h" -const char* getMimeType(SkEncodedImageFormat); +ANDROID_API const char* getMimeType(SkEncodedImageFormat); diff --git a/core/jni/android/graphics/Movie.cpp b/libs/hwui/jni/Movie.cpp index 4c10a85c8257..ede0ca8cda5b 100644 --- a/core/jni/android/graphics/Movie.cpp +++ b/libs/hwui/jni/Movie.cpp @@ -6,13 +6,11 @@ #include "SkStream.h" #include "SkUtils.h" #include "Utils.h" -#include "core_jni_helpers.h" #include <androidfw/Asset.h> #include <androidfw/ResourceTypes.h> #include <hwui/Canvas.h> #include <hwui/Paint.h> -#include <jni.h> #include <netinet/in.h> static jclass gMovie_class; diff --git a/core/jni/android/graphics/Movie.h b/libs/hwui/jni/Movie.h index 736890d5215e..736890d5215e 100644 --- a/core/jni/android/graphics/Movie.h +++ b/libs/hwui/jni/Movie.h diff --git a/core/jni/android/graphics/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp index ae9e04e617b0..ae9e04e617b0 100644 --- a/core/jni/android/graphics/MovieImpl.cpp +++ b/libs/hwui/jni/MovieImpl.cpp diff --git a/core/jni/android/graphics/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp index 15f951688d43..6942017d5f27 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -15,6 +15,7 @@ ** limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "9patch" #define LOG_NDEBUG 1 @@ -30,9 +31,6 @@ #include "NinePatchPeeker.h" #include "NinePatchUtils.h" -#include <nativehelper/JNIHelp.h> -#include "core_jni_helpers.h" - jclass gInsetStruct_class; jmethodID gInsetStruct_constructorMethodID; diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp index 9171fc687276..9171fc687276 100644 --- a/core/jni/android/graphics/NinePatchPeeker.cpp +++ b/libs/hwui/jni/NinePatchPeeker.cpp diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/libs/hwui/jni/NinePatchPeeker.h index e4e58dda4783..e4e58dda4783 100644 --- a/core/jni/android/graphics/NinePatchPeeker.h +++ b/libs/hwui/jni/NinePatchPeeker.h diff --git a/core/jni/android/graphics/Paint.cpp b/libs/hwui/jni/Paint.cpp index 8e1bc8489baa..df8635a8fe5a 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -15,13 +15,12 @@ ** limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Paint" #include <utils/Log.h> -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include <nativehelper/ScopedStringChars.h> #include <nativehelper/ScopedUtfChars.h> #include <nativehelper/ScopedPrimitiveArray.h> diff --git a/core/jni/android/graphics/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp index 4fe9140572d3..ec115b4e141c 100644 --- a/core/jni/android/graphics/PaintFilter.cpp +++ b/libs/hwui/jni/PaintFilter.cpp @@ -15,11 +15,7 @@ ** limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include <android_runtime/AndroidRuntime.h> - -#include "core_jni_helpers.h" #include "hwui/Paint.h" #include "hwui/PaintFilter.h" diff --git a/core/jni/android/graphics/Path.cpp b/libs/hwui/jni/Path.cpp index 481445258e3c..d67bcf221681 100644 --- a/core/jni/android/graphics/Path.cpp +++ b/libs/hwui/jni/Path.cpp @@ -20,9 +20,7 @@ // To change this file, either edit the include, or device/tools/gluemaker/main.cpp, // or one of the auxilary file specifications in device/tools/gluemaker. -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkPath.h" #include "SkPathOps.h" diff --git a/core/jni/android/graphics/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp index a4992de72ff6..f99bef7b7d58 100644 --- a/core/jni/android/graphics/PathEffect.cpp +++ b/libs/hwui/jni/PathEffect.cpp @@ -4,9 +4,6 @@ #include "SkDashPathEffect.h" #include "SkDiscretePathEffect.h" #include "SkPathEffect.h" -#include "core_jni_helpers.h" - -#include <jni.h> class SkPathEffectGlue { public: diff --git a/core/jni/android/graphics/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp index 70e528d4be6f..acf893e9544c 100644 --- a/core/jni/android/graphics/PathMeasure.cpp +++ b/libs/hwui/jni/PathMeasure.cpp @@ -15,9 +15,7 @@ ** limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include <core_jni_helpers.h> #include "SkPathMeasure.h" diff --git a/core/jni/android/graphics/Picture.cpp b/libs/hwui/jni/Picture.cpp index d1b952130e88..d1b952130e88 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/libs/hwui/jni/Picture.cpp diff --git a/core/jni/android/graphics/Picture.h b/libs/hwui/jni/Picture.h index 536f651473a9..536f651473a9 100644 --- a/core/jni/android/graphics/Picture.h +++ b/libs/hwui/jni/Picture.h diff --git a/core/jni/android/graphics/Region.cpp b/libs/hwui/jni/Region.cpp index 87662f713449..1e064b820591 100644 --- a/core/jni/android/graphics/Region.cpp +++ b/libs/hwui/jni/Region.cpp @@ -19,13 +19,10 @@ #include "GraphicsJNI.h" #ifdef __ANDROID__ // Layoutlib does not support parcel -#include <binder/Parcel.h> +#include <android/binder_parcel.h> +#include <android/binder_parcel_jni.h> +#include <android/binder_parcel_utils.h> #endif -#include "android_os_Parcel.h" -#include "android_util_Binder.h" - -#include <jni.h> -#include <core_jni_helpers.h> namespace android { @@ -210,10 +207,11 @@ static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) return 0; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); - std::vector<int32_t> rects; - p->readInt32Vector(&rects); + + AParcel* p = AParcel_fromJavaParcel(env, parcel); + ndk::AParcel_readVector(p, &rects); + AParcel_delete(p); if ((rects.size() % 4) != 0) { return 0; @@ -238,8 +236,6 @@ static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHan return JNI_FALSE; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); - std::vector<int32_t> rects; SkRegion::Iterator it(*region); while (!it.done()) { @@ -251,7 +247,10 @@ static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHan it.next(); } - p->writeInt32Vector(rects); + AParcel* p = AParcel_fromJavaParcel(env, parcel); + ndk::AParcel_writeVector(p, rects); + AParcel_delete(p); + return JNI_TRUE; #else return JNI_FALSE; diff --git a/core/jni/android/graphics/RtlProperties.h b/libs/hwui/jni/RtlProperties.h index 907dd59b6e68..907dd59b6e68 100644 --- a/core/jni/android/graphics/RtlProperties.h +++ b/libs/hwui/jni/RtlProperties.h diff --git a/core/jni/android/graphics/Shader.cpp b/libs/hwui/jni/Shader.cpp index f5e2a5244416..0f6837640524 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -4,11 +4,8 @@ #include "SkImagePriv.h" #include "SkShader.h" #include "SkBlendMode.h" -#include "core_jni_helpers.h" #include "include/effects/SkRuntimeEffect.h" -#include <jni.h> - #include <vector> using namespace android::uirenderer; diff --git a/core/jni/android/graphics/TEST_MAPPING b/libs/hwui/jni/TEST_MAPPING index 10bd0ee906fd..10bd0ee906fd 100644 --- a/core/jni/android/graphics/TEST_MAPPING +++ b/libs/hwui/jni/TEST_MAPPING diff --git a/core/jni/android/graphics/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index 4ce56ba7444f..2a5f402a4fa6 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#include "jni.h" -#include "core_jni_helpers.h" - #include "FontUtils.h" #include "GraphicsJNI.h" #include <nativehelper/ScopedPrimitiveArray.h> diff --git a/core/jni/android/graphics/Utils.cpp b/libs/hwui/jni/Utils.cpp index 17c194d04f84..17c194d04f84 100644 --- a/core/jni/android/graphics/Utils.cpp +++ b/libs/hwui/jni/Utils.cpp diff --git a/core/jni/android/graphics/Utils.h b/libs/hwui/jni/Utils.h index 89255177ba2e..89255177ba2e 100644 --- a/core/jni/android/graphics/Utils.h +++ b/libs/hwui/jni/Utils.h diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp index 09adc824e520..689cf0bea741 100644 --- a/core/jni/android/graphics/YuvToJpegEncoder.cpp +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -4,9 +4,7 @@ #include <ui/PixelFormat.h> #include <hardware/hardware.h> -#include "core_jni_helpers.h" - -#include <jni.h> +#include "graphics_jni_helpers.h" YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported diff --git a/core/jni/android/graphics/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h index 7e7b935df276..7e7b935df276 100644 --- a/core/jni/android/graphics/YuvToJpegEncoder.h +++ b/libs/hwui/jni/YuvToJpegEncoder.h diff --git a/core/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 0ad3339ee05f..4aff3e544efa 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #ifdef __ANDROID_ #include <android/api-level.h> diff --git a/core/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp index 7648fd021d18..232fd71a12b4 100644 --- a/core/jni/android_graphics_ColorSpace.cpp +++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp @@ -14,12 +14,11 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkColor.h" #include "SkColorSpace.h" +#include "SkHalf.h" using namespace android; @@ -42,9 +41,13 @@ static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { /////////////////////////////////////////////////////////////////////////////// static float halfToFloat(uint16_t bits) { - __fp16 h; - memcpy(&h, &bits, 2); - return (float)h; +#ifdef __ANDROID__ // __fp16 is not defined on non-Android builds + __fp16 h; + memcpy(&h, &bits, 2); + return (float)h; +#else + return SkHalfToFloat(bits); +#endif } SkColor4f GraphicsJNI::convertColorLong(jlong color) { diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index 9907da5a2e68..54822f1f07e2 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -14,13 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - -#include "jni.h" #include "GraphicsJNI.h" -#include <nativehelper/JNIHelp.h> -#include <android_runtime/AndroidRuntime.h> #ifdef __ANDROID__ // Layoutlib does not support Looper and device properties #include <utils/Looper.h> #endif @@ -38,8 +33,6 @@ #include <renderthread/RenderProxy.h> #endif -#include "core_jni_helpers.h" - namespace android { using namespace uirenderer; diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 93449ffeae1b..49c7fcd468e1 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "ThreadedRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW @@ -43,8 +44,6 @@ #include <atomic> #include "android_graphics_HardwareRendererObserver.h" -#include "core_jni_helpers.h" -#include "jni.h" namespace android { diff --git a/core/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp index 89b77b0b069a..5b3e65648981 100644 --- a/core/jni/android_graphics_HardwareRendererObserver.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp @@ -16,7 +16,7 @@ #include "android_graphics_HardwareRendererObserver.h" -#include "core_jni_helpers.h" +#include "graphics_jni_helpers.h" #include "nativehelper/jni_macros.h" #include <array> diff --git a/core/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h index 62111fd7d7a1..62111fd7d7a1 100644 --- a/core/jni/android_graphics_HardwareRendererObserver.h +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h diff --git a/core/jni/android/graphics/Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp index 13369763e0cf..7338ef24cb58 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -18,9 +18,6 @@ #include "GraphicsJNI.h" #include "Matrix.h" #include "SkMatrix.h" -#include "core_jni_helpers.h" - -#include <jni.h> namespace android { diff --git a/core/jni/android/graphics/Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h index fe90d2ef945d..fe90d2ef945d 100644 --- a/core/jni/android/graphics/Matrix.h +++ b/libs/hwui/jni/android_graphics_Matrix.h diff --git a/core/jni/android_graphics_Picture.cpp b/libs/hwui/jni/android_graphics_Picture.cpp index 1d085e5ccc49..403efb2ab9c9 100644 --- a/core/jni/android_graphics_Picture.cpp +++ b/libs/hwui/jni/android_graphics_Picture.cpp @@ -19,12 +19,9 @@ #include "Picture.h" #include "SkCanvas.h" #include "SkStream.h" -#include "core_jni_helpers.h" -#include "nativehelper/jni_macros.h" - -#include <jni.h> #include <array> +#include "nativehelper/jni_macros.h" namespace android { diff --git a/core/jni/android_view_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index a8246c7d84b7..85c802b40459 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -14,12 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW -#include "jni.h" #include "GraphicsJNI.h" -#include <nativehelper/JNIHelp.h> -#include <android_runtime/AndroidRuntime.h> #include <Animator.h> #include <DamageAccumulator.h> @@ -32,8 +28,6 @@ #include <hwui/Paint.h> #include <utils/TraceUtils.h> -#include "core_jni_helpers.h" - namespace android { using namespace uirenderer; diff --git a/core/jni/android_view_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp index 40f618025f99..bd20269d3751 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp @@ -14,13 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - -#include "jni.h" -#include <nativehelper/JNIHelp.h> - #include <android/surface_texture_jni.h> -#include "core_jni_helpers.h" +#include "graphics_jni_helpers.h" #include <hwui/Paint.h> #include <SkMatrix.h> diff --git a/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp index 2073ac2d24be..764eff9a04be 100644 --- a/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp +++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp @@ -16,12 +16,10 @@ #define LOG_TAG "OpenGLRenderer" -#include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <Interpolator.h> #include <cutils/log.h> -#include "core_jni_helpers.h" -#include <Interpolator.h> +#include "graphics_jni_helpers.h" namespace android { diff --git a/core/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp index 878d4fc13f6d..c6d26f853c1d 100644 --- a/core/jni/android_graphics_animation_RenderNodeAnimator.cpp +++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp @@ -16,15 +16,11 @@ #define LOG_TAG "OpenGLRenderer" -#include "jni.h" -#include <nativehelper/JNIHelp.h> -#include <android_runtime/AndroidRuntime.h> - #include <Animator.h> #include <Interpolator.h> #include <RenderProperties.h> -#include "core_jni_helpers.h" +#include "graphics_jni_helpers.h" namespace android { diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp index b6b53666e26e..b3121e7b0373 100644 --- a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -13,13 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" #include "android/log.h" -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "Animator.h" #include "Interpolator.h" diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 58a2379a6999..8a262969614e 100644 --- a/core/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -15,8 +15,6 @@ */ #include "GraphicsJNI.h" -#include "jni.h" -#include "core_jni_helpers.h" #include "PathParser.h" #include "VectorDrawable.h" diff --git a/core/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp index 1e6d49e49b72..c2b09c1d15d7 100644 --- a/core/jni/android_nio_utils.cpp +++ b/libs/hwui/jni/android_nio_utils.cpp @@ -16,7 +16,7 @@ #include "android_nio_utils.h" -#include "core_jni_helpers.h" +#include <nativehelper/JNIHelp.h> namespace android { diff --git a/core/jni/android_nio_utils.h b/libs/hwui/jni/android_nio_utils.h index 4aaa0a78c276..4aaa0a78c276 100644 --- a/core/jni/android_nio_utils.h +++ b/libs/hwui/jni/android_nio_utils.h diff --git a/core/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp index 10efb95100ac..df5e9cd44ed0 100644 --- a/core/jni/android_util_PathParser.cpp +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" #include <PathParser.h> @@ -22,7 +21,6 @@ #include <utils/VectorDrawableUtils.h> #include <android/log.h> -#include "core_jni_helpers.h" namespace android { diff --git a/core/jni/android/graphics/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index bfb9bae45f0c..5714cd1d0390 100644 --- a/core/jni/android/graphics/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -14,18 +14,15 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Minikin" -#include <nativehelper/JNIHelp.h> -#include <core_jni_helpers.h> - #include "SkData.h" #include "SkFontMgr.h" #include "SkRefCnt.h" #include "SkTypeface.h" #include "GraphicsJNI.h" #include <nativehelper/ScopedUtfChars.h> -#include <android_runtime/AndroidRuntime.h> #include "Utils.h" #include "FontUtils.h" @@ -51,14 +48,11 @@ static void releaseFont(jlong font) { } static void release_global_ref(const void* /*data*/, void* context) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - if (env == nullptr) { - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_4; - args.name = "release_font_data"; - args.group = nullptr; - jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); - if (result != JNI_OK) { + JNIEnv* env = GraphicsJNI::getJNIEnv(); + bool needToAttach = (env == nullptr); + if (needToAttach) { + env = GraphicsJNI::attachJNIEnv("release_font_data"); + if (env == nullptr) { ALOGE("failed to attach to thread to release global ref."); return; } diff --git a/core/jni/android/graphics/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index b0d10c356a9b..df619d9f1406 100644 --- a/core/jni/android/graphics/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Minikin" -#include <nativehelper/JNIHelp.h> +#include "graphics_jni_helpers.h" #include <nativehelper/ScopedUtfChars.h> -#include <core_jni_helpers.h> #include "FontUtils.h" diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h new file mode 100644 index 000000000000..b97cc6a10179 --- /dev/null +++ b/libs/hwui/jni/graphics_jni_helpers.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRAPHICS_JNI_HELPERS +#define GRAPHICS_JNI_HELPERS + +#include <log/log.h> +#include <nativehelper/JNIHelp.h> +#include <nativehelper/scoped_local_ref.h> +#include <nativehelper/scoped_utf_chars.h> +#include <string> + +// Host targets (layoutlib) do not differentiate between regular and critical native methods, +// and they need all the JNI methods to have JNIEnv* and jclass/jobject as their first two arguments. +// The following macro allows to have those arguments when compiling for host while omitting them when +// compiling for Android. +#ifdef __ANDROID__ +#define CRITICAL_JNI_PARAMS +#define CRITICAL_JNI_PARAMS_COMMA +#else +#define CRITICAL_JNI_PARAMS JNIEnv*, jclass +#define CRITICAL_JNI_PARAMS_COMMA JNIEnv*, jclass, +#endif + +namespace android { + +// Defines some helpful functions. + +static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { + jclass clazz = env->FindClass(class_name); + LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name); + return clazz; +} + +static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + return res; +} + +static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name); + return res; +} + +static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + return res; +} + +static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name); + return res; +} + +template <typename T> +static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) { + jobject res = env->NewGlobalRef(in); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference."); + return static_cast<T>(res); +} + +static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) { + int res = jniRegisterNativeMethods(env, className, gMethods, numMethods); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + return res; +} + +/** + * Read the specified field from jobject, and convert to std::string. + * If the field cannot be obtained, return defaultValue. + */ +static inline std::string getStringField(JNIEnv* env, jobject obj, jfieldID fieldId, + const char* defaultValue) { + ScopedLocalRef<jstring> strObj(env, jstring(env->GetObjectField(obj, fieldId))); + if (strObj != nullptr) { + ScopedUtfChars chars(env, strObj.get()); + return std::string(chars.c_str()); + } + return std::string(defaultValue); +} + +} // namespace android + +#endif // GRAPHICS_JNI_HELPERS diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/libs/hwui/jni/pdf/PdfDocument.cpp index 5f67d3008f45..d21eb3f6a208 100644 --- a/core/jni/android/graphics/pdf/PdfDocument.cpp +++ b/libs/hwui/jni/pdf/PdfDocument.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include <vector> #include "CreateJavaOutputStreamAdaptor.h" diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index 10c30260d7e3..828d6e3992b6 100644 --- a/core/jni/android/graphics/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#undef LOG_TAG #define LOG_TAG "PdfEditor" #include <sys/types.h> @@ -25,8 +27,7 @@ #include "PdfUtils.h" -#include "jni.h" -#include <nativehelper/JNIHelp.h> +#include "graphics_jni_helpers.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" @@ -38,8 +39,6 @@ #include "SkMatrix.h" -#include <core_jni_helpers.h> - namespace android { enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp index 761830b0e97c..cc1f96197c74 100644 --- a/core/jni/android/graphics/pdf/PdfRenderer.cpp +++ b/libs/hwui/jni/pdf/PdfRenderer.cpp @@ -16,14 +16,11 @@ #include "PdfUtils.h" -#include "jni.h" -#include <nativehelper/JNIHelp.h> #include "GraphicsJNI.h" #include "SkBitmap.h" #include "SkMatrix.h" #include "fpdfview.h" -#include "core_jni_helpers.h" #include <vector> #include <utils/Log.h> #include <unistd.h> diff --git a/core/jni/android/graphics/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp index 36355ebbefc4..06d202828b85 100644 --- a/core/jni/android/graphics/pdf/PdfUtils.cpp +++ b/libs/hwui/jni/pdf/PdfUtils.cpp @@ -21,6 +21,7 @@ #include "fpdfview.h" +#undef LOG_TAG #define LOG_TAG "PdfUtils" #include <utils/Log.h> diff --git a/core/jni/android/graphics/pdf/PdfUtils.h b/libs/hwui/jni/pdf/PdfUtils.h index 65327382e899..65327382e899 100644 --- a/core/jni/android/graphics/pdf/PdfUtils.h +++ b/libs/hwui/jni/pdf/PdfUtils.h diff --git a/libs/hwui/jni/scoped_nullable_primitive_array.h b/libs/hwui/jni/scoped_nullable_primitive_array.h new file mode 100644 index 000000000000..77f4c9d14f07 --- /dev/null +++ b/libs/hwui/jni/scoped_nullable_primitive_array.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCOPED_NULLABLE_PRIMITIVE_ARRAY_H +#define SCOPED_NULLABLE_PRIMITIVE_ARRAY_H + +#include <jni.h> + +namespace android { + +#define ARRAY_TRAITS(ARRAY_TYPE, POINTER_TYPE, NAME) \ +class NAME ## ArrayTraits { \ +public: \ + static constexpr void getArrayRegion(JNIEnv* env, ARRAY_TYPE array, size_t start, \ + size_t len, POINTER_TYPE out) { \ + env->Get ## NAME ## ArrayRegion(array, start, len, out); \ + } \ + \ + static constexpr POINTER_TYPE getArrayElements(JNIEnv* env, ARRAY_TYPE array) { \ + return env->Get ## NAME ## ArrayElements(array, nullptr); \ + } \ + \ + static constexpr void releaseArrayElements(JNIEnv* env, ARRAY_TYPE array, \ + POINTER_TYPE buffer, jint mode) { \ + env->Release ## NAME ## ArrayElements(array, buffer, mode); \ + } \ +}; \ + +ARRAY_TRAITS(jbooleanArray, jboolean*, Boolean) +ARRAY_TRAITS(jbyteArray, jbyte*, Byte) +ARRAY_TRAITS(jcharArray, jchar*, Char) +ARRAY_TRAITS(jdoubleArray, jdouble*, Double) +ARRAY_TRAITS(jfloatArray, jfloat*, Float) +ARRAY_TRAITS(jintArray, jint*, Int) +ARRAY_TRAITS(jlongArray, jlong*, Long) +ARRAY_TRAITS(jshortArray, jshort*, Short) + +#undef ARRAY_TRAITS + +template<typename JavaArrayType, typename PrimitiveType, class Traits, size_t preallocSize = 10> +class ScopedArrayRO { +public: + ScopedArrayRO(JNIEnv* env, JavaArrayType javaArray) : mEnv(env), mJavaArray(javaArray) { + if (mJavaArray == nullptr) { + mSize = 0; + mRawArray = nullptr; + } else { + mSize = mEnv->GetArrayLength(mJavaArray); + if (mSize <= preallocSize) { + Traits::getArrayRegion(mEnv, mJavaArray, 0, mSize, mBuffer); + mRawArray = mBuffer; + } else { + mRawArray = Traits::getArrayElements(mEnv, mJavaArray); + } + } + } + + ~ScopedArrayRO() { + if (mRawArray != nullptr && mRawArray != mBuffer) { + Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT); + } + } + + const PrimitiveType* get() const { return mRawArray; } + const PrimitiveType& operator[](size_t n) const { return mRawArray[n]; } + size_t size() const { return mSize; } + +private: + JNIEnv* const mEnv; + JavaArrayType mJavaArray; + PrimitiveType* mRawArray; + size_t mSize; + PrimitiveType mBuffer[preallocSize]; + DISALLOW_COPY_AND_ASSIGN(ScopedArrayRO); +}; + +// ScopedNullable***ArrayRO provide convenient read-only access to Java array from JNI code. +// These accept nullptr. In that case, get() returns nullptr and size() returns 0. +using ScopedNullableBooleanArrayRO = ScopedArrayRO<jbooleanArray, jboolean, BooleanArrayTraits>; +using ScopedNullableByteArrayRO = ScopedArrayRO<jbyteArray, jbyte, ByteArrayTraits>; +using ScopedNullableCharArrayRO = ScopedArrayRO<jcharArray, jchar, CharArrayTraits>; +using ScopedNullableDoubleArrayRO = ScopedArrayRO<jdoubleArray, jdouble, DoubleArrayTraits>; +using ScopedNullableFloatArrayRO = ScopedArrayRO<jfloatArray, jfloat, FloatArrayTraits>; +using ScopedNullableIntArrayRO = ScopedArrayRO<jintArray, jint, IntArrayTraits>; +using ScopedNullableLongArrayRO = ScopedArrayRO<jlongArray, jlong, LongArrayTraits>; +using ScopedNullableShortArrayRO = ScopedArrayRO<jshortArray, jshort, ShortArrayTraits>; + +} // namespace android + +#endif // SCOPED_NULLABLE_PRIMITIVE_ARRAY_H diff --git a/core/jni/android/graphics/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp index 8dae6558bb0d..69865171a09d 100644 --- a/core/jni/android/graphics/text/LineBreaker.cpp +++ b/libs/hwui/jni/text/LineBreaker.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "LineBreaker" #include "utils/misc.h" #include "utils/Log.h" +#include "graphics_jni_helpers.h" #include <nativehelper/ScopedStringChars.h> #include <nativehelper/ScopedPrimitiveArray.h> -#include <nativehelper/JNIHelp.h> -#include "core_jni_helpers.h" #include "scoped_nullable_primitive_array.h" #include <cstdint> #include <vector> diff --git a/core/jni/android/graphics/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp index 3b5ecbcd1d35..7793746ee285 100644 --- a/core/jni/android/graphics/text/MeasuredText.cpp +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "MeasuredText" #include "GraphicsJNI.h" @@ -21,8 +22,6 @@ #include "utils/Log.h" #include <nativehelper/ScopedStringChars.h> #include <nativehelper/ScopedPrimitiveArray.h> -#include <nativehelper/JNIHelp.h> -#include "core_jni_helpers.h" #include <cstdint> #include <vector> #include <list> diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6bb896fd7b29..88d6033ed9fb 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -23,6 +23,7 @@ cc_library_shared { "libandroid_runtime", "libbinder", "libcutils", + "libhwui", "liblog", "libutils", "libgui", diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index b1e3d6fe845a..213b3adfb2a8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -20,6 +20,7 @@ cc_test { shared_libs: [ "libandroid_runtime", "libinputservice", + "libhwui", "libgui", "libutils", ], diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 19085bff0033..6028a8a90141 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -35,6 +35,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.PropertyInvalidatedCache; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; @@ -84,6 +85,23 @@ import java.util.function.Consumer; @RequiresFeature(PackageManager.FEATURE_LOCATION) public class LocationManager { + @GuardedBy("mLock") + private PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache = + new PropertyInvalidatedCache<Integer, Boolean>( + 4, + CACHE_KEY_LOCATION_ENABLED_PROPERTY) { + @Override + protected Boolean recompute(Integer userHandle) { + try { + return mService.isLocationEnabledForUser(userHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + }; + + private final Object mLock = new Object(); + /** * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a * specific package. @@ -462,6 +480,13 @@ public class LocationManager { */ @SystemApi public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) { + synchronized (mLock) { + if (mLocationEnabledCache != null) { + return mLocationEnabledCache.query(userHandle.getIdentifier()); + } + } + + // fallback if cache is disabled try { return mService.isLocationEnabledForUser(userHandle.getIdentifier()); } catch (RemoteException e) { @@ -3107,6 +3132,29 @@ public class LocationManager { public void onLocationBatch(List<Location> locations) { execute((listener) -> listener.onLocationBatch(locations)); } + + } + } + + /** + * @hide + */ + public static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY = + "cache_key.location_enabled"; + + /** + * @hide + */ + public static void invalidateLocalLocationEnabledCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_LOCATION_ENABLED_PROPERTY); + } + + /** + * @hide + */ + public void disableLocalLocationEnabledCaches() { + synchronized (mLock) { + mLocationEnabledCache = null; } } } diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index 085602cbcd4f..6006d5079b07 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -27,22 +27,6 @@ import android.annotation.NonNull; public abstract class LocationManagerInternal { /** - * Requests that a provider change its allowed state. A provider may or may not honor this - * request, and if the provider does change its state as a result, that may happen - * asynchronously after some delay. - * - * <p>Setting a provider's state to allowed implies that any consents or terms and conditions - * that may be necessary to allow the provider are agreed to. Setting a providers state to - * disallowed implies that any consents or terms and conditions have their agreement revoked. - * - * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} - * @param allowed Whether the location provider is being requested to allow or disallow - * itself - * @throws IllegalArgumentException if provider is null - */ - public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed); - - /** * Returns true if the given provider is enabled for the given user. * * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl index b7817ff1e1fc..4246c6cd1004 100644 --- a/location/java/com/android/internal/location/ILocationProvider.aidl +++ b/location/java/com/android/internal/location/ILocationProvider.aidl @@ -37,6 +37,4 @@ interface ILocationProvider { @UnsupportedAppUsage oneway void sendExtraCommand(String command, in Bundle extras); - - oneway void requestSetAllowed(boolean allowed); } diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index 49fcaabe981a..9cc30d0d751e 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -17,7 +17,6 @@ package com.android.location.provider { method @Deprecated protected int onGetStatus(android.os.Bundle); method @Deprecated protected long onGetStatusUpdateTime(); method protected void onInit(); - method protected void onRequestSetAllowed(boolean); method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle); method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource); method public void reportLocation(android.location.Location); diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index bd29d8ac2a85..d3fb58fe257e 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -315,17 +315,6 @@ public abstract class LocationProviderBase { return false; } - /** - * Invoked when the system wishes to request that the provider sets its allowed state as - * desired. This implies that the caller is providing/retracting consent for any terms and - * conditions or consents associated with the provider. - * - * <p>It is generally only necessary to override this function if the provider has some barriers - * or gates for enabling/disabling itself, in which case this function should handle those - * appropriately. A provider that is always allowed has no need to override this function. - */ - protected void onRequestSetAllowed(boolean allowed) {} - private final class Service extends ILocationProvider.Stub { @Override @@ -356,10 +345,5 @@ public abstract class LocationProviderBase { public void sendExtraCommand(String command, Bundle extras) { onSendExtraCommand(command, extras); } - - @Override - public void requestSetAllowed(boolean allowed) { - onRequestSetAllowed(allowed); - } } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 0b825f6dba54..383202b20550 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1654,6 +1654,12 @@ public class AudioManager { * @hide * Interface to be notified of changes in the preferred audio device set for a given audio * strategy. + * <p>Note that this listener will only be invoked whenever + * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or + * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in + * preferred device. It will not be invoked directly after registration with + * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)} + * to indicate which strategies had preferred devices at the time of registration.</p> * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes) * @see #removePreferredDeviceForStrategy(AudioProductStrategy) * @see #getPreferredDeviceForStrategy(AudioProductStrategy) diff --git a/media/java/android/media/IMediaRoute2ProviderService.aidl b/media/java/android/media/IMediaRoute2ProviderService.aidl index cd0def30fa03..6cd2547dcbcf 100644 --- a/media/java/android/media/IMediaRoute2ProviderService.aidl +++ b/media/java/android/media/IMediaRoute2ProviderService.aidl @@ -29,13 +29,13 @@ oneway interface IMediaRoute2ProviderService { // MediaRoute2ProviderService#MediaRoute2ProviderServiceStub for readability. void setCallback(IMediaRoute2ProviderServiceCallback callback); void updateDiscoveryPreference(in RouteDiscoveryPreference discoveryPreference); - void setRouteVolume(String routeId, int volume); + void setRouteVolume(String routeId, int volume, long requestId); void requestCreateSession(String packageName, String routeId, long requestId, in @nullable Bundle sessionHints); - void selectRoute(String sessionId, String routeId); - void deselectRoute(String sessionId, String routeId); - void transferToRoute(String sessionId, String routeId); - void setSessionVolume(String sessionId, int volume); - void releaseSession(String sessionId); + void selectRoute(String sessionId, String routeId, long requestId); + void deselectRoute(String sessionId, String routeId, long requestId); + void transferToRoute(String sessionId, String routeId, long requestId); + void setSessionVolume(String sessionId, int volume, long requestId); + void releaseSession(String sessionId, long requestId); } diff --git a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl index e35b0c4a1fdd..ab42d75bf14f 100644 --- a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl +++ b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl @@ -31,4 +31,5 @@ oneway interface IMediaRoute2ProviderServiceCallback { void notifySessionCreationFailed(long requestId); void notifySessionUpdated(in RoutingSessionInfo sessionInfo); void notifySessionReleased(in RoutingSessionInfo sessionInfo); + void notifyRequestFailed(long requestId, int reason); } diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index ffad6592e902..a2f9ee906c03 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -30,4 +30,5 @@ oneway interface IMediaRouter2Manager { void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); void notifyRoutesChanged(in List<MediaRoute2Info> routes); + void notifyRequestFailed(int requestId, int reason); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index cbec32386ed0..c7cb07d4ac1c 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -71,16 +71,17 @@ interface IMediaRouterService { void registerManager(IMediaRouter2Manager manager, String packageName); void unregisterManager(IMediaRouter2Manager manager); void setRouteVolumeWithManager(IMediaRouter2Manager manager, in MediaRoute2Info route, - int volume); + int volume, int requestId); void requestCreateSessionWithManager(IMediaRouter2Manager manager, String packageName, in @nullable MediaRoute2Info route, int requestId); void selectRouteWithManager(IMediaRouter2Manager manager, String sessionId, - in MediaRoute2Info route); + in MediaRoute2Info route, int requestId); void deselectRouteWithManager(IMediaRouter2Manager manager, String sessionId, - in MediaRoute2Info route); + in MediaRoute2Info route, int requestId); void transferToRouteWithManager(IMediaRouter2Manager manager, String sessionId, - in MediaRoute2Info route); - void setSessionVolumeWithManager(IMediaRouter2Manager manager, String sessionId, int volume); - void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId); + in MediaRoute2Info route, int requestId); + void setSessionVolumeWithManager(IMediaRouter2Manager manager, String sessionId, int volume, + int requestId); + void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId, int requestId); } diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index abc7e0b7be0e..f2b4db1afdac 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -1959,7 +1959,7 @@ final public class MediaCodec { * If this codec is to be used with {@link LinearBlock} and/or {@link * GraphicBlock}, pass this flag. * <p> - * When this flag is set, the following APIs throw IllegalStateException. + * When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}. * <ul> * <li>{@link #getInputBuffer} * <li>{@link #getInputImage} @@ -1986,6 +1986,12 @@ final public class MediaCodec { public @interface ConfigureFlag {} /** + * Thrown when the codec is configured for block model and an incompatible API is called. + */ + public class IncompatibleWithBlockModelException extends RuntimeException { + } + + /** * Configures a component. * * @param format The format of the input data (decoder) or the desired @@ -2526,7 +2532,7 @@ final public class MediaCodec { throws CryptoException { synchronized(mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.remove(index); @@ -2778,7 +2784,7 @@ final public class MediaCodec { int flags) throws CryptoException { synchronized(mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.remove(index); @@ -2813,7 +2819,7 @@ final public class MediaCodec { public final int dequeueInputBuffer(long timeoutUs) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } int res = native_dequeueInputBuffer(timeoutUs); @@ -2848,7 +2854,7 @@ final public class MediaCodec { public boolean isMappable() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is invalid"); } return mMappable; } @@ -2867,10 +2873,10 @@ final public class MediaCodec { public @NonNull ByteBuffer map() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is invalid"); } if (!mMappable) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is not mappable"); } if (mMapped == null) { mMapped = native_map(); @@ -2896,7 +2902,7 @@ final public class MediaCodec { public void recycle() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is invalid"); } if (mMapped != null) { mMapped.setAccessible(false); @@ -3002,7 +3008,7 @@ final public class MediaCodec { public boolean isMappable() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is invalid"); } return mMappable; } @@ -3021,10 +3027,10 @@ final public class MediaCodec { public @NonNull Image map() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is invalid"); } if (!mMappable) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is not mappable"); } if (mMapped == null) { mMapped = native_map(); @@ -3050,7 +3056,7 @@ final public class MediaCodec { public void recycle() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is invalid"); } if (mMapped != null) { mMapped.close(); @@ -3127,8 +3133,9 @@ final public class MediaCodec { if (buffer == null) { buffer = new GraphicBlock(); } - if (width < 0 || height < 0) { - throw new IllegalArgumentException(); + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException( + "non-positive width or height: " + width + "x" + height); } synchronized (buffer.mLock) { buffer.native_obtain(width, height, format, usage, codecNames); @@ -3177,16 +3184,8 @@ final public class MediaCodec { * @param block The linear block object * @param offset The byte offset into the input buffer at which the data starts. * @param size The number of bytes of valid input data. - * @param presentationTimeUs The presentation timestamp in microseconds for this - * buffer. This is normally the media time at which this - * buffer should be presented (rendered). When using an output - * surface, this will be propagated as the {@link - * SurfaceTexture#getTimestamp timestamp} for the frame (after - * conversion to nanoseconds). - * @param flags A bitmask of flags - * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. - * While not prohibited, most codecs do not use the - * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @param cryptoInfo Metadata describing the structure of the encrypted input sample. + * may be null if clear. * @return this object * @throws IllegalStateException if a buffer is already set */ @@ -3194,106 +3193,91 @@ final public class MediaCodec { @NonNull LinearBlock block, int offset, int size, - long presentationTimeUs, - @BufferFlag int flags) { + @Nullable MediaCodec.CryptoInfo cryptoInfo) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } if (mLinearBlock != null || mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("Cannot set block twice"); } mLinearBlock = block; mOffset = offset; mSize = size; - mPresentationTimeUs = presentationTimeUs; - mFlags = flags; + mCryptoInfo = cryptoInfo; return this; } /** - * Set an encrypted linear block to this queue request. Exactly one - * buffer must be set for a queue request before calling {@link #queue}. + * Set a graphic block to this queue request. Exactly one buffer must + * be set for a queue request before calling {@link #queue}. * - * @param block The linear block object - * @param offset The byte offset into the input buffer at which the data starts. - * @param presentationTimeUs The presentation timestamp in microseconds for this - * buffer. This is normally the media time at which this - * buffer should be presented (rendered). When using an output - * surface, this will be propagated as the {@link - * SurfaceTexture#getTimestamp timestamp} for the frame (after - * conversion to nanoseconds). - * @param cryptoInfo Metadata describing the structure of the encrypted input sample. - * @param flags A bitmask of flags - * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. - * While not prohibited, most codecs do not use the - * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @param block The graphic block object * @return this object * @throws IllegalStateException if a buffer is already set */ - public @NonNull QueueRequest setEncryptedLinearBlock( - @NonNull LinearBlock block, - int offset, - @NonNull MediaCodec.CryptoInfo cryptoInfo, - long presentationTimeUs, - @BufferFlag int flags) { + public @NonNull QueueRequest setGraphicBlock( + @NonNull GraphicBlock block) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } if (mLinearBlock != null || mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("Cannot set block twice"); } - mLinearBlock = block; - mOffset = offset; - mCryptoInfo = cryptoInfo; - mPresentationTimeUs = presentationTimeUs; - mFlags = flags; + mGraphicBlock = block; return this; } /** - * Set a graphic block to this queue request. Exactly one buffer must - * be set for a queue request before calling {@link #queue}. + * Set timestamp to this queue request. * - * @param block The graphic block object * @param presentationTimeUs The presentation timestamp in microseconds for this * buffer. This is normally the media time at which this * buffer should be presented (rendered). When using an output * surface, this will be propagated as the {@link * SurfaceTexture#getTimestamp timestamp} for the frame (after * conversion to nanoseconds). + * @return this object + */ + public @NonNull QueueRequest setPresentationTimeUs(long presentationTimeUs) { + if (!isAccessible()) { + throw new IllegalStateException("The request is stale"); + } + mPresentationTimeUs = presentationTimeUs; + return this; + } + + /** + * Set flags to this queue request. + * * @param flags A bitmask of flags * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. * While not prohibited, most codecs do not use the * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. * @return this object - * @throws IllegalStateException if a buffer is already set */ - public @NonNull QueueRequest setGraphicBlock( - @NonNull GraphicBlock block, - long presentationTimeUs, - @BufferFlag int flags) { + public @NonNull QueueRequest setFlags(@BufferFlag int flags) { if (!isAccessible()) { - throw new IllegalStateException(); - } - if (mLinearBlock != null || mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } - mGraphicBlock = block; - mPresentationTimeUs = presentationTimeUs; mFlags = flags; return this; } /** - * Add a integer parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add an integer parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type int, that can also be set with {@link MediaFormat#setInteger}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setIntegerParameter( @NonNull String key, int value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(Integer.valueOf(value)); @@ -3301,15 +3285,20 @@ final public class MediaCodec { } /** - * Add a long parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a long parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type long, that can also be set with {@link MediaFormat#setLong}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setLongParameter( @NonNull String key, long value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(Long.valueOf(value)); @@ -3317,15 +3306,20 @@ final public class MediaCodec { } /** - * Add a float parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a float parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type float, that can also be set with {@link MediaFormat#setFloat}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setFloatParameter( @NonNull String key, float value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(Float.valueOf(value)); @@ -3333,15 +3327,20 @@ final public class MediaCodec { } /** - * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a {@link ByteBuffer} parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of byte buffer, that can also be set with {@link MediaFormat#setByteBuffer}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setByteBufferParameter( @NonNull String key, @NonNull ByteBuffer value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(value); @@ -3349,15 +3348,20 @@ final public class MediaCodec { } /** - * Add a string parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a string parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type string, that can also be set with {@link MediaFormat#setString}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setStringParameter( @NonNull String key, @NonNull String value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(value); @@ -3369,10 +3373,10 @@ final public class MediaCodec { */ public void queue() { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } if (mLinearBlock == null && mGraphicBlock == null) { - throw new IllegalStateException(); + throw new IllegalStateException("No block is set"); } setAccessible(false); if (mLinearBlock != null) { @@ -3447,7 +3451,7 @@ final public class MediaCodec { private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>(); /** - * Return a clear {@link QueueRequest} object for an input slot index. + * Return a {@link QueueRequest} object for an input slot index. * * @param index input slot index from * {@link Callback#onInputBufferAvailable} @@ -3459,17 +3463,19 @@ final public class MediaCodec { public @NonNull QueueRequest getQueueRequest(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_BLOCK) { - throw new IllegalStateException(); + throw new IllegalStateException("The codec is not configured for block model"); } if (index < 0 || index >= mQueueRequests.size()) { - throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException("Expected range of index: [0," + + (mQueueRequests.size() - 1) + "]; actual: " + index); } QueueRequest request = mQueueRequests.get(index); if (request == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unavailable index: " + index); } if (!request.isAccessible()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The request is stale at index " + index); } return request.clear(); } @@ -3529,7 +3535,7 @@ final public class MediaCodec { @NonNull BufferInfo info, long timeoutUs) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } int res = native_dequeueOutputBuffer(info, timeoutUs); @@ -3644,7 +3650,8 @@ final public class MediaCodec { frame.clear(); break; default: - throw new IllegalStateException(); + throw new IllegalStateException( + "Unrecognized buffer mode: " + mBufferMode); } } releaseOutputBuffer( @@ -3910,7 +3917,7 @@ final public class MediaCodec { public ByteBuffer[] getInputBuffers() { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } if (mCachedInputBuffers == null) { throw new IllegalStateException(); @@ -3946,7 +3953,7 @@ final public class MediaCodec { public ByteBuffer[] getOutputBuffers() { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } if (mCachedOutputBuffers == null) { throw new IllegalStateException(); @@ -3978,7 +3985,7 @@ final public class MediaCodec { public ByteBuffer getInputBuffer(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } ByteBuffer newBuffer = getBuffer(true /* input */, index); @@ -4012,7 +4019,7 @@ final public class MediaCodec { public Image getInputImage(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } Image newImage = getImage(true /* input */, index); @@ -4046,7 +4053,7 @@ final public class MediaCodec { public ByteBuffer getOutputBuffer(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } ByteBuffer newBuffer = getBuffer(false /* input */, index); @@ -4079,7 +4086,7 @@ final public class MediaCodec { public Image getOutputImage(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } Image newImage = getImage(false /* input */, index); @@ -4106,7 +4113,7 @@ final public class MediaCodec { */ public @Nullable LinearBlock getLinearBlock() { if (mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("This output frame is not linear"); } return mLinearBlock; } @@ -4118,7 +4125,7 @@ final public class MediaCodec { */ public @Nullable GraphicBlock getGraphicBlock() { if (mLinearBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("This output frame is not graphic"); } return mGraphicBlock; } @@ -4139,7 +4146,7 @@ final public class MediaCodec { /** * Returns a read-only {@link MediaFormat} for this frame. The returned - * object is valid only while the client is holding the output frame. + * object is valid only until the client calls {@link MediaCodec#releaseOutputBuffer}. */ public @NonNull MediaFormat getFormat() { return mFormat; @@ -4151,7 +4158,7 @@ final public class MediaCodec { * Client can find out what the change is by querying {@link MediaFormat} * object returned from {@link #getFormat}. */ - public void getChangedKeys(@NonNull Set<String> keys) { + public void retrieveChangedKeys(@NonNull Set<String> keys) { keys.clear(); keys.addAll(mChangedKeys); } @@ -4211,17 +4218,19 @@ final public class MediaCodec { public @NonNull OutputFrame getOutputFrame(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_BLOCK) { - throw new IllegalStateException(); + throw new IllegalStateException("The codec is not configured for block model"); } if (index < 0 || index >= mOutputFrames.size()) { - throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException("Expected range of index: [0," + + (mQueueRequests.size() - 1) + "]; actual: " + index); } OutputFrame frame = mOutputFrames.get(index); if (frame == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unavailable index: " + index); } if (!frame.isAccessible()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The output frame is stale at index " + index); } if (!frame.isLoaded()) { native_getOutputFrame(frame, index); diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index bf04fe88f9e4..8d63cf04da6d 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -104,10 +104,12 @@ public final class MediaRoute2Info implements Parcelable { /** @hide */ @IntDef({ - DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_REMOTE_TV, - DEVICE_TYPE_REMOTE_SPEAKER, DEVICE_TYPE_BLUETOOTH}) + TYPE_UNKNOWN, TYPE_BUILTIN_SPEAKER, TYPE_WIRED_HEADSET, + TYPE_WIRED_HEADPHONES, TYPE_BLUETOOTH_A2DP, TYPE_HDMI, TYPE_USB_DEVICE, + TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID, + TYPE_REMOTE_TV, TYPE_REMOTE_SPEAKER, TYPE_GROUP}) @Retention(RetentionPolicy.SOURCE) - public @interface DeviceType {} + public @interface Type {} /** * The default receiver device type of the route indicating the type is unknown. @@ -140,6 +142,121 @@ public final class MediaRoute2Info implements Parcelable { */ public static final int DEVICE_TYPE_BLUETOOTH = 3; + + /** + * The default route type indicating the type is unknown. + * + * @see #getType + * @hide + */ + public static final int TYPE_UNKNOWN = 0; + + /** + * A route type describing the speaker system (i.e. a mono speaker or stereo speakers) built + * in a device. + * + * @see #getType + * @hide + */ + public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; + + /** + * A route type describing a headset, which is the combination of a headphones and microphone. + * + * @see #getType + * @hide + */ + public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET; + + /** + * A route type describing a pair of wired headphones. + * + * @see #getType + * @hide + */ + public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES; + + /** + * A route type indicating the presentation of the media is happening + * on a bluetooth device such as a bluetooth speaker. + * + * @see #getType + * @hide + */ + public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; + + /** + * A route type describing an HDMI connection. + * + * @see #getType + * @hide + */ + public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI; + + /** + * A route type describing a USB audio device. + * + * @see #getType + * @hide + */ + public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE; + + /** + * A route type describing a USB audio device in accessory mode. + * + * @see #getType + * @hide + */ + public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY; + + /** + * A route type describing the audio device associated with a dock. + * + * @see #getType + * @hide + */ + public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK; + + /** + * A device type describing a USB audio headset. + * + * @see #getType + * @hide + */ + public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET; + + /** + * A route type describing a Hearing Aid. + * + * @see #getType + * @hide + */ + public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID; + + /** + * A route type indicating the presentation of the media is happening on a TV. + * + * @see #getType + * @hide + */ + public static final int TYPE_REMOTE_TV = 1001; + + /** + * A route type indicating the presentation of the media is happening on a speaker. + * + * @see #getType + * @hide + */ + public static final int TYPE_REMOTE_SPEAKER = 1002; + + /** + * A route type indicating the presentation of the media is happening on multiple devices. + * + * @see #getType + * @hide + */ + public static final int TYPE_GROUP = 2000; + /** * Media feature: Live audio. * <p> @@ -196,8 +313,8 @@ public final class MediaRoute2Info implements Parcelable { final String mId; final CharSequence mName; final List<String> mFeatures; - @DeviceType - final int mDeviceType; + @Type + final int mType; final boolean mIsSystem; final Uri mIconUri; final CharSequence mDescription; @@ -214,7 +331,7 @@ public final class MediaRoute2Info implements Parcelable { mId = builder.mId; mName = builder.mName; mFeatures = builder.mFeatures; - mDeviceType = builder.mDeviceType; + mType = builder.mType; mIsSystem = builder.mIsSystem; mIconUri = builder.mIconUri; mDescription = builder.mDescription; @@ -231,7 +348,7 @@ public final class MediaRoute2Info implements Parcelable { mId = in.readString(); mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mFeatures = in.createStringArrayList(); - mDeviceType = in.readInt(); + mType = in.readInt(); mIsSystem = in.readBoolean(); mIconUri = in.readParcelable(null); mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); @@ -285,9 +402,26 @@ public final class MediaRoute2Info implements Parcelable { * {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER}, * {@link #DEVICE_TYPE_BLUETOOTH}. */ - @DeviceType + @Type public int getDeviceType() { - return mDeviceType; + return getType(); + } + + /** + * Gets the type of this route. + * + * @return The type of this route: + * {@link #TYPE_UNKNOWN}, + * {@link #TYPE_BUILTIN_SPEAKER}, {@link #TYPE_WIRED_HEADSET}, {@link #TYPE_WIRED_HEADPHONES}, + * {@link #TYPE_BLUETOOTH_A2DP}, {@link #TYPE_HDMI}, {@link #TYPE_DOCK}, + * {@Link #TYPE_USB_DEVICE}, {@link #TYPE_USB_ACCESSORY}, {@link #TYPE_USB_HEADSET} + * {@link #TYPE_HEARING_AID}, + * {@link #TYPE_REMOTE_TV}, {@link #TYPE_REMOTE_SPEAKER}, {@link #TYPE_GROUP}. + * @hide + */ + @Type + public int getType() { + return mType; } /** @@ -437,7 +571,7 @@ public final class MediaRoute2Info implements Parcelable { return Objects.equals(mId, other.mId) && Objects.equals(mName, other.mName) && Objects.equals(mFeatures, other.mFeatures) - && (mDeviceType == other.mDeviceType) + && (mType == other.mType) && (mIsSystem == other.mIsSystem) && Objects.equals(mIconUri, other.mIconUri) && Objects.equals(mDescription, other.mDescription) @@ -452,7 +586,7 @@ public final class MediaRoute2Info implements Parcelable { @Override public int hashCode() { // Note: mExtras is not included. - return Objects.hash(mId, mName, mFeatures, mDeviceType, mIsSystem, mIconUri, mDescription, + return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription, mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume, mProviderId); } @@ -488,7 +622,7 @@ public final class MediaRoute2Info implements Parcelable { dest.writeString(mId); TextUtils.writeToParcel(mName, dest, flags); dest.writeStringList(mFeatures); - dest.writeInt(mDeviceType); + dest.writeInt(mType); dest.writeBoolean(mIsSystem); dest.writeParcelable(mIconUri, flags); TextUtils.writeToParcel(mDescription, dest, flags); @@ -509,8 +643,8 @@ public final class MediaRoute2Info implements Parcelable { final CharSequence mName; final List<String> mFeatures; - @DeviceType - int mDeviceType = DEVICE_TYPE_UNKNOWN; + @Type + int mType = TYPE_UNKNOWN; boolean mIsSystem; Uri mIconUri; CharSequence mDescription; @@ -557,7 +691,7 @@ public final class MediaRoute2Info implements Parcelable { mId = routeInfo.mId; mName = routeInfo.mName; mFeatures = new ArrayList<>(routeInfo.mFeatures); - mDeviceType = routeInfo.mDeviceType; + mType = routeInfo.mType; mIsSystem = routeInfo.mIsSystem; mIconUri = routeInfo.mIconUri; mDescription = routeInfo.mDescription; @@ -621,8 +755,17 @@ public final class MediaRoute2Info implements Parcelable { * Sets the route's device type. */ @NonNull - public Builder setDeviceType(@DeviceType int deviceType) { - mDeviceType = deviceType; + public Builder setDeviceType(@Type int type) { + return setType(type); + } + + /** + * Sets the route's type. + * @hide + */ + @NonNull + public Builder setType(@Type int type) { + mType = type; return this; } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 38233fd06745..aa0eda1fdbb1 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -19,6 +19,7 @@ package android.media; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; @@ -37,6 +38,8 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -51,7 +54,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * Media apps which use {@link MediaRouter2} can request to play their media on the routes. * </p><p> * When {@link MediaRouter2 media router} wants to play media on a route, - * {@link #onCreateSession(String, String, long, Bundle)} will be called to handle the request. + * {@link #onCreateSession(long, String, String, Bundle)} will be called to handle the request. * A session can be considered as a group of currently selected routes for each connection. * Create and manage the sessions by yourself, and notify the {@link RoutingSessionInfo * session infos} when there are any changes. @@ -61,6 +64,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * a {@link MediaRouter2 media router} by an application. See * {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} for the details. * </p> + * Use {@link #notifyRequestFailed(long, int)} to notify the failure with previously received + * request ID. */ public abstract class MediaRoute2ProviderService extends Service { private static final String TAG = "MR2ProviderService"; @@ -79,7 +84,53 @@ public abstract class MediaRoute2ProviderService extends Service { * * @see #notifySessionCreated(RoutingSessionInfo, long) */ - public static final long REQUEST_ID_UNKNOWN = 0; + public static final long REQUEST_ID_NONE = 0; + + /** + * The request has failed due to unknown reason. + * + * @see #notifyRequestFailed(long, int) + */ + public static final int REASON_UNKNOWN_ERROR = 0; + + /** + * The request has failed since this service rejected the request. + * + * @see #notifyRequestFailed(long, int) + */ + public static final int REASON_REJECTED = 1; + + /** + * The request has failed due to a network error. + * + * @see #notifyRequestFailed(long, int) + */ + public static final int REASON_NETWORK_ERROR = 2; + + /** + * The request has failed since the requested route is no longer available. + * + * @see #notifyRequestFailed(long, int) + */ + public static final int REASON_ROUTE_NOT_AVAILABLE = 3; + + /** + * The request has failed since the request is not valid. For example, selecting a route + * which is not selectable. + * + * @see #notifyRequestFailed(long, int) + */ + public static final int REASON_INVALID_COMMAND = 4; + + /** + * @hide + */ + @IntDef(prefix = "REASON_", value = { + REASON_UNKNOWN_ERROR, REASON_REJECTED, REASON_NETWORK_ERROR, REASON_ROUTE_NOT_AVAILABLE, + REASON_INVALID_COMMAND + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Reason {} private final Handler mHandler; private final Object mSessionLock = new Object(); @@ -116,20 +167,23 @@ public abstract class MediaRoute2ProviderService extends Service { /** * Called when a volume setting is requested on a route of the provider * + * @param requestId the id of this request * @param routeId the id of the route * @param volume the target volume - * @see MediaRoute2Info#getVolumeMax() + * @see MediaRoute2Info.Builder#setVolume(int) */ - public abstract void onSetRouteVolume(@NonNull String routeId, int volume); + public abstract void onSetRouteVolume(long requestId, @NonNull String routeId, int volume); /** * Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on * a routing session of the provider * + * @param requestId the id of this request * @param sessionId the id of the routing session * @param volume the target volume + * @see RoutingSessionInfo.Builder#setVolume(int) */ - public abstract void onSetSessionVolume(@NonNull String sessionId, int volume); + public abstract void onSetSessionVolume(long requestId, @NonNull String sessionId, int volume); /** * Gets information of the session with the given id. @@ -161,14 +215,15 @@ public abstract class MediaRoute2ProviderService extends Service { /** * Notifies clients of that the session is created and ready for use. * <p> - * If this session is created without any creation request, use {@link #REQUEST_ID_UNKNOWN} + * If this session is created without any creation request, use {@link #REQUEST_ID_NONE} * as the request ID. * * @param sessionInfo information of the new session. * The {@link RoutingSessionInfo#getId() id} of the session must be unique. * @param requestId id of the previous request to create this session provided in - * {@link #onCreateSession(String, String, long, Bundle)} - * @see #onCreateSession(String, String, long, Bundle) + * {@link #onCreateSession(long, String, String, Bundle)}. Can be + * {@link #REQUEST_ID_NONE} if this session is created without any request. + * @see #onCreateSession(long, String, String, Bundle) * @see #getSessionInfo(String) */ public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo, @@ -201,8 +256,8 @@ public abstract class MediaRoute2ProviderService extends Service { * Notifies clients of that the session could not be created. * * @param requestId id of the previous request to create the session provided in - * {@link #onCreateSession(String, String, long, Bundle)}. - * @see #onCreateSession(String, String, long, Bundle) + * {@link #onCreateSession(long, String, String, Bundle)}. + * @see #onCreateSession(long, String, String, Bundle) */ public final void notifySessionCreationFailed(long requestId) { if (mRemoteCallback == null) { @@ -246,7 +301,7 @@ public abstract class MediaRoute2ProviderService extends Service { * Notifies that the session is released. * * @param sessionId id of the released session. - * @see #onReleaseSession(String) + * @see #onReleaseSession(long, String) */ public final void notifySessionReleased(@NonNull String sessionId) { if (TextUtils.isEmpty(sessionId)) { @@ -273,6 +328,29 @@ public abstract class MediaRoute2ProviderService extends Service { } /** + * Notifies to the client that the request has failed. + * + * @param requestId the ID of the previous request + * @param reason the reason why the request has failed + * + * @see #REASON_UNKNOWN_ERROR + * @see #REASON_REJECTED + * @see #REASON_NETWORK_ERROR + * @see #REASON_ROUTE_NOT_AVAILABLE + * @see #REASON_INVALID_COMMAND + */ + public final void notifyRequestFailed(long requestId, @Reason int reason) { + if (mRemoteCallback == null) { + return; + } + try { + mRemoteCallback.notifyRequestFailed(requestId, reason); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to notify that the request has failed."); + } + } + + /** * Called when the service receives a request to create a session. * <p> * You should create and maintain your own session and notifies the client of @@ -288,9 +366,9 @@ public abstract class MediaRoute2ProviderService extends Service { * If you can't create the session or want to reject the request, call * {@link #notifySessionCreationFailed(long)} with the given {@code requestId}. * + * @param requestId the id of this request * @param packageName the package name of the application that selected the route * @param routeId the id of the route initially being connected - * @param requestId the id of this session creation request * @param sessionHints an optional bundle of app-specific arguments sent by * {@link MediaRouter2}, or null if none. The contents of this bundle * may affect the result of session creation. @@ -299,8 +377,8 @@ public abstract class MediaRoute2ProviderService extends Service { * @see RoutingSessionInfo.Builder#addSelectedRoute(String) * @see RoutingSessionInfo.Builder#setControlHints(Bundle) */ - public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId, - long requestId, @Nullable Bundle sessionHints); + public abstract void onCreateSession(long requestId, @NonNull String packageName, + @NonNull String routeId, @Nullable Bundle sessionHints); /** * Called when the session should be released. A client of the session or system can request @@ -312,44 +390,48 @@ public abstract class MediaRoute2ProviderService extends Service { * Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger * this method to be called. * + * @param requestId the id of this request * @param sessionId id of the session being released. * @see #notifySessionReleased(String) * @see #getSessionInfo(String) */ - public abstract void onReleaseSession(@NonNull String sessionId); + public abstract void onReleaseSession(long requestId, @NonNull String sessionId); - //TODO: make a way to reject the request /** * Called when a client requests selecting a route for the session. * After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * + * @param requestId the id of this request * @param sessionId id of the session * @param routeId id of the route */ - public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId); + public abstract void onSelectRoute(long requestId, @NonNull String sessionId, + @NonNull String routeId); - //TODO: make a way to reject the request /** * Called when a client requests deselecting a route from the session. * After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * + * @param requestId the id of this request * @param sessionId id of the session * @param routeId id of the route */ - public abstract void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId); + public abstract void onDeselectRoute(long requestId, @NonNull String sessionId, + @NonNull String routeId); - //TODO: make a way to reject the request /** * Called when a client requests transferring a session to a route. * After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * + * @param requestId the id of this request * @param sessionId id of the session * @param routeId id of the route */ - public abstract void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId); + public abstract void onTransferToRoute(long requestId, @NonNull String sessionId, + @NonNull String routeId); /** * Called when the {@link RouteDiscoveryPreference discovery preference} has changed. @@ -439,12 +521,12 @@ public abstract class MediaRoute2ProviderService extends Service { } @Override - public void setRouteVolume(String routeId, int volume) { + public void setRouteVolume(String routeId, int volume, long requestId) { if (!checkCallerisSystem()) { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume, - MediaRoute2ProviderService.this, routeId, volume)); + MediaRoute2ProviderService.this, requestId, routeId, volume)); } @Override @@ -454,12 +536,12 @@ public abstract class MediaRoute2ProviderService extends Service { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession, - MediaRoute2ProviderService.this, packageName, routeId, requestId, + MediaRoute2ProviderService.this, requestId, packageName, routeId, requestCreateSession)); } @Override - public void selectRoute(@NonNull String sessionId, String routeId) { + public void selectRoute(String sessionId, String routeId, long requestId) { if (!checkCallerisSystem()) { return; } @@ -468,11 +550,11 @@ public abstract class MediaRoute2ProviderService extends Service { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute, - MediaRoute2ProviderService.this, sessionId, routeId)); + MediaRoute2ProviderService.this, requestId, sessionId, routeId)); } @Override - public void deselectRoute(@NonNull String sessionId, String routeId) { + public void deselectRoute(String sessionId, String routeId, long requestId) { if (!checkCallerisSystem()) { return; } @@ -481,11 +563,11 @@ public abstract class MediaRoute2ProviderService extends Service { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute, - MediaRoute2ProviderService.this, sessionId, routeId)); + MediaRoute2ProviderService.this, requestId, sessionId, routeId)); } @Override - public void transferToRoute(@NonNull String sessionId, String routeId) { + public void transferToRoute(String sessionId, String routeId, long requestId) { if (!checkCallerisSystem()) { return; } @@ -494,20 +576,20 @@ public abstract class MediaRoute2ProviderService extends Service { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute, - MediaRoute2ProviderService.this, sessionId, routeId)); + MediaRoute2ProviderService.this, requestId, sessionId, routeId)); } @Override - public void setSessionVolume(String sessionId, int volume) { + public void setSessionVolume(String sessionId, int volume, long requestId) { if (!checkCallerisSystem()) { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume, - MediaRoute2ProviderService.this, sessionId, volume)); + MediaRoute2ProviderService.this, requestId, sessionId, volume)); } @Override - public void releaseSession(@NonNull String sessionId) { + public void releaseSession(String sessionId, long requestId) { if (!checkCallerisSystem()) { return; } @@ -516,7 +598,7 @@ public abstract class MediaRoute2ProviderService extends Service { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession, - MediaRoute2ProviderService.this, sessionId)); + MediaRoute2ProviderService.this, requestId, sessionId)); } } } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 5942a3d05e67..34109194acba 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -817,7 +817,7 @@ public class MediaRouter2 { * @return An optional bundle of app-specific arguments to send to the provider, * or null if none. The contents of this bundle may affect the result of * controller creation. - * @see MediaRoute2ProviderService#onCreateSession(String, String, long, Bundle) + * @see MediaRoute2ProviderService#onCreateSession(long, String, String, Bundle) */ @Nullable Bundle onGetControllerHints(@NonNull MediaRoute2Info route); @@ -976,8 +976,12 @@ public class MediaRouter2 { } /** - * Selects a route for the remote session. The given route must satisfy all of the - * following conditions: + * Selects a route for the remote session. After a route is selected, the media is expected + * to be played to the all the selected routes. This is different from {@link + * MediaRouter2#transferTo(MediaRoute2Info)} transferring to a route}, + * where the media is expected to 'move' from one route to another. + * <p> + * The given route must satisfy all of the following conditions: * <ul> * <li>ID should not be included in {@link #getSelectedRoutes()}</li> * <li>ID should be included in {@link #getSelectableRoutes()}</li> @@ -1024,8 +1028,10 @@ public class MediaRouter2 { } /** - * Deselects a route from the remote session. The given route must satisfy all of the - * following conditions: + * Deselects a route from the remote session. After a route is deselected, the media is + * expected to be stopped on the deselected routes. + * <p> + * The given route must satisfy all of the following conditions: * <ul> * <li>ID should be included in {@link #getSelectedRoutes()}</li> * <li>ID should be included in {@link #getDeselectableRoutes()}</li> diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 636ee92f68a8..ff2c863aa7b0 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -337,7 +337,8 @@ public class MediaRouter2Manager { } if (client != null) { try { - mMediaRouterService.setRouteVolumeWithManager(client, route, volume); + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setRouteVolumeWithManager(client, route, volume, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to send control request.", ex); } @@ -368,8 +369,9 @@ public class MediaRouter2Manager { } if (client != null) { try { + int requestId = mNextRequestId.getAndIncrement(); mMediaRouterService.setSessionVolumeWithManager( - client, sessionInfo.getId(), volume); + client, sessionInfo.getId(), volume, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to send control request.", ex); } @@ -443,6 +445,12 @@ public class MediaRouter2Manager { } } + void notifyRequestFailed(int reason) { + for (CallbackRecord record : mCallbackRecords) { + record.mExecutor.execute(() -> record.mCallback.onRequestFailed(reason)); + } + } + void updatePreferredFeatures(String packageName, List<String> preferredFeatures) { List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures); if ((prevFeatures == null && preferredFeatures.size() == 0) @@ -593,7 +601,9 @@ public class MediaRouter2Manager { } if (client != null) { try { - mMediaRouterService.selectRouteWithManager(mClient, getSessionId(), route); + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.selectRouteWithManager( + mClient, getSessionId(), route, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to select route for session.", ex); } @@ -635,7 +645,9 @@ public class MediaRouter2Manager { } if (client != null) { try { - mMediaRouterService.deselectRouteWithManager(mClient, getSessionId(), route); + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.deselectRouteWithManager( + mClient, getSessionId(), route, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to remove route from session.", ex); } @@ -678,7 +690,9 @@ public class MediaRouter2Manager { } if (client != null) { try { - mMediaRouterService.transferToRouteWithManager(mClient, getSessionId(), route); + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.transferToRouteWithManager( + mClient, getSessionId(), route, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to transfer to route for session.", ex); } @@ -696,7 +710,9 @@ public class MediaRouter2Manager { } if (client != null) { try { - mMediaRouterService.releaseSessionWithManager(mClient, getSessionId()); + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.releaseSessionWithManager( + mClient, getSessionId(), requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to notify of controller release", ex); } @@ -783,6 +799,17 @@ public class MediaRouter2Manager { public void onPreferredFeaturesChanged(@NonNull String packageName, @NonNull List<String> preferredFeatures) {} + /** + * Called when a previous request has failed. + * + * @param reason the reason that the request has failed. Can be one of followings: + * {@link MediaRoute2ProviderService#REASON_UNKNOWN_ERROR}, + * {@link MediaRoute2ProviderService#REASON_REJECTED}, + * {@link MediaRoute2ProviderService#REASON_NETWORK_ERROR}, + * {@link MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE}, + * {@link MediaRoute2ProviderService#REASON_INVALID_COMMAND}, + */ + public void onRequestFailed(int reason) {} } final class CallbackRecord { @@ -826,6 +853,13 @@ public class MediaRouter2Manager { } @Override + public void notifyRequestFailed(int requestId, int reason) { + // Note: requestId is not used. + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRequestFailed, + MediaRouter2Manager.this, reason)); + } + + @Override public void notifyPreferredFeaturesChanged(String packageName, List<String> features) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures, MediaRouter2Manager.this, packageName, features)); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index e80562b3fd1c..bcff6a1df918 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -19,6 +19,8 @@ package com.android.mediaroutertest; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED; import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; +import static android.media.MediaRoute2ProviderService.REASON_REJECTED; +import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE; import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SAMPLE; import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SPECIAL; @@ -32,6 +34,7 @@ import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.VOLUME_MAX; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -55,7 +58,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -69,6 +71,7 @@ import java.util.function.Predicate; @SmallTest public class MediaRouter2ManagerTest { private static final String TAG = "MediaRouter2ManagerTest"; + private static final int WAIT_TIME_MS = 2000; private static final int TIMEOUT_MS = 5000; private Context mContext; @@ -111,6 +114,11 @@ public class MediaRouter2ManagerTest { releaseAllSessions(); // unregister callbacks clearCallbacks(); + + SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance(); + if (instance != null) { + instance.setProxy(null); + } } @Test @@ -296,7 +304,7 @@ public class MediaRouter2ManagerTest { String selectedSystemRouteId = MediaRouter2Utils.getOriginalId( mManager.getActiveSessions().get(0).getSelectedRoutes().get(0)); - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(Collections.emptyList()); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); MediaRoute2Info volRoute = routes.get(selectedSystemRouteId); assertNotNull(volRoute); @@ -398,6 +406,53 @@ public class MediaRouter2ManagerTest { } } + /** + * Tests that {@link android.media.MediaRoute2ProviderService#notifyRequestFailed(long, int)} + * should invoke the callback only when the right requestId is used. + */ + @Test + public void testOnRequestFailedCalledForProperRequestId() throws Exception { + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); + MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); + + SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance(); + assertNotNull(instance); + + final List<Long> requestIds = new ArrayList<>(); + final CountDownLatch onSetRouteVolumeLatch = new CountDownLatch(1); + instance.setProxy(new SampleMediaRoute2ProviderService.Proxy() { + @Override + public void onSetRouteVolume(String routeId, int volume, long requestId) { + requestIds.add(requestId); + onSetRouteVolumeLatch.countDown(); + } + }); + + addManagerCallback(new MediaRouter2Manager.Callback() {}); + mManager.setRouteVolume(volRoute, 0); + assertTrue(onSetRouteVolumeLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertFalse(requestIds.isEmpty()); + + final int failureReason = REASON_REJECTED; + final CountDownLatch onRequestFailedLatch = new CountDownLatch(1); + addManagerCallback(new MediaRouter2Manager.Callback() { + @Override + public void onRequestFailed(int reason) { + if (reason == failureReason) { + onRequestFailedLatch.countDown(); + } + } + }); + + final long invalidRequestId = REQUEST_ID_NONE; + instance.notifyRequestFailed(invalidRequestId, failureReason); + assertFalse(onRequestFailedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); + + final long validRequestId = requestIds.get(0); + instance.notifyRequestFailed(validRequestId, failureReason); + assertTrue(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + @Test public void testVolumeHandling() throws Exception { Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); @@ -429,10 +484,11 @@ public class MediaRouter2ManagerTest { } @Override - public void onControlCategoriesChanged(String packageName, + public void onPreferredFeaturesChanged(String packageName, List<String> preferredFeatures) { if (TextUtils.equals(mPackageName, packageName) - && preferredFeatures.equals(routeFeatures)) { + && preferredFeatures.size() == routeFeatures.size() + && preferredFeatures.containsAll(routeFeatures)) { featuresLatch.countDown(); } } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java index 3faefdb148e1..0e7c7fc499b4 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java @@ -16,9 +16,9 @@ package com.android.mediaroutertest; -import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER; -import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV; import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; +import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER; +import static android.media.MediaRoute2Info.TYPE_REMOTE_TV; import android.annotation.NonNull; import android.annotation.Nullable; @@ -75,15 +75,16 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @GuardedBy("sLock") private static SampleMediaRoute2ProviderService sInstance; + private Proxy mProxy; private void initializeRoutes() { MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1) .addFeature(FEATURE_SAMPLE) - .setDeviceType(DEVICE_TYPE_REMOTE_TV) + .setType(TYPE_REMOTE_TV) .build(); MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2) .addFeature(FEATURE_SAMPLE) - .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER) + .setType(TYPE_REMOTE_SPEAKER) .build(); MediaRoute2Info route3 = new MediaRoute2Info.Builder( ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3) @@ -179,7 +180,13 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onSetRouteVolume(String routeId, int volume) { + public void onSetRouteVolume(long requestId, String routeId, int volume) { + Proxy proxy = mProxy; + if (proxy != null) { + proxy.onSetRouteVolume(routeId, volume, requestId); + return; + } + MediaRoute2Info route = mRoutes.get(routeId); if (route == null) { return; @@ -192,7 +199,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onSetSessionVolume(String sessionId, int volume) { + public void onSetSessionVolume(long requestId, String sessionId, int volume) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); if (sessionInfo == null) { return; @@ -205,7 +212,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onCreateSession(String packageName, String routeId, long requestId, + public void onCreateSession(long requestId, String packageName, String routeId, @Nullable Bundle sessionHints) { MediaRoute2Info route = mRoutes.get(routeId); if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { @@ -238,7 +245,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onReleaseSession(String sessionId) { + public void onReleaseSession(long requestId, String sessionId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); if (sessionInfo == null) { return; @@ -258,7 +265,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onSelectRoute(String sessionId, String routeId) { + public void onSelectRoute(long requestId, String sessionId, String routeId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); if (route == null || sessionInfo == null) { @@ -280,7 +287,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onDeselectRoute(String sessionId, String routeId) { + public void onDeselectRoute(long requestId, String sessionId, String routeId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); @@ -308,7 +315,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onTransferToRoute(String sessionId, String routeId) { + public void onTransferToRoute(long requestId, String sessionId, String routeId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); @@ -347,10 +354,18 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } String sessionId = mRouteIdToSessionId.get(routeId); - onDeselectRoute(sessionId, routeId); + onDeselectRoute(REQUEST_ID_NONE, sessionId, routeId); } void publishRoutes() { notifyRoutes(mRoutes.values()); } + + public void setProxy(@Nullable Proxy proxy) { + mProxy = proxy; + } + + public static class Proxy { + public void onSetRouteVolume(String routeId, int volume, long requestId) {} + } } diff --git a/native/android/Android.bp b/native/android/Android.bp index 257ae7332cc1..ed73f39e57f8 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -51,6 +51,7 @@ cc_library_shared { "surface_control.cpp", "system_fonts.cpp", "trace.cpp", + "thermal.cpp" ], shared_libs: [ @@ -72,6 +73,7 @@ cc_library_shared { "libxml2", "libEGL", "libGLESv2", + "libpowermanager", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", ], @@ -81,6 +83,8 @@ cc_library_shared { "libarect", ], + header_libs: [ "libhwui_internal_headers" ], + whole_static_libs: ["libnativedisplay", "libnativewindow"], export_static_lib_headers: ["libarect"], diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index a8f1d2c7bbba..d56aa86ae6fa 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -280,6 +280,11 @@ LIBANDROID { android_res_nquery; # introduced=29 android_res_nresult; # introduced=29 android_res_nsend; # introduced=29 + AThermal_acquireManager; # introduced=30 + AThermal_releaseManager; # introduced=30 + AThermal_getCurrentThermalStatus; # introduced=30 + AThermal_registerThermalStatusListener; # introduced=30 + AThermal_unregisterThermalStatusListener; # introduced=30 local: *; }; diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp new file mode 100644 index 000000000000..545c423908a0 --- /dev/null +++ b/native/android/thermal.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "thermal" + +#include <cerrno> +#include <thread> + +#include <android/thermal.h> +#include <android/os/BnThermalStatusListener.h> +#include <android/os/IThermalService.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> + +using android::sp; + +using namespace android; +using namespace android::os; + +struct ThermalServiceListener : public BnThermalStatusListener { + public: + virtual binder::Status onStatusChange(int32_t status) override; + ThermalServiceListener(AThermalManager *manager) {mMgr = manager;} + private: + AThermalManager *mMgr; +}; + +struct ListenerCallback { + AThermal_StatusCallback callback; + void* data; +}; + +struct AThermalManager { + public: + static AThermalManager* createAThermalManager(); + AThermalManager() = delete; + ~AThermalManager(); + status_t notifyStateChange(int32_t status); + status_t getCurrentThermalStatus(int32_t *status); + status_t addListener(AThermal_StatusCallback, void *data); + status_t removeListener(AThermal_StatusCallback, void *data); + private: + AThermalManager(sp<IThermalService> service); + sp<IThermalService> mThermalSvc; + sp<ThermalServiceListener> mServiceListener; + std::vector<ListenerCallback> mListeners; + std::mutex mMutex; +}; + +binder::Status ThermalServiceListener::onStatusChange(int32_t status) { + if (mMgr != nullptr) { + mMgr->notifyStateChange(status); + } + return binder::Status::ok(); +} + +AThermalManager* AThermalManager::createAThermalManager() { + sp<IBinder> binder = + defaultServiceManager()->checkService(String16("thermalservice")); + + if (binder == nullptr) { + ALOGE("%s: Thermal service is not ready ", __FUNCTION__); + return nullptr; + } + return new AThermalManager(interface_cast<IThermalService>(binder)); +} + +AThermalManager::AThermalManager(sp<IThermalService> service) + : mThermalSvc(service), + mServiceListener(nullptr) { +} + +AThermalManager::~AThermalManager() { + std::unique_lock<std::mutex> lock(mMutex); + + mListeners.clear(); + if (mServiceListener != nullptr) { + bool success = false; + mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); + mServiceListener = nullptr; + } +} + +status_t AThermalManager::notifyStateChange(int32_t status) { + std::unique_lock<std::mutex> lock(mMutex); + AThermalStatus thermalStatus = static_cast<AThermalStatus>(status); + + for (auto listener : mListeners) { + listener.callback(listener.data, thermalStatus); + } + return OK; +} + +status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) { + std::unique_lock<std::mutex> lock(mMutex); + + if (callback == nullptr) { + // Callback can not be nullptr + return EINVAL; + } + for (const auto& cb : mListeners) { + // Don't re-add callbacks. + if (callback == cb.callback && data == cb.data) { + return EINVAL; + } + } + mListeners.emplace_back(ListenerCallback{callback, data}); + + if (mServiceListener != nullptr) { + return OK; + } + bool success = false; + mServiceListener = new ThermalServiceListener(this); + if (mServiceListener == nullptr) { + return ENOMEM; + } + auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success); + if (!success || !ret.isOk()) { + ALOGE("Failed in registerThermalStatusListener %d", success); + if (ret.exceptionCode() == binder::Status::EX_SECURITY) { + return EPERM; + } + return EPIPE; + } + return OK; +} + +status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) { + std::unique_lock<std::mutex> lock(mMutex); + + auto it = std::remove_if(mListeners.begin(), + mListeners.end(), + [&](const ListenerCallback& cb) { + return callback == cb.callback && + data == cb.data; + }); + if (it == mListeners.end()) { + // If the listener and data pointer were not previously added. + return EINVAL; + } + mListeners.erase(it, mListeners.end()); + + if (!mListeners.empty()) { + return OK; + } + if (mServiceListener == nullptr) { + return OK; + } + bool success = false; + auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); + if (!success || !ret.isOk()) { + ALOGE("Failed in unregisterThermalStatusListener %d", success); + if (ret.exceptionCode() == binder::Status::EX_SECURITY) { + return EPERM; + } + return EPIPE; + } + mServiceListener = nullptr; + return OK; +} + +status_t AThermalManager::getCurrentThermalStatus(int32_t *status) { + binder::Status ret = mThermalSvc->getCurrentThermalStatus(status); + + if (!ret.isOk()) { + if (ret.exceptionCode() == binder::Status::EX_SECURITY) { + return EPERM; + } + return EPIPE; + } + return OK; +} + +/** + * Acquire an instance of the thermal manager. This must be freed using + * {@link AThermal_releaseManager}. + * + * @return manager instance on success, nullptr on failure. + */ +AThermalManager* AThermal_acquireManager() { + auto manager = AThermalManager::createAThermalManager(); + + return manager; +} + +/** + * Release the thermal manager pointer acquired by + * {@link AThermal_acquireManager}. + * + * @param manager The manager to be released. + * + */ +void AThermal_releaseManager(AThermalManager *manager) { + delete manager; +} + +/** + * Gets the current thermal status. + * + * @param manager The manager instance to use to query the thermal status, + * acquired by {@link AThermal_acquireManager}. + * + * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. +*/ +AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) { + int32_t status = 0; + status_t ret = manager->getCurrentThermalStatus(&status); + if (ret != OK) { + return AThermalStatus::ATHERMAL_STATUS_ERROR; + } + return static_cast<AThermalStatus>(status); +} + +/** + * Register the thermal status listener for thermal status change. + * + * @param manager The manager instance to use to register. + * acquired by {@link AThermal_acquireManager}. + * @param callback The callback function to be called when thermal status updated. + * @param data The data pointer to be passed when callback is called. + * + * @return 0 on success + * EINVAL if the listener and data pointer were previously added and not removed. + * EPERM if the required permission is not held. + * EPIPE if communication with the system service has failed. + */ +int AThermal_registerThermalStatusListener(AThermalManager *manager, + AThermal_StatusCallback callback, void *data) { + return manager->addListener(callback, data); +} + +/** + * Unregister the thermal status listener previously resgistered. + * + * @param manager The manager instance to use to unregister. + * acquired by {@link AThermal_acquireManager}. + * @param callback The callback function to be called when thermal status updated. + * @param data The data pointer to be passed when callback is called. + * + * @return 0 on success + * EINVAL if the listener and data pointer were not previously added. + * EPERM if the required permission is not held. + * EPIPE if communication with the system service has failed. + */ +int AThermal_unregisterThermalStatusListener(AThermalManager *manager, + AThermal_StatusCallback callback, void *data) { + return manager->removeListener(callback, data); +} diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 376ea77740c2..15b473c2a6ab 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -37,6 +37,8 @@ cc_library_shared { "liblog", ], + header_libs: [ "libhwui_internal_headers" ], + static_libs: ["libarect"], arch: { diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index d1946b085df4..56f390698eb2 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -20,7 +20,7 @@ #include <android/bitmap.h> #include <android/data_space.h> #include <android/imagedecoder.h> -#include <android/graphics/MimeType.h> +#include <MimeType.h> #include <android/rect.h> #include <hwui/ImageDecoder.h> #include <log/log.h> diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp index 88decc86c387..1a3b36d046e1 100644 --- a/native/webview/plat_support/Android.bp +++ b/native/webview/plat_support/Android.bp @@ -30,12 +30,14 @@ cc_library_shared { "graphic_buffer_impl.cpp", ], + header_libs: [ "libhwui_internal_headers" ], + shared_libs: [ "libandroidfw", - "libandroid_runtime", "libcutils", "libhwui", "liblog", + "libnativehelper", "libui", "libutils", "libvulkan", diff --git a/native/webview/plat_support/graphics_utils.cpp b/native/webview/plat_support/graphics_utils.cpp index 56825cee4520..8d7a59e46e72 100644 --- a/native/webview/plat_support/graphics_utils.cpp +++ b/native/webview/plat_support/graphics_utils.cpp @@ -25,11 +25,9 @@ #include <cstdlib> #include <jni.h> #include <utils/Log.h> -#include "android/graphics/GraphicsJNI.h" +#include "GraphicsJNI.h" #include "graphic_buffer_impl.h" #include "SkCanvasStateUtils.h" -#include "SkGraphics.h" -#include "SkPicture.h" #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java index e42ded74acd0..82ea7449bf6d 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java @@ -84,7 +84,6 @@ public class VerificationActivity extends Activity { // retrieve data from calling intent Intent callingIntent = getIntent(); Uri url = callingIntent.getData(); - Bundle extras = callingIntent.getExtras(); if (url != null) { sVerifiedUrl = url.toString(); @@ -96,7 +95,7 @@ public class VerificationActivity extends Activity { intent.setData(url); } intent.setAction(DynamicSystemClient.ACTION_START_INSTALL); - intent.putExtras(extras); + intent.putExtras(callingIntent); Log.d(TAG, "Starting Installation Service"); startServiceAsUser(intent, UserHandle.SYSTEM); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index aad46e9f0959..2dc6f393f7cc 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -989,6 +989,11 @@ public class SettingsProvider extends ContentProvider { String value = setting != null ? setting.getValue() : null; updateGlobalSetting(Settings.Global.ADB_ENABLED, value, null, true, userId, true); + + setting = getGlobalSetting(Settings.Global.ADB_WIFI_ENABLED); + value = setting != null ? setting.getValue() : null; + updateGlobalSetting(Settings.Global.ADB_WIFI_ENABLED, + value, null, true, userId, true); } } finally { Binder.restoreCallingIdentity(identity); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 4dc372a39404..0f2ee6ac8bbd 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -111,6 +111,7 @@ public class SettingsBackupTest { Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, Settings.Global.ADB_ENABLED, + Settings.Global.ADB_WIFI_ENABLED, Settings.Global.ADD_USERS_WHEN_LOCKED, Settings.Global.AIRPLANE_MODE_ON, Settings.Global.AIRPLANE_MODE_RADIOS, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 5458676e1061..f141578f088d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -457,6 +457,25 @@ android:excludeFromRecents="true"> </activity> + <!-- started from WirelessDebuggingManager --> + <activity android:name=".wifi.WifiDebuggingActivity" + android:permission="android.permission.MANAGE_DEBUGGING" + android:theme="@style/Theme.SystemUI.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + </activity> + <activity-alias + android:name=".WifiDebuggingActivityAlias" + android:permission="android.permission.DUMP" + android:targetActivity=".wifi.WifiDebuggingActivity" + android:exported="true"> + </activity-alias> + <activity android:name=".wifi.WifiDebuggingSecondaryUserActivity" + android:theme="@style/Theme.SystemUI.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + </activity> + <!-- started from NetworkPolicyManagerService --> <activity android:name=".net.NetworkOverLimitActivity" @@ -696,8 +715,7 @@ <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" android:authorities="com.android.keyguard.clock" - android:enabled="false" - android:exported="false" + android:exported="true" android:grantUriPermissions="true"> </provider> diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml index 481c4dbe3bf1..a06f434d068c 100644 --- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml +++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml @@ -25,6 +25,7 @@ <androidx.recyclerview.widget.RecyclerView android:id="@+id/bubble_overflow_recycler" android:layout_gravity="center_horizontal" + android:nestedScrollingEnabled="false" android:layout_width="wrap_content" android:layout_height="wrap_content"/> @@ -32,6 +33,8 @@ android:id="@+id/bubble_overflow_empty_state" android:layout_width="match_parent" android:layout_height="match_parent" + android:paddingLeft="@dimen/bubble_overflow_empty_state_padding" + android:paddingRight="@dimen/bubble_overflow_empty_state_padding" android:orientation="vertical" android:gravity="center"> diff --git a/packages/SystemUI/res/layout/bubble_overflow_button.xml b/packages/SystemUI/res/layout/bubble_overflow_button.xml index eb5dc9b0051a..8f0fd4f37461 100644 --- a/packages/SystemUI/res/layout/bubble_overflow_button.xml +++ b/packages/SystemUI/res/layout/bubble_overflow_button.xml @@ -14,11 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<ImageView +<com.android.systemui.bubbles.BadgedImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/bubble_overflow_button" android:layout_width="@dimen/individual_bubble_size" android:layout_height="@dimen/individual_bubble_size" - android:src="@drawable/ic_bubble_overflow_button" - android:scaleType="center" - android:layout_gravity="end"/> + android:src="@drawable/ic_bubble_overflow_button"/> diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml index 982aa8ef6d16..c2dbacaa64f7 100644 --- a/packages/SystemUI/res/layout/people_strip.xml +++ b/packages/SystemUI/res/layout/people_strip.xml @@ -19,39 +19,34 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/notification_section_header_height" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:focusable="true" android:clickable="true" > - <com.android.systemui.statusbar.notification.row.NotificationBackgroundView - android:id="@+id/backgroundNormal" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - - <com.android.systemui.statusbar.notification.row.NotificationBackgroundView - android:id="@+id/backgroundDimmed" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <LinearLayout android:id="@+id/people_list" android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center" + android:layout_marginEnd="8dp" + android:gravity="bottom" android:orientation="horizontal"> - <TextView + <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_marginStart="@dimen/notification_section_header_padding_left" - android:gravity="start" - android:textAlignment="gravity" - android:text="@string/notification_section_header_conversations" - android:textSize="12sp" - android:textColor="@color/notification_section_header_label_color" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - /> + android:gravity="start|center_vertical" + android:layout_weight="1"> + + <TextView + style="@style/TextAppearance.NotificationSectionHeaderButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/notification_section_header_conversations" + /> + + </FrameLayout> <ImageView android:layout_width="48dp" @@ -84,16 +79,10 @@ <ImageView android:layout_width="48dp" android:layout_height="48dp" - android:layout_marginEnd="8dp" android:padding="8dp" android:scaleType="fitCenter" /> </LinearLayout> - <com.android.systemui.statusbar.notification.FakeShadowView - android:id="@+id/fake_shadow" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - </com.android.systemui.statusbar.notification.stack.PeopleHubView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml index 174a3b8e004b..36ba66af5729 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml @@ -25,9 +25,9 @@ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/content" android:layout_width="match_parent" - android:layout_height="wrap_content" > + android:layout_height="wrap_content"> <com.android.systemui.statusbar.notification.row.FooterViewButton - style="@android:style/Widget.Material.Button.Borderless" + style="@style/TextAppearance.NotificationSectionHeaderButton" android:id="@+id/manage_text" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -35,10 +35,9 @@ android:focusable="true" android:contentDescription="@string/accessibility_manage_notification" android:text="@string/manage_notifications_text" - android:textColor="?attr/wallpaperTextColor" - android:textAllCaps="false"/> + /> <com.android.systemui.statusbar.notification.row.FooterViewButton - style="@android:style/Widget.Material.Button.Borderless" + style="@style/TextAppearance.NotificationSectionHeaderButton" android:id="@+id/dismiss_text" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -46,6 +45,6 @@ android:focusable="true" android:contentDescription="@string/accessibility_clear_all" android:text="@string/clear_all_notifications_text" - android:textColor="?attr/wallpaperTextColor"/> + /> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> </com.android.systemui.statusbar.notification.row.FooterView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml index 508619a27e81..0043d7a7bdad 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml @@ -19,32 +19,21 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/notification_section_header_height" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:focusable="true" android:clickable="true" > - <com.android.systemui.statusbar.notification.row.NotificationBackgroundView - android:id="@+id/backgroundNormal" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <com.android.systemui.statusbar.notification.row.NotificationBackgroundView - android:id="@+id/backgroundDimmed" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <LinearLayout android:id="@+id/content" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" android:gravity="center_vertical" android:orientation="horizontal" > <include layout="@layout/status_bar_notification_section_header_contents"/> </LinearLayout> - <com.android.systemui.statusbar.notification.FakeShadowView - android:id="@+id/fake_shadow" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - </com.android.systemui.statusbar.notification.stack.SectionHeaderView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml index feabd1c72a65..df4b0471c78b 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml @@ -16,26 +16,30 @@ <!-- Used by both status_bar_notification_header and SectionHeaderView --> <merge xmlns:android="http://schemas.android.com/apk/res/android" > - <TextView - android:id="@+id/header_label" + <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_marginStart="@dimen/notification_section_header_padding_left" - android:gravity="start" - android:textAlignment="gravity" - android:text="@string/notification_section_header_gentle" - android:textSize="12sp" - android:textColor="@color/notification_section_header_label_color" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - /> + android:gravity="start|center_vertical" + android:layout_weight="1"> + + <TextView + style="@style/TextAppearance.NotificationSectionHeaderButton" + android:id="@+id/header_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/notification_section_header_gentle" + /> + + </FrameLayout> <ImageView android:id="@+id/btn_clear_all" - android:layout_width="@dimen/notification_section_header_height" - android:layout_height="@dimen/notification_section_header_height" - android:layout_marginEnd="4dp" + android:layout_width="48dp" + android:layout_height="48dp" android:src="@drawable/status_bar_notification_section_header_clear_btn" android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all" android:scaleType="center" + android:tint="?attr/wallpaperTextColor" + android:tintMode="src_in" + android:visibility="gone" /> </merge> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 15575a49bb5e..1c2404f1921e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -744,7 +744,7 @@ <!-- The top padding of the clear all button --> <dimen name="clear_all_padding_top">12dp</dimen> - <dimen name="notification_section_header_height">48dp</dimen> + <dimen name="notification_section_header_height">56dp</dimen> <dimen name="notification_section_header_padding_left">16dp</dimen> <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or @@ -1128,8 +1128,10 @@ <dimen name="bubble_padding_top">16dp</dimen> <!-- Size of individual bubbles. --> <dimen name="individual_bubble_size">60dp</dimen> + <!-- Size of bubble bitmap. --> + <dimen name="bubble_bitmap_size">52dp</dimen> <!-- Size of bubble icon bitmap. --> - <dimen name="bubble_icon_bitmap_size">52dp</dimen> + <dimen name="bubble_overflow_icon_bitmap_size">24dp</dimen> <!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. --> <dimen name="bubble_touch_padding">12dp</dimen> <!-- Size of the circle around the bubbles when they're in the dismiss target. --> @@ -1141,6 +1143,12 @@ <dimen name="bubble_expanded_view_slop">8dp</dimen> <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded --> <dimen name="bubble_expanded_default_height">180dp</dimen> + <!-- Default height of bubble overflow --> + <dimen name="bubble_overflow_height">380dp</dimen> + <!-- Bubble overflow padding when there are no bubbles --> + <dimen name="bubble_overflow_empty_state_padding">16dp</dimen> + <!-- Margin of overflow bubbles --> + <dimen name="bubble_overflow_margin">16dp</dimen> <!-- Height of the triangle that points to the expanded bubble --> <dimen name="bubble_pointer_height">4dp</dimen> <!-- Width of the triangle that points to the expanded bubble --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 5e9feff566e5..4aafec886a37 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -168,6 +168,24 @@ <!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. --> <string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string> + <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] --> + <string name="wifi_debugging_title">Allow wireless debugging on this network?</string> + + <!-- Message of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] --> + <string name="wifi_debugging_message">Network Name (SSID)\n<xliff:g id="ssid" example="My wifi">%1$s</xliff:g>\n\nWi\u2011Fi Address (BSSID)\n<xliff:g id="bssid" example="AB:CD:EF:12:34:56">%2$s</xliff:g></string> + + <!-- Option to always allow wireless debugging on this network [CHAR LIMIT=NONE] --> + <string name="wifi_debugging_always">Always allow on this network</string> + + <!-- Button label for confirming acceptance of enabling wireless debugging [CHAR LIMIT=15] --> + <string name="wifi_debugging_allow">Allow</string> + + <!-- Title of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] --> + <string name="wifi_debugging_secondary_user_title">Wireless debugging not allowed</string> + + <!-- Message of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] --> + <string name="wifi_debugging_secondary_user_message">The user currently signed in to this device can\u2019t turn on wireless debugging. To use this feature, switch to the primary user.</string> + <!-- Title of USB contaminant presence dialog [CHAR LIMIT=NONE] --> <string name="usb_contaminant_title">USB port disabled</string> @@ -1204,6 +1222,9 @@ <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] --> <string name="notification_section_header_gentle">Silent notifications</string> + <!-- Section title for notifications that vibrate or make noise. [CHAR LIMIT=40] --> + <string name="notification_section_header_alerting">Alerting notifications</string> + <!-- Section title for conversational notifications. [CHAR LIMIT=40] --> <string name="notification_section_header_conversations">Conversations</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 557e2d65202b..36c4526fb521 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -554,6 +554,14 @@ <item name="android:gravity">center</item> </style> + <style + name="TextAppearance.NotificationSectionHeaderButton" + parent="@android:style/Widget.Material.Button.Borderless"> + <item name="android:textColor">?attr/wallpaperTextColor</item> + <item name="android:textAllCaps">false</item> + <item name="android:textSize">16sp</item> + </style> + <style name="TextAppearance.HeadsUpStatusBarText" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info"> </style> @@ -661,5 +669,5 @@ <item name="android:textSize">12sp</item> <item name="android:textColor">@color/control_secondary_text</item> </style> - + </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java new file mode 100644 index 000000000000..b813e2178fa3 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import android.app.WallpaperManager; +import android.content.Context; + +public class WallpaperManagerCompat { + private final WallpaperManager mWallpaperManager; + + public WallpaperManagerCompat(Context context) { + mWallpaperManager = context.getSystemService(WallpaperManager.class); + } + + public void setWallpaperZoomOut(float zoom) { + mWallpaperManager.setWallpaperZoomOut(zoom); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index e475ef1d9761..6f06f6986c00 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -51,7 +51,7 @@ import javax.inject.Named; public class KeyguardClockSwitch extends RelativeLayout { private static final String TAG = "KeyguardClockSwitch"; - private static final boolean CUSTOM_CLOCKS_ENABLED = false; + private static final boolean CUSTOM_CLOCKS_ENABLED = true; /** * Animation fraction when text is transitioned to/from bold. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 09d4d5fcbde9..20b1e0d2c822 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -57,6 +57,12 @@ public interface KeyguardSecurityView { int PROMPT_REASON_PREPARE_FOR_UPDATE = 6; /** + * Primary auth is required because the user uses weak/convenience biometrics and hasn't used + * primary auth since a while + */ + int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7; + + /** * Interface back to keyguard to tell it when security * @param callback */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 9ba3860f4fb8..f57571d8d4b4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -80,6 +80,7 @@ import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; import android.util.Log; +import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; @@ -91,6 +92,7 @@ import com.android.systemui.DumpController; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -108,6 +110,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TimeZone; +import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; @@ -264,6 +267,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // If the user long pressed the lock icon, disabling face auth for the current session. private boolean mLockIconPressed; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private final Executor mBackgroundExecutor; /** * Short delay before restarting biometric authentication after a successful try @@ -320,12 +324,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; + private class BiometricAuthenticated { + private final boolean mAuthenticated; + private final boolean mIsStrongBiometric; + + BiometricAuthenticated(boolean authenticated, boolean isStrongBiometric) { + this.mAuthenticated = authenticated; + this.mIsStrongBiometric = isStrongBiometric; + } + } + private SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray(); private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray(); - private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray(); - private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray(); + private SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); + private SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>(); @@ -523,10 +537,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } @VisibleForTesting - protected void onFingerprintAuthenticated(int userId) { + protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) { Assert.isMainThread(); Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated"); - mUserFingerprintAuthenticated.put(userId, true); + mUserFingerprintAuthenticated.put(userId, + new BiometricAuthenticated(true, isStrongBiometric)); // Update/refresh trust state only if user can skip bouncer if (getUserCanSkipBouncer(userId)) { mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT); @@ -536,7 +551,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT); + cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT, + isStrongBiometric); } } @@ -546,9 +562,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Only authenticate fingerprint once when assistant is visible mAssistantVisible = false; + // Report unlock with strong or non-strong biometric + reportSuccessfulBiometricUnlock(isStrongBiometric, userId); + Trace.endSection(); } + private void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) { + mBackgroundExecutor.execute(new Runnable() { + @Override + public void run() { + mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId); + } + }); + } + private void handleFingerprintAuthFailed() { Assert.isMainThread(); for (int i = 0; i < mCallbacks.size(); i++) { @@ -574,7 +602,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void handleFingerprintAuthenticated(int authUserId) { + private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated"); try { final int userId; @@ -592,7 +620,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId); return; } - onFingerprintAuthenticated(userId); + onFingerprintAuthenticated(userId, isStrongBiometric); } finally { setFingerprintRunningState(BIOMETRIC_STATE_STOPPED); } @@ -683,10 +711,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } @VisibleForTesting - protected void onFaceAuthenticated(int userId) { + protected void onFaceAuthenticated(int userId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated"); Assert.isMainThread(); - mUserFaceAuthenticated.put(userId, true); + mUserFaceAuthenticated.put(userId, + new BiometricAuthenticated(true, isStrongBiometric)); // Update/refresh trust state only if user can skip bouncer if (getUserCanSkipBouncer(userId)) { mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE); @@ -697,7 +726,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onBiometricAuthenticated(userId, - BiometricSourceType.FACE); + BiometricSourceType.FACE, + isStrongBiometric); } } @@ -707,6 +737,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Only authenticate face once when assistant is visible mAssistantVisible = false; + // Report unlock with strong or non-strong biometric + reportSuccessfulBiometricUnlock(isStrongBiometric, userId); + Trace.endSection(); } @@ -737,7 +770,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void handleFaceAuthenticated(int authUserId) { + private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated"); try { if (mGoingToSleep) { @@ -760,7 +793,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } if (DEBUG_FACE) Log.d(TAG, "Face auth succeeded for user " + userId); - onFaceAuthenticated(userId); + onFaceAuthenticated(userId, isStrongBiometric); } finally { setFaceRunningState(BIOMETRIC_STATE_STOPPED); } @@ -914,9 +947,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Returns whether the user is unlocked with biometrics. */ public boolean getUserUnlockedWithBiometric(int userId) { - boolean fingerprintOrFace = mUserFingerprintAuthenticated.get(userId) - || mUserFaceAuthenticated.get(userId); - return fingerprintOrFace && isUnlockingWithBiometricAllowed(); + BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId); + BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); + boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated + && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric); + boolean faceAllowed = face != null && face.mAuthenticated + && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric); + return fingerprintAllowed || faceAllowed; } public boolean getUserTrustIsManaged(int userId) { @@ -970,8 +1007,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mUserTrustIsUsuallyManaged.get(userId); } - public boolean isUnlockingWithBiometricAllowed() { - return mStrongAuthTracker.isUnlockingWithBiometricAllowed(); + public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) { + return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric); } public boolean isUserInLockdown(int userId) { @@ -1169,7 +1206,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationSucceeded(AuthenticationResult result) { Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); - handleFingerprintAuthenticated(result.getUserId()); + handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric()); Trace.endSection(); } @@ -1201,7 +1238,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) { Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); - handleFaceAuthenticated(result.getUserId()); + handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); Trace.endSection(); } @@ -1305,9 +1342,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback; } - public boolean isUnlockingWithBiometricAllowed() { + public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) { int userId = getCurrentUser(); - return isBiometricAllowedForUser(userId); + return isBiometricAllowedForUser(isStrongBiometric, userId); } public boolean hasUserAuthenticatedSinceBoot() { @@ -1438,12 +1475,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Context context, @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher, - DumpController dumpController) { + DumpController dumpController, + @Background Executor backgroundExecutor) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged); dumpController.registerDumpable(this); + mBackgroundExecutor = backgroundExecutor; mHandler = new Handler(mainLooper) { @Override @@ -1753,14 +1792,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private boolean shouldListenForFingerprintAssistant() { + BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser()); return mAssistantVisible && mKeyguardOccluded - && !mUserFingerprintAuthenticated.get(getCurrentUser(), false) + && !(fingerprint != null && fingerprint.mAuthenticated) && !mUserHasTrust.get(getCurrentUser(), false); } private boolean shouldListenForFaceAssistant() { + BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser()); return mAssistantVisible && mKeyguardOccluded - && !mUserFaceAuthenticated.get(getCurrentUser(), false) + && !(face != null && face.mAuthenticated) && !mUserHasTrust.get(getCurrentUser(), false); } @@ -1817,7 +1858,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void onLockIconPressed() { mLockIconPressed = true; final int userId = getCurrentUser(); - mUserFaceAuthenticated.put(userId, false); + mUserFaceAuthenticated.put(userId, null); updateFaceListeningState(); mStrongAuthTracker.onStrongAuthRequiredChanged(userId); } @@ -2691,9 +2732,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (mFpm != null && mFpm.isHardwareDetected()) { final int userId = ActivityManager.getCurrentUser(); final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId); + BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId); pw.println(" Fingerprint state (user=" + userId + ")"); - pw.println(" allowed=" + isUnlockingWithBiometricAllowed()); - pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId)); + pw.println(" allowed=" + + (fingerprint != null + && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric))); + pw.println(" auth'd=" + (fingerprint != null && fingerprint.mAuthenticated)); pw.println(" authSinceBoot=" + getStrongAuthTracker().hasUserAuthenticatedSinceBoot()); pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId)); @@ -2706,9 +2750,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (mFaceManager != null && mFaceManager.isHardwareDetected()) { final int userId = ActivityManager.getCurrentUser(); final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId); + BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); pw.println(" Face authentication state (user=" + userId + ")"); - pw.println(" allowed=" + isUnlockingWithBiometricAllowed()); - pw.println(" auth'd=" + mUserFaceAuthenticated.get(userId)); + pw.println(" allowed=" + + (face != null && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric))); + pw.println(" auth'd=" + + (face != null && face.mAuthenticated)); pw.println(" authSinceBoot=" + getStrongAuthTracker().hasUserAuthenticatedSinceBoot()); pw.println(" disabled(DPM)=" + isFaceDisabled(userId)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 49f72a925a0e..12e0ecd011cf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -239,7 +239,8 @@ public class KeyguardUpdateMonitorCallback { * @param userId the user id for which the biometric sample was authenticated * @param biometricSourceType */ - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { } + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { } /** * Called when biometric authentication provides help string (e.g. "Try again") diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 9cd4aec4617d..03674648d1e4 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -149,8 +149,6 @@ public final class ClockManager { LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); @@ -211,7 +209,8 @@ public final class ClockManager { return mContentObserver; } - private void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { + @VisibleForTesting + void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { ClockPlugin plugin = pluginSupplier.get(); mPreviewClocks.addClockPlugin(plugin); mBuiltinClocks.add(pluginSupplier); diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java index 8503396975b5..85ce313670e3 100644 --- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java +++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java @@ -57,7 +57,6 @@ public class CornerHandleView extends View { mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(getStrokePx()); - setLayerType(View.LAYER_TYPE_SOFTWARE, mPaint); final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); @@ -118,14 +117,8 @@ public class CornerHandleView extends View { // Handle color is same as home handle color. int color = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightColor, mDarkColor); - // Shadow color is inverse of handle color. - int shadowColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, - mDarkColor, mLightColor); if (mPaint.getColor() != color) { mPaint.setColor(color); - mPaint.setShadowLayer(/** radius */ getResources().getDimensionPixelSize( - com.android.internal.R.dimen.assist_handle_shadow_radius), /** shadowDx */ 0, - /** shadowDy */ 0, /** color */ shadowColor); if (getVisibility() == VISIBLE && getAlpha() > 0) { invalidate(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java index 1a47daceafcc..dc0cb03e9b23 100644 --- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java +++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java @@ -96,6 +96,7 @@ public class LatencyTester extends SystemUI { private void fakeWakeAndUnlock() { mBiometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthenticated( - KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT); + KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT, + true /* isStrongBiometric */); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java index 601bae286451..a1cb7f61ad04 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java @@ -17,7 +17,6 @@ package com.android.systemui.bubbles; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Path; import android.graphics.Rect; @@ -50,9 +49,9 @@ public class BadgedImageView extends ImageView { // Flyout gets shown before the dot private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT; - private Bubble mBubble; + private BubbleViewProvider mBubble; - private int mIconBitmapSize; + private int mBubbleBitmapSize; private DotRenderer mDotRenderer; private DotRenderer.DrawParams mDrawParams; private boolean mOnLeft; @@ -78,18 +77,18 @@ public class BadgedImageView extends ImageView { public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mIconBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size); + mBubbleBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size); mDrawParams = new DotRenderer.DrawParams(); Path iconPath = PathParser.createPathFromPathData( getResources().getString(com.android.internal.R.string.config_icon_mask)); - mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE); + mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE); } /** * Updates the view with provided info. */ - public void update(Bubble bubble) { + public void update(BubbleViewProvider bubble) { mBubble = bubble; setImageBitmap(bubble.getBadgedImage()); setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); @@ -147,7 +146,7 @@ public class BadgedImageView extends ImageView { * @param iconPath The new icon path to use when calculating dot position. */ void drawDot(Path iconPath) { - mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE); + mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE); invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index cdeb5c3f2511..7ca23085f6ee 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -95,6 +95,7 @@ class Bubble implements BubbleViewProvider { private int mDotColor; private Path mDotPath; + public static String groupId(NotificationEntry entry) { UserHandle user = entry.getSbn().getUser(); return user.getIdentifier() + "|" + entry.getSbn().getPackageName(); @@ -111,6 +112,7 @@ class Bubble implements BubbleViewProvider { mSuppressionListener = listener; } + @Override public String getKey() { return mKey; } @@ -127,14 +129,17 @@ class Bubble implements BubbleViewProvider { return mEntry.getSbn().getPackageName(); } + @Override public Bitmap getBadgedImage() { return mBadgedImage; } + @Override public int getDotColor() { return mDotColor; } + @Override public Path getDotPath() { return mDotPath; } @@ -150,10 +155,12 @@ class Bubble implements BubbleViewProvider { } @Nullable + @Override public BadgedImageView getIconView() { return mIconView; } + @Override @Nullable public BubbleExpandedView getExpandedView() { return mExpandedView; @@ -240,6 +247,7 @@ class Bubble implements BubbleViewProvider { * Note that this contents visibility doesn't affect visibility at {@link android.view.View}, * and setting {@code false} actually means rendering the expanded view in transparent. */ + @Override public void setContentVisibility(boolean visibility) { if (mExpandedView != null) { mExpandedView.setContentVisibility(visibility); @@ -333,7 +341,8 @@ class Bubble implements BubbleViewProvider { /** * Whether the bubble for this notification should show a dot indicating updated content. */ - boolean showDot() { + @Override + public boolean showDot() { return mShowBubbleUpdateDot && !mEntry.shouldSuppressNotificationDot() && !shouldSuppressNotification(); @@ -484,6 +493,7 @@ class Bubble implements BubbleViewProvider { return Objects.hash(mKey); } + @Override public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) { if (this.getEntry() == null || this.getEntry().getSbn() == null) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index fe191f40d31f..e3983c5b2d92 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -94,6 +94,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private Point mDisplaySize; private int mMinHeight; + private int mOverflowHeight; private int mSettingsIconHeight; private int mPointerWidth; private int mPointerHeight; @@ -218,6 +219,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize); Resources res = getResources(); mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); + mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop); } @@ -420,20 +422,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList return true; } - // TODO(138116789) Fix overflow height. void updateHeight() { if (DEBUG_BUBBLE_EXPANDED_VIEW) { Log.d(TAG, "updateHeight: bubble=" + getBubbleKey()); } if (usingActivityView()) { - float desiredHeight = mMinHeight; + float desiredHeight = mOverflowHeight; if (!mIsOverflow) { desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight); } float height = Math.min(desiredHeight, getMaxExpandedHeight()); - height = Math.max(height, mMinHeight); + height = Math.max(height, mIsOverflow? mOverflowHeight : mMinHeight); LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams(); - mNeedsNewHeight = lp.height != height; + mNeedsNewHeight = lp.height != height; if (!mKeyboardVisible) { // If the keyboard is visible... don't adjust the height because that will cause // a configuration change and the keyboard will be lost. diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java index 5b9ea7dd5e3a..ca53fa7e6811 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java @@ -58,7 +58,7 @@ public class BubbleFlyoutView extends FrameLayout { private final int mFlyoutSpaceFromBubble; private final int mPointerSize; private final int mBubbleSize; - private final int mBubbleIconBitmapSize; + private final int mBubbleBitmapSize; private final float mBubbleIconTopPadding; private final int mFlyoutElevation; @@ -156,13 +156,13 @@ public class BubbleFlyoutView extends FrameLayout { mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); - mBubbleIconBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size); - mBubbleIconTopPadding = (mBubbleSize - mBubbleIconBitmapSize) / 2f; + mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size); + mBubbleIconTopPadding = (mBubbleSize - mBubbleBitmapSize) / 2f; mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation); - mOriginalDotSize = SIZE_PERCENTAGE * mBubbleIconBitmapSize; + mOriginalDotSize = SIZE_PERCENTAGE * mBubbleBitmapSize; mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f; mNewDotSize = mNewDotRadius * 2f; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java index 6cf1086e70be..a0e744979112 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java @@ -17,18 +17,23 @@ package com.android.systemui.bubbles; import static android.view.View.GONE; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Path; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.InsetDrawable; +import android.util.PathParser; +import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.ImageView; import com.android.systemui.R; @@ -37,15 +42,24 @@ import com.android.systemui.R; * Class for showing aged out bubbles. */ public class BubbleOverflow implements BubbleViewProvider { + public static final String KEY = "Overflow"; - private ImageView mOverflowBtn; + private BadgedImageView mOverflowBtn; private BubbleExpandedView mOverflowExpandedView; private LayoutInflater mInflater; private Context mContext; + private Bitmap mIcon; + private Path mPath; + private int mBitmapSize; + private int mIconBitmapSize; + private int mDotColor; public BubbleOverflow(Context context) { mContext = context; mInflater = LayoutInflater.from(context); + mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size); + mIconBitmapSize = mContext.getResources().getDimensionPixelSize( + R.dimen.bubble_overflow_icon_bitmap_size); } public void setUpOverflow(ViewGroup parentViewGroup) { @@ -54,12 +68,49 @@ public class BubbleOverflow implements BubbleViewProvider { false /* attachToRoot */); mOverflowExpandedView.setOverflow(true); - mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button, + updateIcon(mContext, parentViewGroup); + } + + // TODO(b/149146374) Propagate theme change to bubbles in overflow. + void updateIcon(Context context, ViewGroup parentViewGroup) { + mInflater = LayoutInflater.from(context); + mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button, parentViewGroup /* root */, false /* attachToRoot */); - setOverflowBtnTheme(); + TypedArray ta = mContext.obtainStyledAttributes( + new int[]{android.R.attr.colorBackgroundFloating}); + int bgColor = ta.getColor(0, Color.WHITE /* default */); + ta.recycle(); + + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); + int colorAccent = mContext.getColor(typedValue.resourceId); + mOverflowBtn.getDrawable().setTint(colorAccent); + mDotColor = colorAccent; + + ColorDrawable bg = new ColorDrawable(bgColor); + InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), + mBitmapSize - mIconBitmapSize /* inset */); + AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg); + + BubbleIconFactory iconFactory = new BubbleIconFactory(context); + mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable, + null /* user */, + true /* shrinkNonAdaptiveIcons */).icon; + + float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(), + null /* outBounds */, null /* path */, null /* outMaskShape */); + float radius = DEFAULT_PATH_SIZE / 2f; + mPath = PathParser.createPathFromPathData( + context.getResources().getString(com.android.internal.R.string.config_icon_mask)); + Matrix matrix = new Matrix(); + matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */, + radius /* pivot y */); + mPath.transform(matrix); + mOverflowBtn.setVisibility(GONE); + mOverflowBtn.update(this); } ImageView getBtn() { @@ -70,38 +121,49 @@ public class BubbleOverflow implements BubbleViewProvider { mOverflowBtn.setVisibility(visible); } - // TODO(b/149146374) Propagate theme change to bubbles in overflow. - void setOverflowBtnTheme() { - TypedArray ta = mContext.obtainStyledAttributes( - new int[]{android.R.attr.colorBackgroundFloating}); - int bgColor = ta.getColor(0, Color.WHITE /* default */); - ta.recycle(); + @Override + public BubbleExpandedView getExpandedView() { + return mOverflowExpandedView; + } - InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28); - ColorDrawable bg = new ColorDrawable(bgColor); - AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg); - mOverflowBtn.setImageDrawable(adaptiveIcon); + @Override + public int getDotColor() { + return mDotColor; } + @Override + public Bitmap getBadgedImage() { + return mIcon; + } - public BubbleExpandedView getExpandedView() { - return mOverflowExpandedView; + @Override + public boolean showDot() { + return false; + } + + @Override + public Path getDotPath() { + return mPath; } + @Override public void setContentVisibility(boolean visible) { mOverflowExpandedView.setContentVisibility(visible); } + @Override public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) { // TODO(b/149133814) Log overflow UI events. } + @Override public View getIconView() { return mOverflowBtn; } + @Override public String getKey() { - return BubbleOverflowActivity.KEY; + return BubbleOverflow.KEY; } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index eb836b1a21f9..7636c6712e41 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -29,7 +29,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -47,7 +46,6 @@ import javax.inject.Inject; * Must be public to be accessible to androidx...AppComponentFactory */ public class BubbleOverflowActivity extends Activity { - public static final String KEY = "Overflow"; private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES; private LinearLayout mEmptyState; @@ -73,10 +71,10 @@ public class BubbleOverflowActivity extends Activity { new GridLayoutManager(getApplicationContext(), getResources().getInteger(R.integer.bubbles_overflow_columns))); + int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin); mAdapter = new BubbleOverflowAdapter(mOverflowBubbles, - mBubbleController::promoteBubbleFromOverflow); + mBubbleController::promoteBubbleFromOverflow, bubbleMargin); mRecyclerView.setAdapter(mAdapter); - onDataChanged(mBubbleController.getOverflowBubbles()); mBubbleController.setOverflowCallback(() -> { onDataChanged(mBubbleController.getOverflowBubbles()); @@ -141,10 +139,13 @@ public class BubbleOverflowActivity extends Activity { class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> { private Consumer<Bubble> mPromoteBubbleFromOverflow; private List<Bubble> mBubbles; + private int mBubbleMargin; - public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble) { + public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, + int bubbleMargin) { mBubbles = list; mPromoteBubbleFromOverflow = promoteBubble; + mBubbleMargin = bubbleMargin; } @Override @@ -152,6 +153,12 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V int viewType) { BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext()) .inflate(R.layout.bubble_view, parent, false); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ); + params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin); + view.setLayoutParams(params); return new ViewHolder(view); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 955edcf6acb9..072c20c684dd 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -410,13 +410,7 @@ public class BubbleStackView extends FrameLayout { setFocusable(true); mBubbleContainer.bringToFront(); - mBubbleOverflow = new BubbleOverflow(mContext); - if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) { - mBubbleOverflow.setUpOverflow(this); - mBubbleContainer.addView(mBubbleOverflow.getBtn(), 0, - new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - - } + setUpOverflow(); setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { if (!mIsExpanded || mIsExpansionAnimating) { @@ -525,14 +519,29 @@ public class BubbleStackView extends FrameLayout { addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); } + private void setUpOverflow() { + if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) { + return; + } + int overflowBtnIndex = 0; + if (mBubbleOverflow == null) { + mBubbleOverflow = new BubbleOverflow(mContext); + mBubbleOverflow.setUpOverflow(this); + } else { + mBubbleContainer.removeView(mBubbleOverflow.getBtn()); + mBubbleOverflow.updateIcon(mContext, this); + overflowBtnIndex = mBubbleContainer.getChildCount() - 1; + } + mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex, + new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + + } /** * Handle theme changes. */ public void onThemeChanged() { setUpFlyout(); - if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) { - mBubbleOverflow.setOverflowBtnTheme(); - } + setUpOverflow(); } /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */ @@ -726,7 +735,7 @@ public class BubbleStackView extends FrameLayout { if (mExpandedBubble == null || (BubbleExperimentConfig.allowBubbleOverflow(mContext) && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn() - && mExpandedBubble.getKey() == BubbleOverflowActivity.KEY)) { + && mExpandedBubble.getKey() == BubbleOverflow.KEY)) { return null; } return (Bubble) mExpandedBubble; @@ -1651,7 +1660,7 @@ public class BubbleStackView extends FrameLayout { * is between 0 and the bubble count minus 1. */ int getBubbleIndex(@Nullable BubbleViewProvider provider) { - if (provider == null || provider.getKey() == BubbleOverflowActivity.KEY) { + if (provider == null || provider.getKey() == BubbleOverflow.KEY) { return 0; } Bubble b = (Bubble) provider; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index 645696d0bcac..5e3e747ad2c0 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -100,9 +100,6 @@ class BubbleTouchHandler implements View.OnTouchListener { && !(mTouchedView instanceof BubbleStackView) && !(mTouchedView instanceof BubbleFlyoutView)) { - if (mTouchedView.getId() == R.id.bubble_overflow_button) { - mStack.showOverflow(); - } // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge // of expanded view). resetForNextGesture(); @@ -225,9 +222,12 @@ class BubbleTouchHandler implements View.OnTouchListener { mBubbleData.setExpanded(!mBubbleData.isExpanded()); } else { final String key = ((BadgedImageView) mTouchedView).getKey(); - mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key)); + if (key == BubbleOverflow.KEY) { + mStack.showOverflow(); + } else { + mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key)); + } } - resetForNextGesture(); break; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java index 59fc435222d6..f04933abdcc2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java @@ -16,6 +16,8 @@ package com.android.systemui.bubbles; +import android.graphics.Bitmap; +import android.graphics.Path; import android.view.View; /** @@ -23,8 +25,20 @@ import android.view.View; */ interface BubbleViewProvider { BubbleExpandedView getExpandedView(); + void setContentVisibility(boolean visible); + View getIconView(); + void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index); + String getKey(); + + Bitmap getBadgedImage(); + + int getDotColor(); + + Path getDotPath(); + + boolean showDot(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 60c8c4e10cf0..245d4afbf015 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -157,7 +157,7 @@ public class StackAnimationController extends /** Horizontal offset of bubbles in the stack. */ private float mStackOffset; /** Diameter of the bubble icon. */ - private int mBubbleIconBitmapSize; + private int mBubbleBitmapSize; /** Width of the bubble (icon and padding). */ private int mBubbleSize; /** @@ -263,7 +263,7 @@ public class StackAnimationController extends return false; } - float stackCenter = mStackPosition.x + mBubbleIconBitmapSize / 2; + float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2; float screenCenter = mLayout.getWidth() / 2; return stackCenter < screenCenter; } @@ -306,7 +306,7 @@ public class StackAnimationController extends * @return The X value that the stack will end up at after the fling/spring. */ public float flingStackThenSpringToEdge(float x, float velX, float velY) { - final boolean stackOnLeftSide = x - mBubbleIconBitmapSize / 2 < mLayout.getWidth() / 2; + final boolean stackOnLeftSide = x - mBubbleBitmapSize / 2 < mLayout.getWidth() / 2; final boolean stackShouldFlingLeft = stackOnLeftSide ? velX < ESCAPE_VELOCITY @@ -642,7 +642,7 @@ public class StackAnimationController extends new SpringForce() .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) .setStiffness(SpringForce.STIFFNESS_MEDIUM), - velX, mLayout.getWidth() / 2f - mBubbleIconBitmapSize / 2f); + velX, mLayout.getWidth() / 2f - mBubbleBitmapSize / 2f); springFirstBubbleWithStackFollowing( DynamicAnimation.TRANSLATION_Y, @@ -809,7 +809,7 @@ public class StackAnimationController extends Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); - mBubbleIconBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size); + mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java index 6a64c8386798..f719cc65be71 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java @@ -130,7 +130,8 @@ public class FalsingManagerImpl implements FalsingManager { new KeyguardUpdateMonitorCallback() { @Override public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType) { + BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { if (userId == KeyguardUpdateMonitor.getCurrentUser() && biometricSourceType == BiometricSourceType.FACE) { mJustUnlockedWithFace = true; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index 2f3e3364a50a..a084ae6ed50f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -78,7 +78,8 @@ public class BrightLineFalsingManager implements FalsingManager { new KeyguardUpdateMonitorCallback() { @Override public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType) { + BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { if (userId == KeyguardUpdateMonitor.getCurrentUser() && biometricSourceType == BiometricSourceType.FACE) { mJustUnlockedWithFace = true; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 374153ce2409..2e6c9559d696 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -22,6 +22,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BA import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; import static com.android.systemui.DejankUtils.whitelistIpcs; @@ -538,7 +539,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { } @Override - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { if (mLockPatternUtils.isSecure(userId)) { mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt( userId); @@ -675,6 +677,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) { return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; + } else if (any && (strongAuth + & STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT) != 0) { + return KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT; } return KeyguardSecurityView.PROMPT_REASON_NONE; } @@ -1842,6 +1847,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { mShowKeyguardWakeLock.release(); } mKeyguardDisplayManager.show(); + + // schedule 4hr idle timeout after which non-strong biometrics (i.e. weak or convenience + // biometric) can't be used to unlock device until unlocking with strong biometric or + // primary auth (i.e. PIN/pattern/password) + mLockPatternUtils.scheduleNonStrongBiometricIdleTimeout( + KeyguardUpdateMonitor.getCurrentUser()); + Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index 187a3bdec13f..4f9052709870 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -59,6 +59,7 @@ public class UserDetailView extends PseudoGridView { private final Context mContext; protected UserSwitcherController mController; + private View mCurrentUserView; public Adapter(Context context, UserSwitcherController controller) { super(controller); @@ -89,6 +90,9 @@ public class UserDetailView extends PseudoGridView { v.bind(name, item.picture, item.info.id); } v.setActivated(item.isCurrent); + if (item.isCurrent) { + mCurrentUserView = v; + } v.setDisabledByAdmin(item.isDisabledByAdmin); if (!item.isSwitchToEnabled) { v.setEnabled(false); @@ -107,6 +111,12 @@ public class UserDetailView extends PseudoGridView { mController.startActivity(intent); } else if (tag.isSwitchToEnabled) { MetricsLogger.action(mContext, MetricsEvent.QS_SWITCH_USER); + if (!tag.isAddUser && !tag.isRestricted && !tag.isDisabledByAdmin) { + if (mCurrentUserView != null) { + mCurrentUserView.setActivated(false); + } + view.setActivated(true); + } switchTo(tag); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 9e1e347da5e0..f06cd54f7756 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -497,9 +497,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset flashOutAnimator.addUpdateListener(animation -> mScreenshotFlash.setAlpha((float) animation.getAnimatedValue())); - final PointF startPos = new PointF((float) bounds.left, (float) bounds.top); - final PointF finalPos = new PointF(mScreenshotOffsetXPx, - mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale); + final PointF startPos = new PointF(bounds.centerX(), bounds.centerY()); + final PointF finalPos = new PointF(mScreenshotOffsetXPx + width * cornerScale / 2f, + mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale / 2f); ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1); toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS); @@ -517,11 +517,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset } if (t < xPositionPct) { - mScreenshotView.setX(MathUtils.lerp( - startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct))); + float xCenter = MathUtils.lerp(startPos.x, finalPos.x, + mFastOutSlowIn.getInterpolation(t / xPositionPct)); + mScreenshotView.setX(xCenter - width * mScreenshotView.getScaleX() / 2f); } - mScreenshotView.setY(MathUtils.lerp( - startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t))); + float yCenter = MathUtils.lerp(startPos.y, finalPos.y, + mFastOutSlowIn.getInterpolation(t)); + mScreenshotView.setY(yCenter - height * mScreenshotView.getScaleY() / 2f); }); toCorner.addListener(new AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index 7de70f5f0087..25715212a344 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -281,7 +281,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, mSmartActionsEnabled) - .setAction(Intent.ACTION_SEND), + .setAction(Intent.ACTION_SEND) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM); Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( @@ -310,7 +311,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, mSmartActionsEnabled) - .setAction(Intent.ACTION_EDIT), + .setAction(Intent.ACTION_EDIT) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM); Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( Icon.createWithResource(r, R.drawable.ic_screenshot_edit), @@ -324,7 +326,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()) .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, - mSmartActionsEnabled), + mSmartActionsEnabled) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder( Icon.createWithResource(r, R.drawable.ic_screenshot_delete), @@ -361,9 +364,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { String actionType = extras.getString( ScreenshotNotificationSmartActionsProvider.ACTION_TYPE, ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE); - Intent intent = new Intent(context, - GlobalScreenshot.SmartActionsReceiver.class).putExtra( - GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent); + Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class) + .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled); PendingIntent broadcastIntent = PendingIntent.getBroadcast(context, mRandom.nextInt(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index ab69d477c2ee..4bb8621f9988 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -90,7 +90,7 @@ open class BlurUtils @Inject constructor( } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { - IndentingPrintWriter(pw, " ").use { + IndentingPrintWriter(pw, " ").let { it.println("BlurUtils:") it.increaseIndent() it.println("minBlurRadius: $minBlurRadius") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 7d3d4061014b..4f8e6cfdf767 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -645,7 +645,13 @@ public class KeyguardIndicationController implements StateListener, @Override public void onBiometricHelp(int msgId, String helpString, BiometricSourceType biometricSourceType) { - if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) { + // TODO(b/141025588): refactor to reduce repetition of code/comments + // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong + // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to + // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the + // check of whether non-strong biometric is allowed + if (!mKeyguardUpdateMonitor + .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) { return; } boolean showSwipeToUnlock = @@ -705,13 +711,21 @@ public class KeyguardIndicationController implements StateListener, private boolean shouldSuppressFingerprintError(int msgId, KeyguardUpdateMonitor updateMonitor) { - return ((!updateMonitor.isUnlockingWithBiometricAllowed() + // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong + // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to + // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the + // check of whether non-strong biometric is allowed + return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED); } private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) { - return ((!updateMonitor.isUnlockingWithBiometricAllowed() + // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong + // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to + // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the + // check of whether non-strong biometric is allowed + return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) || msgId == FaceManager.FACE_ERROR_CANCELED); } @@ -745,8 +759,9 @@ public class KeyguardIndicationController implements StateListener, } @Override - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { - super.onBiometricAuthenticated(userId, biometricSourceType); + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric); mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt index 009551168010..48386dce5d3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt @@ -22,6 +22,7 @@ import android.provider.DeviceConfig import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT import com.android.systemui.util.DeviceConfigProxy @@ -45,7 +46,7 @@ class NotificationSectionsFeatureManager @Inject constructor( fun getNotificationBuckets(): IntArray { return when { isFilteringEnabled() -> - intArrayOf(BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) + intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) NotificationUtils.useNewInterruptionModel(context) -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT) else -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index e612c07ac18a..9c942a52b966 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.NotificationSectionsFeatureMa import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationGroupManager @@ -90,12 +91,12 @@ open class NotificationRankingManager @Inject constructor( val bIsHighPriority = b.isHighPriority() when { - usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1 - usePeopleFiltering && aIsImportantPeople != bIsImportantPeople -> - if (aIsImportantPeople) -1 else 1 aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1 // Provide consistent ranking with headsUpManager aHeadsUp -> headsUpManager.compare(a, b) + usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1 + usePeopleFiltering && aIsImportantPeople != bIsImportantPeople -> + if (aIsImportantPeople) -1 else 1 // Upsort current media notification. aMedia != bMedia -> if (aMedia) -1 else 1 // Upsort PRIORITY_MAX system notifications @@ -162,7 +163,9 @@ open class NotificationRankingManager @Inject constructor( isMedia: Boolean, isSystemMax: Boolean ) { - if (usePeopleFiltering && entry.isPeopleNotification()) { + if (usePeopleFiltering && isHeadsUp) { + entry.bucket = BUCKET_HEADS_UP + } else if (usePeopleFiltering && entry.isPeopleNotification()) { entry.bucket = BUCKET_PEOPLE } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) { entry.bucket = BUCKET_ALERTING diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt index efcef7124035..16574abab7aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt @@ -24,8 +24,8 @@ abstract class PeopleHubModule { @Binds abstract fun peopleHubSectionFooterViewAdapter( - impl: PeopleHubSectionFooterViewAdapterImpl - ): PeopleHubSectionFooterViewAdapter + impl: PeopleHubViewAdapterImpl + ): PeopleHubViewAdapter @Binds abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt index ec1d6deb1b8f..e28d03fc8b42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt @@ -25,17 +25,16 @@ import android.provider.Settings import android.view.View import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager import javax.inject.Inject import javax.inject.Singleton /** Boundary between the View and PeopleHub, as seen by the View. */ -interface PeopleHubSectionFooterViewAdapter { - fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) +interface PeopleHubViewAdapter { + fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription } -/** Abstract `View` representation of PeopleHub footer in [NotificationSectionsManager]. */ -interface PeopleHubSectionFooterViewBoundary { +/** Abstract `View` representation of PeopleHub. */ +interface PeopleHubViewBoundary { /** View used for animating the activity launch caused by clicking a person in the hub. */ val associatedViewForClickAnimation: View @@ -57,23 +56,22 @@ interface PeopleHubViewModelFactory { } /** - * Wraps a [PeopleHubSectionFooterViewBoundary] in a [DataListener], and connects it to the data + * Wraps a [PeopleHubViewBoundary] in a [DataListener], and connects it to the data * pipeline. * * @param dataSource PeopleHub data pipeline. */ @Singleton -class PeopleHubSectionFooterViewAdapterImpl @Inject constructor( +class PeopleHubViewAdapterImpl @Inject constructor( private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory> -) : PeopleHubSectionFooterViewAdapter { +) : PeopleHubViewAdapter { - override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) { - dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary)) - } + override fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription = + dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary)) } private class PeopleHubDataListenerImpl( - private val viewBoundary: PeopleHubSectionFooterViewBoundary + private val viewBoundary: PeopleHubViewBoundary ) : DataListener<PeopleHubViewModelFactory> { override fun onDataChanged(data: PeopleHubViewModelFactory) { @@ -92,7 +90,7 @@ private class PeopleHubDataListenerImpl( * Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s. * * This class serves as the glue between the View layer (which depends on - * [PeopleHubSectionFooterViewBoundary]) and the Data layer (which produces [PeopleHubModel]s). + * [PeopleHubViewBoundary]) and the Data layer (which produces [PeopleHubModel]s). */ @Singleton class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index a0fef0068b05..e79d89f3a45c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -67,29 +67,34 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int givenSize = MeasureSpec.getSize(heightMeasureSpec); + final int givenHeight = MeasureSpec.getSize(heightMeasureSpec); final int viewHorizontalPadding = getPaddingStart() + getPaddingEnd(); + + // Max height is as large as possible, unless otherwise requested int ownMaxHeight = Integer.MAX_VALUE; int heightMode = MeasureSpec.getMode(heightMeasureSpec); - if (heightMode != MeasureSpec.UNSPECIFIED && givenSize != 0) { - ownMaxHeight = Math.min(givenSize, ownMaxHeight); + if (heightMode != MeasureSpec.UNSPECIFIED && givenHeight != 0) { + // Set our max height to what was requested from the parent + ownMaxHeight = Math.min(givenHeight, ownMaxHeight); } - int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST); + + // height of the largest child int maxChildHeight = 0; + int atMostOwnMaxHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } - int childHeightSpec = newHeightSpec; + int childHeightSpec = atMostOwnMaxHeightSpec; ViewGroup.LayoutParams layoutParams = child.getLayoutParams(); if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) { if (layoutParams.height >= 0) { - // An actual height is set - childHeightSpec = layoutParams.height > ownMaxHeight - ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY) - : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY); + // If an actual height is set, cap it to the max height + childHeightSpec = MeasureSpec.makeMeasureSpec( + Math.min(layoutParams.height, ownMaxHeight), + MeasureSpec.EXACTLY); } child.measure(getChildMeasureSpec( widthMeasureSpec, viewHorizontalPadding, layoutParams.width), @@ -100,15 +105,22 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { mMatchParentViews.add(child); } } + + // Set our own height to the given height, or the height of the largest child int ownHeight = heightMode == MeasureSpec.EXACTLY - ? givenSize : Math.min(ownMaxHeight, maxChildHeight); - newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY); + ? givenHeight + : Math.min(ownMaxHeight, maxChildHeight); + int exactlyOwnHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY); + + // Now that we know our own height, measure the children that are MATCH_PARENT for (View child : mMatchParentViews) { child.measure(getChildMeasureSpec( widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width), - newHeightSpec); + exactlyOwnHeightSpec); } mMatchParentViews.clear(); + + // Finish up int width = MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(width, ownHeight); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index 23433cb1682d..b3561c2deda7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -16,11 +16,10 @@ package com.android.systemui.statusbar.notification.stack; -import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; - import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; @@ -35,18 +34,21 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.people.DataListener; -import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; -import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewBoundary; +import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter; +import com.android.systemui.statusbar.notification.people.PeopleHubViewBoundary; import com.android.systemui.statusbar.notification.people.PersonViewModel; +import com.android.systemui.statusbar.notification.people.Subscription; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; +import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.inject.Inject; @@ -63,63 +65,65 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section private static final String TAG = "NotifSectionsManager"; private static final boolean DEBUG = false; - private NotificationStackScrollLayout mParent; private final ActivityStarter mActivityStarter; private final StatusBarStateController mStatusBarStateController; private final ConfigurationController mConfigurationController; - private final int mNumberOfSections; + private final PeopleHubViewAdapter mPeopleHubViewAdapter; private final NotificationSectionsFeatureManager mSectionsFeatureManager; - private final NotificationRowComponent.Builder mNotificationRowComponentBuilder; - private boolean mInitialized = false; - - private SectionHeaderView mGentleHeader; - private boolean mGentleHeaderVisible = false; + private final int mNumberOfSections; - private boolean mPeopleHubVisible = false; - private PeopleHubView mPeopleHubView; - private final PeopleHubSectionFooterViewAdapter mPeopleHubViewAdapter; - private final PeopleHubSectionFooterViewBoundary mPeopleHubViewBoundary = - new PeopleHubSectionFooterViewBoundary() { - @Override - public void setVisible(boolean isVisible) { - if (mPeopleHubVisible != isVisible) { - mPeopleHubVisible = isVisible; - if (mInitialized) { - updateSectionBoundaries(); - } - } + private final PeopleHubViewBoundary mPeopleHubViewBoundary = new PeopleHubViewBoundary() { + @Override + public void setVisible(boolean isVisible) { + if (mPeopleHubVisible != isVisible) { + mPeopleHubVisible = isVisible; + if (mInitialized) { + updateSectionBoundaries(); } + } + } - @NonNull - @Override - public View getAssociatedViewForClickAnimation() { - return mPeopleHubView; - } + @NonNull + @Override + public View getAssociatedViewForClickAnimation() { + return mPeopleHubView; + } - @NonNull - @Override - public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() { - return mPeopleHubView.getPersonViewAdapters(); - } - }; + @NonNull + @Override + public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() { + return mPeopleHubView.getPersonViewAdapters(); + } + }; + + private NotificationStackScrollLayout mParent; + private boolean mInitialized = false; + private SectionHeaderView mGentleHeader; + private boolean mGentleHeaderVisible; @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; + private SectionHeaderView mAlertingHeader; + private boolean mAlertingHeaderVisible; + + private PeopleHubView mPeopleHubView; + private boolean mPeopleHeaderVisible; + private boolean mPeopleHubVisible = false; + @Nullable private Subscription mPeopleHubSubscription; + @Inject NotificationSectionsManager( ActivityStarter activityStarter, StatusBarStateController statusBarStateController, ConfigurationController configurationController, - PeopleHubSectionFooterViewAdapter peopleHubViewAdapter, - NotificationSectionsFeatureManager sectionsFeatureManager, - NotificationRowComponent.Builder notificationRowComponentBuilder) { + PeopleHubViewAdapter peopleHubViewAdapter, + NotificationSectionsFeatureManager sectionsFeatureManager) { mActivityStarter = activityStarter; mStatusBarStateController = statusBarStateController; mConfigurationController = configurationController; mPeopleHubViewAdapter = peopleHubViewAdapter; mSectionsFeatureManager = sectionsFeatureManager; mNumberOfSections = mSectionsFeatureManager.getNumberOfBuckets(); - mNotificationRowComponentBuilder = notificationRowComponentBuilder; } NotificationSection[] createSectionsForBuckets() { @@ -141,105 +145,81 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section mInitialized = true; mParent = parent; reinflateViews(layoutInflater); - mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary); mConfigurationController.addCallback(mConfigurationListener); } - /** - * Reinflates the entire notification header, including all decoration views. - */ - void reinflateViews(LayoutInflater layoutInflater) { - int oldGentleHeaderPos = -1; - int oldPeopleHubPos = -1; - if (mGentleHeader != null) { - if (mGentleHeader.getTransientContainer() != null) { - mGentleHeader.getTransientContainer().removeView(mGentleHeader); - } else if (mGentleHeader.getParent() != null) { - oldGentleHeaderPos = mParent.indexOfChild(mGentleHeader); - mParent.removeView(mGentleHeader); + private <T extends ExpandableView> T reinflateView( + T view, LayoutInflater layoutInflater, @LayoutRes int layoutResId) { + int oldPos = -1; + if (view != null) { + if (view.getTransientContainer() != null) { + view.getTransientContainer().removeView(mGentleHeader); + } else if (view.getParent() != null) { + oldPos = mParent.indexOfChild(view); + mParent.removeView(view); } } - if (mPeopleHubView != null) { - if (mPeopleHubView.getTransientContainer() != null) { - mPeopleHubView.getTransientContainer().removeView(mPeopleHubView); - } else if (mPeopleHubView.getParent() != null) { - oldPeopleHubPos = mParent.indexOfChild(mPeopleHubView); - mParent.removeView(mPeopleHubView); - } + + view = (T) layoutInflater.inflate(layoutResId, mParent, false); + + if (oldPos != -1) { + mParent.addView(view, oldPos); } - mGentleHeader = (SectionHeaderView) layoutInflater.inflate( - R.layout.status_bar_notification_section_header, mParent, false); - NotificationRowComponent sectionHeaderComponent = mNotificationRowComponentBuilder - .activatableNotificationView(mGentleHeader) - .build(); - sectionHeaderComponent.getActivatableNotificationViewController().init(); + return view; + } + /** + * Reinflates the entire notification header, including all decoration views. + */ + void reinflateViews(LayoutInflater layoutInflater) { + mGentleHeader = reinflateView( + mGentleHeader, layoutInflater, R.layout.status_bar_notification_section_header); + mGentleHeader.setHeaderText(R.string.notification_section_header_gentle); mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick); mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick); - if (oldGentleHeaderPos != -1) { - mParent.addView(mGentleHeader, oldGentleHeaderPos); - } - - mPeopleHubView = (PeopleHubView) layoutInflater.inflate( - R.layout.people_strip, mParent, false); + mAlertingHeader = reinflateView( + mAlertingHeader, layoutInflater, R.layout.status_bar_notification_section_header); + mAlertingHeader.setHeaderText(R.string.notification_section_header_alerting); + mAlertingHeader.setOnHeaderClickListener(this::onGentleHeaderClick); - NotificationRowComponent notificationRowComponent = mNotificationRowComponentBuilder - .activatableNotificationView(mPeopleHubView) - .build(); - notificationRowComponent.getActivatableNotificationViewController().init(); - - if (oldPeopleHubPos != -1) { - mParent.addView(mPeopleHubView, oldPeopleHubPos); + if (mPeopleHubSubscription != null) { + mPeopleHubSubscription.unsubscribe(); } + mPeopleHubView = reinflateView(mPeopleHubView, layoutInflater, R.layout.people_strip); + mPeopleHubSubscription = mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary); } - /** Listener for when the "clear all" buttton is clciked on the gentle notification header. */ + /** Listener for when the "clear all" button is clicked on the gentle notification header. */ void setOnClearGentleNotifsClickListener(View.OnClickListener listener) { mOnClearGentleNotifsClickListener = listener; } - /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */ - void onUiModeChanged() { - mGentleHeader.onUiModeChanged(); - } - @Override public boolean beginsSection(@NonNull View view, @Nullable View previous) { - boolean begin = false; - if (view instanceof ActivatableNotificationView) { - if (previous instanceof ActivatableNotificationView) { - // If we're drawing the first non-person notification, break out a section - ActivatableNotificationView curr = (ActivatableNotificationView) view; - ActivatableNotificationView prev = (ActivatableNotificationView) previous; - - begin = getBucket(curr) != getBucket(prev); - } - } - - if (!begin) { - begin = view == mGentleHeader || view == mPeopleHubView; - } - - return begin; + return view == mGentleHeader + || view == mPeopleHubView + || view == mAlertingHeader + || !Objects.equals(getBucket(view), getBucket(previous)); } private boolean isUsingMultipleSections() { return mNumberOfSections > 1; } - private @PriorityBucket int getBucket(ActivatableNotificationView view) - throws IllegalArgumentException { - if (view instanceof ExpandableNotificationRow) { - return ((ExpandableNotificationRow) view).getEntry().getBucket(); - } else if (view == mGentleHeader) { + @Nullable + private Integer getBucket(View view) { + if (view == mGentleHeader) { return BUCKET_SILENT; } else if (view == mPeopleHubView) { return BUCKET_PEOPLE; + } else if (view == mAlertingHeader) { + return BUCKET_ALERTING; + } else if (view instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) view).getEntry().getBucket(); } - - throw new IllegalArgumentException("I don't know how to find a bucket for this view :("); + return null; } /** @@ -251,118 +231,104 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section return; } - boolean peopleNotificationsPresent = false; - int firstNonHeadsUpIndex = -1; - int firstGentleIndex = -1; - int notifCount = 0; + final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD; + final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled(); - final int n = mParent.getChildCount(); - for (int i = 0; i < n; i++) { - View child = mParent.getChildAt(i); - if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) { - notifCount++; - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (firstNonHeadsUpIndex == -1 && !row.isHeadsUp()) { - firstNonHeadsUpIndex = i; + boolean peopleNotifsPresent = false; + int peopleHeaderTarget = -1; + int alertingHeaderTarget = -1; + int gentleHeaderTarget = -1; + + int viewCount = 0; + + if (showHeaders) { + final int childCount = mParent.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mParent.getChildAt(i); + if (child.getVisibility() == View.GONE + || !(child instanceof ExpandableNotificationRow)) { + continue; } - if (row.getEntry().getBucket() == BUCKET_PEOPLE) { - peopleNotificationsPresent = true; + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + switch (row.getEntry().getBucket()) { + case BUCKET_PEOPLE: + if (peopleHeaderTarget == -1) { + peopleNotifsPresent = true; + peopleHeaderTarget = viewCount; + viewCount++; + } + break; + case BUCKET_ALERTING: + if (usingPeopleFiltering && alertingHeaderTarget == -1) { + alertingHeaderTarget = viewCount; + viewCount++; + } + break; + case BUCKET_SILENT: + if (gentleHeaderTarget == -1) { + gentleHeaderTarget = viewCount; + viewCount++; + } + break; } - if (row.getEntry().getBucket() == BUCKET_SILENT) { - firstGentleIndex = i; - break; + viewCount++; + } + if (usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) { + // Insert the people header even if there are no people visible, in order to show + // the hub. Put it directly above the next header. + if (alertingHeaderTarget != -1) { + peopleHeaderTarget = alertingHeaderTarget; + alertingHeaderTarget++; + gentleHeaderTarget++; + } else if (gentleHeaderTarget != -1) { + peopleHeaderTarget = gentleHeaderTarget; + gentleHeaderTarget++; + } else { + // Put it at the end of the list. + peopleHeaderTarget = viewCount; } } } - if (firstNonHeadsUpIndex == -1) { - firstNonHeadsUpIndex = firstGentleIndex != -1 ? firstGentleIndex : notifCount; - } - - // make room for peopleHub - int offset = adjustPeopleHubVisibilityAndPosition( - firstNonHeadsUpIndex, peopleNotificationsPresent); - if (firstGentleIndex != -1) { - firstGentleIndex += offset; - } - - adjustGentleHeaderVisibilityAndPosition(firstGentleIndex); + // Allow swiping the people header if the section is empty + mPeopleHubView.setCanSwipe(mPeopleHubVisible && !peopleNotifsPresent); - mGentleHeader.setAreThereDismissableGentleNotifs( - mParent.hasActiveClearableNotifications(ROWS_GENTLE)); + mPeopleHeaderVisible = adjustHeaderVisibilityAndPosition( + peopleHeaderTarget, mPeopleHubView, mPeopleHeaderVisible); + mAlertingHeaderVisible = adjustHeaderVisibilityAndPosition( + alertingHeaderTarget, mAlertingHeader, mAlertingHeaderVisible); + mGentleHeaderVisible = adjustHeaderVisibilityAndPosition( + gentleHeaderTarget, mGentleHeader, mGentleHeaderVisible); } - private void adjustGentleHeaderVisibilityAndPosition(int firstGentleNotifIndex) { - final boolean showGentleHeader = - firstGentleNotifIndex != -1 - && mStatusBarStateController.getState() != StatusBarState.KEYGUARD; - final int currentHeaderIndex = mParent.indexOfChild(mGentleHeader); - - if (!showGentleHeader) { - if (mGentleHeaderVisible) { - mGentleHeaderVisible = false; - mParent.removeView(mGentleHeader); + private boolean adjustHeaderVisibilityAndPosition( + int targetIndex, StackScrollerDecorView header, boolean isCurrentlyVisible) { + if (targetIndex == -1) { + if (isCurrentlyVisible) { + mParent.removeView(header); } + return false; } else { - if (!mGentleHeaderVisible) { - mGentleHeaderVisible = true; + if (header instanceof SwipeableView) { + ((SwipeableView) header).resetTranslation(); + } + if (!isCurrentlyVisible) { // If the header is animating away, it will still have a parent, so detach it first // TODO: We should really cancel the active animations here. This will happen // automatically when the view's intro animation starts, but it's a fragile link. - if (mGentleHeader.getTransientContainer() != null) { - mGentleHeader.getTransientContainer().removeTransientView(mGentleHeader); - mGentleHeader.setTransientContainer(null); + if (header.getTransientContainer() != null) { + header.getTransientContainer().removeTransientView(header); + header.setTransientContainer(null); } - mParent.addView(mGentleHeader, firstGentleNotifIndex); - } else if (currentHeaderIndex != firstGentleNotifIndex - 1) { - // Relocate the header to be immediately before the first child in the section - int targetIndex = firstGentleNotifIndex; - if (currentHeaderIndex < firstGentleNotifIndex) { - // Adjust the target index to account for the header itself being temporarily - // removed during the position change. - targetIndex--; - } - - mParent.changeViewPosition(mGentleHeader, targetIndex); + header.setContentVisible(true); + mParent.addView(header, targetIndex); + } else if (mParent.indexOfChild(header) != targetIndex) { + mParent.changeViewPosition(header, targetIndex); } + return true; } } - private int adjustPeopleHubVisibilityAndPosition( - int targetIndex, boolean peopleNotificationsPresent) { - final boolean showPeopleHeader = mNumberOfSections > 2 - && mStatusBarStateController.getState() != StatusBarState.KEYGUARD - && (peopleNotificationsPresent || mPeopleHubVisible); - final int currentHubIndex = mParent.indexOfChild(mPeopleHubView); - final boolean currentlyVisible = currentHubIndex >= 0; - - mPeopleHubView.setCanSwipe(showPeopleHeader && !peopleNotificationsPresent); - - if (!showPeopleHeader) { - if (currentlyVisible) { - mParent.removeView(mPeopleHubView); - return -1; - } - } else { - mPeopleHubView.unDismiss(); - mPeopleHubView.resetTranslation(); - if (!currentlyVisible) { - if (mPeopleHubView.getTransientContainer() != null) { - mPeopleHubView.getTransientContainer().removeTransientView(mPeopleHubView); - mPeopleHubView.setTransientContainer(null); - } - mParent.addView(mPeopleHubView, targetIndex); - return 1; - } else if (currentHubIndex != targetIndex) { - if (currentHubIndex < targetIndex) { - targetIndex--; - } - mParent.changeViewPosition(mPeopleHubView, targetIndex); - } - } - return 0; - } - /** * Updates the boundaries (as tracked by their first and last views) of the priority sections. * @@ -388,7 +354,12 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section //TODO: do this in a single pass, and more better for (ActivatableNotificationView v : children) { - if (getBucket(v) == filter) { + Integer bucket = getBucket(v); + if (bucket == null) { + throw new IllegalArgumentException("Cannot find section bucket for view"); + } + + if (bucket == filter) { viewsInBucket.add(v); } @@ -463,16 +434,17 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section /** * For now, declare the available notification buckets (sections) here so that other * presentation code can decide what to do based on an entry's buckets - * */ @Retention(SOURCE) @IntDef(prefix = { "BUCKET_" }, value = { + BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT }) public @interface PriorityBucket {} - public static final int BUCKET_PEOPLE = 0; - public static final int BUCKET_ALERTING = 1; - public static final int BUCKET_SILENT = 2; + public static final int BUCKET_HEADS_UP = 0; + public static final int BUCKET_PEOPLE = 1; + public static final int BUCKET_ALERTING = 2; + public static final int BUCKET_SILENT = 3; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 2eeda1f48df3..1bd9bbecc26e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -815,7 +815,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mBgColor = mContext.getColor(R.color.notification_shade_background_color); updateBackgroundDimming(); mShelf.onUiModeChanged(); - mSectionsManager.onUiModeChanged(); } @ShadeViewRefactor(RefactorComponent.DECORATOR) @@ -1632,8 +1631,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.COORDINATOR) private ExpandableView getChildAtPosition(float touchX, float touchY) { - return getChildAtPosition(touchX, touchY, true /* requireMinHeight */); - + return getChildAtPosition( + touchX, touchY, true /* requireMinHeight */, true /* ignoreDecors */); } /** @@ -1642,17 +1641,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * @param touchX the x coordinate * @param touchY the y coordinate * @param requireMinHeight Whether a minimum height is required for a child to be returned. + * @param ignoreDecors Whether decors can be returned * @return the child at the given location. */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) private ExpandableView getChildAtPosition(float touchX, float touchY, - boolean requireMinHeight) { + boolean requireMinHeight, boolean ignoreDecors) { // find the view under the pointer, accounting for GONE views final int count = getChildCount(); for (int childIdx = 0; childIdx < count; childIdx++) { ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx); if (slidingChild.getVisibility() != VISIBLE - || slidingChild instanceof StackScrollerDecorView) { + || (ignoreDecors && slidingChild instanceof StackScrollerDecorView)) { continue; } float childTop = slidingChild.getTranslationY(); @@ -4166,7 +4166,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd case MotionEvent.ACTION_DOWN: { final int y = (int) ev.getY(); mScrolledToTopOnFirstDown = isScrolledToTop(); - if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) { + final ExpandableView childAtTouchPos = getChildAtPosition( + ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */); + if (childAtTouchPos == null) { setIsBeingDragged(false); recycleVelocityTracker(); break; @@ -6299,8 +6301,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (view instanceof PeopleHubView) { - PeopleHubView row = (PeopleHubView) view; - row.dismiss(false); mSectionsManager.hidePeopleRow(); } @@ -6325,8 +6325,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public View getChildAtPosition(MotionEvent ev) { - View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(), - ev.getY()); + View child = NotificationStackScrollLayout.this.getChildAtPosition( + ev.getX(), + ev.getY(), + true /* requireMinHeight */, + false /* ignoreDecors */); if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; ExpandableNotificationRow parent = row.getNotificationParent(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt index e5717aeefdcb..151c6b272a3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt @@ -25,30 +25,32 @@ import com.android.systemui.R import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin import com.android.systemui.statusbar.notification.people.DataListener import com.android.systemui.statusbar.notification.people.PersonViewModel -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView +import com.android.systemui.statusbar.notification.row.StackScrollerDecorView class PeopleHubView(context: Context, attrs: AttributeSet) : - ActivatableNotificationView(context, attrs), SwipeableView { + StackScrollerDecorView(context, attrs), SwipeableView { private lateinit var contents: ViewGroup - private lateinit var personControllers: List<PersonDataListenerImpl> - val personViewAdapters: Sequence<DataListener<PersonViewModel?>> - get() = personControllers.asSequence() + lateinit var personViewAdapters: Sequence<DataListener<PersonViewModel?>> + private set override fun onFinishInflate() { - super.onFinishInflate() contents = requireViewById(R.id.people_list) - personControllers = (0 until contents.childCount) + personViewAdapters = (0 until contents.childCount) .reversed() .asSequence() .mapNotNull { idx -> (contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl) } .toList() + .asSequence() + super.onFinishInflate() + setVisible(true /* nowVisible */, false /* animate */) } - override fun getContentView(): View = contents + override fun findContentView(): View = contents + override fun findSecondaryView(): View? = null override fun hasFinishedInitialization(): Boolean = true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java index add982dabf02..ad3ff69eb5c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.stack; import android.annotation.Nullable; +import android.annotation.StringRes; import android.content.Context; -import android.graphics.RectF; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -28,7 +28,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import java.util.Objects; @@ -36,23 +36,22 @@ import java.util.Objects; * Similar in size and appearance to the NotificationShelf, appears at the beginning of some * notification sections. Currently only used for gentle notifications. */ -public class SectionHeaderView extends ActivatableNotificationView { +public class SectionHeaderView extends StackScrollerDecorView { private ViewGroup mContents; private TextView mLabelView; private ImageView mClearAllButton; @Nullable private View.OnClickListener mOnClearClickListener = null; - private final RectF mTmpRect = new RectF(); - public SectionHeaderView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onFinishInflate() { - super.onFinishInflate(); mContents = Objects.requireNonNull(findViewById(R.id.content)); bindContents(); + super.onFinishInflate(); + setVisible(true /* nowVisible */, false /* animate */); } private void bindContents() { @@ -64,15 +63,20 @@ public class SectionHeaderView extends ActivatableNotificationView { } @Override - protected View getContentView() { + protected View findContentView() { return mContents; } + @Override + protected View findSecondaryView() { + return null; + } + /** * Destroys and reinflates the visible contents of the section header. For use on configuration * changes or any other time that layout values might need to be re-evaluated. * - * Does not reinflate the base content view itself ({@link #getContentView()} or any of the + * Does not reinflate the base content view itself ({@link #findContentView()} or any of the * decorator views, such as the background view or shadow view. */ void reinflateContents() { @@ -88,35 +92,20 @@ public class SectionHeaderView extends ActivatableNotificationView { return true; } - /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */ - void onUiModeChanged() { - updateBackgroundColors(); - mLabelView.setTextColor( - getContext().getColor(R.color.notification_section_header_label_color)); - mClearAllButton.setImageResource( - R.drawable.status_bar_notification_section_header_clear_btn); - } - void setAreThereDismissableGentleNotifs(boolean areThereDismissableGentleNotifs) { mClearAllButton.setVisibility(areThereDismissableGentleNotifs ? View.VISIBLE : View.GONE); } @Override - protected boolean disallowSingleClick(MotionEvent event) { - // Disallow single click on lockscreen if user is tapping on clear all button - mTmpRect.set( - mClearAllButton.getLeft(), - mClearAllButton.getTop(), - mClearAllButton.getLeft() + mClearAllButton.getWidth(), - mClearAllButton.getTop() + mClearAllButton.getHeight()); - return mTmpRect.contains(event.getX(), event.getY()); + public boolean onInterceptTouchEvent(MotionEvent ev) { + return super.onInterceptTouchEvent(ev); } /** * Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything). */ void setOnHeaderClickListener(View.OnClickListener listener) { - mContents.setOnClickListener(listener); + mLabelView.setOnClickListener(listener); } /** Fired when the user clicks on the "X" button on the far right of the header. */ @@ -124,4 +113,8 @@ public class SectionHeaderView extends ActivatableNotificationView { mOnClearClickListener = listener; mClearAllButton.setOnClickListener(listener); } + + void setHeaderText(@StringRes int resId) { + mLabelView.setText(resId); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 691e1c422bfe..1dde5c03a627 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -150,14 +150,26 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private KeyguardViewMediator mKeyguardViewMediator; private ScrimController mScrimController; private StatusBar mStatusBar; - private int mPendingAuthenticatedUserId = -1; - private BiometricSourceType mPendingAuthenticatedBioSourceType = null; + private PendingAuthenticated mPendingAuthenticated = null; private boolean mPendingShowBouncer; private boolean mHasScreenTurnedOnSinceAuthenticating; private boolean mFadedAwayAfterWakeAndUnlock; private final MetricsLogger mMetricsLogger; + private static final class PendingAuthenticated { + public final int userId; + public final BiometricSourceType biometricSourceType; + public final boolean isStrongBiometric; + + PendingAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + this.userId = userId; + this.biometricSourceType = biometricSourceType; + this.isStrongBiometric = isStrongBiometric; + } + } + @Inject public BiometricUnlockController(Context context, DozeScrimController dozeScrimController, KeyguardViewMediator keyguardViewMediator, ScrimController scrimController, @@ -251,28 +263,30 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } @Override - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated"); if (mUpdateMonitor.isGoingToSleep()) { - mPendingAuthenticatedUserId = userId; - mPendingAuthenticatedBioSourceType = biometricSourceType; + mPendingAuthenticated = new PendingAuthenticated(userId, biometricSourceType, + isStrongBiometric); Trace.endSection(); return; } mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH) .setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType))); boolean unlockAllowed = mKeyguardBypassController.onBiometricAuthenticated( - biometricSourceType); + biometricSourceType, isStrongBiometric); if (unlockAllowed) { mKeyguardViewMediator.userActivity(); - startWakeAndUnlock(biometricSourceType); + startWakeAndUnlock(biometricSourceType, isStrongBiometric); } else { Log.d(TAG, "onBiometricAuthenticated aborted by bypass controller"); } } - public void startWakeAndUnlock(BiometricSourceType biometricSourceType) { - startWakeAndUnlock(calculateMode(biometricSourceType)); + public void startWakeAndUnlock(BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + startWakeAndUnlock(calculateMode(biometricSourceType, isStrongBiometric)); } public void startWakeAndUnlock(@WakeAndUnlockMode int mode) { @@ -373,45 +387,46 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp public void onStartedGoingToSleep(int why) { resetMode(); mFadedAwayAfterWakeAndUnlock = false; - mPendingAuthenticatedUserId = -1; - mPendingAuthenticatedBioSourceType = null; + mPendingAuthenticated = null; } @Override public void onFinishedGoingToSleep(int why) { Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep"); - BiometricSourceType pendingType = mPendingAuthenticatedBioSourceType; - int pendingUserId = mPendingAuthenticatedUserId; - if (pendingUserId != -1 && pendingType != null) { + if (mPendingAuthenticated != null) { // Post this to make sure it's executed after the device is fully locked. - mHandler.post(() -> onBiometricAuthenticated(pendingUserId, pendingType)); + mHandler.post(() -> onBiometricAuthenticated(mPendingAuthenticated.userId, + mPendingAuthenticated.biometricSourceType, + mPendingAuthenticated.isStrongBiometric)); + mPendingAuthenticated = null; } - mPendingAuthenticatedUserId = -1; - mPendingAuthenticatedBioSourceType = null; Trace.endSection(); } public boolean hasPendingAuthentication() { - return mPendingAuthenticatedUserId != -1 - && mUpdateMonitor.isUnlockingWithBiometricAllowed() - && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser(); + return mPendingAuthenticated != null + && mUpdateMonitor + .isUnlockingWithBiometricAllowed(mPendingAuthenticated.isStrongBiometric) + && mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser(); } public int getMode() { return mMode; } - private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType) { + private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { if (biometricSourceType == BiometricSourceType.FACE || biometricSourceType == BiometricSourceType.IRIS) { - return calculateModeForPassiveAuth(); + return calculateModeForPassiveAuth(isStrongBiometric); } else { - return calculateModeForFingerprint(); + return calculateModeForFingerprint(isStrongBiometric); } } - private @WakeAndUnlockMode int calculateModeForFingerprint() { - boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed(); + private @WakeAndUnlockMode int calculateModeForFingerprint(boolean isStrongBiometric) { + boolean unlockingAllowed = + mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric); boolean deviceDreaming = mUpdateMonitor.isDreaming(); if (!mUpdateMonitor.isDeviceInteractive()) { @@ -440,8 +455,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp return MODE_NONE; } - private @WakeAndUnlockMode int calculateModeForPassiveAuth() { - boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed(); + private @WakeAndUnlockMode int calculateModeForPassiveAuth(boolean isStrongBiometric) { + boolean unlockingAllowed = + mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric); boolean deviceDreaming = mUpdateMonitor.isDreaming(); boolean bypass = mKeyguardBypassController.getBypassEnabled(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index b4d0d479ff39..03918e09be5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -38,11 +38,20 @@ open class KeyguardBypassController : Dumpable { private val mKeyguardStateController: KeyguardStateController private val statusBarStateController: StatusBarStateController private var hasFaceFeature: Boolean + private var pendingUnlock: PendingUnlock? = null /** + * Pending unlock info: + * * The pending unlock type which is set if the bypass was blocked when it happened. + * + * Whether the pending unlock type is strong biometric or non-strong biometric + * (i.e. weak or convenience). */ - private var pendingUnlockType: BiometricSourceType? = null + private data class PendingUnlock( + val pendingUnlockType: BiometricSourceType, + val isStrongBiometric: Boolean + ) lateinit var unlockController: BiometricUnlockController var isPulseExpanding = false @@ -86,7 +95,7 @@ open class KeyguardBypassController : Dumpable { statusBarStateController.addCallback(object : StatusBarStateController.StateListener { override fun onStateChanged(newState: Int) { if (newState != StatusBarState.KEYGUARD) { - pendingUnlockType = null + pendingUnlock = null } } }) @@ -101,7 +110,7 @@ open class KeyguardBypassController : Dumpable { lockscreenUserManager.addUserChangedListener( object : NotificationLockscreenUserManager.UserChangedListener { override fun onUserChanged(userId: Int) { - pendingUnlockType = null + pendingUnlock = null } }) } @@ -111,11 +120,14 @@ open class KeyguardBypassController : Dumpable { * * @return false if we can not wake and unlock right now */ - fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean { + fun onBiometricAuthenticated( + biometricSourceType: BiometricSourceType, + isStrongBiometric: Boolean + ): Boolean { if (bypassEnabled) { val can = canBypass() if (!can && (isPulseExpanding || qSExpanded)) { - pendingUnlockType = biometricSourceType + pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric) } return can } @@ -123,10 +135,12 @@ open class KeyguardBypassController : Dumpable { } fun maybePerformPendingUnlock() { - if (pendingUnlockType != null) { - if (onBiometricAuthenticated(pendingUnlockType!!)) { - unlockController.startWakeAndUnlock(pendingUnlockType) - pendingUnlockType = null + if (pendingUnlock != null) { + if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType, + pendingUnlock!!.isStrongBiometric)) { + unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType, + pendingUnlock!!.isStrongBiometric) + pendingUnlock = null } } } @@ -162,12 +176,17 @@ open class KeyguardBypassController : Dumpable { } fun onStartedGoingToSleep() { - pendingUnlockType = null + pendingUnlock = null } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { pw.println("KeyguardBypassController:") - pw.println(" pendingUnlockType: $pendingUnlockType") + if (pendingUnlock != null) { + pw.println(" mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}") + pw.println(" mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}") + } else { + pw.println(" mPendingUnlock: $pendingUnlock") + } pw.println(" bypassEnabled: $bypassEnabled") pw.println(" canBypass: ${canBypass()}") pw.println(" bouncerShowing: $bouncerShowing") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 60589843002e..61cef6827bd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -387,7 +387,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); - boolean unlockingAllowed = mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(); + // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong + // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to + // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the + // check of whether non-strong biometric is allowed + boolean unlockingAllowed = mKeyguardUpdateMonitor + .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */); if (fingerprintRunning && unlockingAllowed) { AccessibilityNodeInfo.AccessibilityAction unlock = new AccessibilityNodeInfo.AccessibilityAction( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index fb7976ff19a8..c61d7bbf67c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -202,8 +202,10 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType) { - if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) { + BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + if (mFirstBypassAttempt + && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) { mDelayShowingKeyguardStatusBar = true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 10821d63d4cc..945a9db7c836 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -21,7 +21,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.app.AlarmManager; -import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -45,7 +44,6 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; @@ -116,7 +114,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo * A scrim varies its opacity based on a busyness factor, for example * how many notifications are currently visible. */ - public static final float BUSY_SCRIM_ALPHA = 0.54f; + public static final float BUSY_SCRIM_ALPHA = 0.75f; /** * The most common scrim, the one under the keyguard. @@ -146,8 +144,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private GradientColors mColors; private boolean mNeedsDrawableColorUpdate; - private float mScrimBehindAlpha; - private float mScrimBehindAlphaResValue; private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; // Assuming the shade is expanded during initialization @@ -192,7 +188,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo @Inject public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, - @Main Resources resources, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor, DockManager dockManager) { @@ -203,14 +198,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardVisibilityCallback = new KeyguardVisibilityCallback(); - mScrimBehindAlphaResValue = resources.getFloat(R.dimen.scrim_behind_alpha); mHandler = handler; mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, "hide_aod_wallpaper", mHandler); mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build(); // Scrim alpha is initially set to the value on the resource but might be changed // to make sure that text on top of it is legible. - mScrimBehindAlpha = mScrimBehindAlphaResValue; mDozeParameters = dozeParameters; mDockManager = dockManager; keyguardStateController.addCallback(new KeyguardStateController.Callback() { @@ -587,7 +580,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo int mainColor = mColors.getMainColor(); float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor, 4.5f /* minimumContrast */) / 255f; - mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity); dispatchScrimState(mScrimBehind.getViewAlpha()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java index ba55f2ddcf13..c404cf69f1a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java @@ -74,6 +74,12 @@ public class UserAvatarView extends View { this(context, null); } + @Override + public void setActivated(boolean activated) { + super.setActivated(activated); + mDrawable.invalidateSelf(); + } + /** * @deprecated use {@link #setAvatar(Bitmap)} instead. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 0ab08a8fd168..a7f60d64c332 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -293,13 +293,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated"); - if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) { - Trace.endSection(); - return; + if (mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) { + update(false /* updateAlways */); } - update(false /* updateAlways */); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index d28a66994e1d..2abae3e26013 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -257,6 +257,7 @@ public class KeyguardUserSwitcher { private Context mContext; private KeyguardUserSwitcher mKeyguardUserSwitcher; + private View mCurrentUserView; public Adapter(Context context, UserSwitcherController controller, KeyguardUserSwitcher kgu) { @@ -285,6 +286,9 @@ public class KeyguardUserSwitcher { // Disable the icon if switching is disabled v.setAvatarEnabled(item.isSwitchToEnabled); convertView.setActivated(item.isCurrent); + if (item.isCurrent) { + mCurrentUserView = convertView; + } convertView.setTag(item); return convertView; } @@ -297,6 +301,12 @@ public class KeyguardUserSwitcher { // tapping the guest user while it's current clears the session. mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); } else if (user.isSwitchToEnabled) { + if (!user.isAddUser && !user.isRestricted && !user.isDisabledByAdmin) { + if (mCurrentUserView != null) { + mCurrentUserView.setActivated(false); + } + v.setActivated(true); + } switchTo(user); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 8dfcb0ac215d..9ce31d030c43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -236,8 +236,7 @@ public class UserSwitcherController implements Dumpable { picture, avatarSize, avatarSize, true); } } - int index = isCurrent ? 0 : records.size(); - records.add(index, new UserRecord(info, picture, false /* isGuest */, + records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent, false /* isAddUser */, false /* isRestricted */, switchToEnabled)); } @@ -269,8 +268,7 @@ public class UserSwitcherController implements Dumpable { records.add(guestRecord); } } else { - int index = guestRecord.isCurrent ? 0 : records.size(); - records.add(index, guestRecord); + records.add(guestRecord); } if (canCreateUser) { diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt index f4157f21e158..8625d63a3c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt @@ -126,6 +126,13 @@ class PhysicsAnimator<T> private constructor (val target: T) { internal var startAction: () -> Unit = ::startInternal /** + * Action to run when [cancel] is called. This can be changed by + * [PhysicsAnimatorTestUtils.prepareForTest] to cancel animations from the main thread, which + * is required. + */ + internal var cancelAction: (Set<FloatPropertyCompat<in T>>) -> Unit = ::cancelInternal + + /** * Springs a property to the given value, using the provided configuration settings. * * Springs are used when you know the exact value to which you want to animate. They can be @@ -429,10 +436,13 @@ class PhysicsAnimator<T> private constructor (val target: T) { max = max(currentValue, this.max) } - // Apply the configuration and start the animation. Since flings can't be - // redirected while in motion, cancel it first. + // Flings can't be updated to a new position while maintaining velocity, because + // we're using the explicitly provided start velocity. Cancel any flings (or + // springs) on this property before flinging. + cancel(animatedProperty) + + // Apply the configuration and start the animation. getFlingAnimation(animatedProperty) - .also { it.cancel() } .also { flingConfig.applyToAnimation(it) } .start() } @@ -707,11 +717,26 @@ class PhysicsAnimator<T> private constructor (val target: T) { return springConfigs.keys.union(flingConfigs.keys) } + /** + * Cancels the given properties. This is typically called immediately by [cancel], unless this + * animator is under test. + */ + internal fun cancelInternal(properties: Set<FloatPropertyCompat<in T>>) { + for (property in properties) { + flingAnimations[property]?.cancel() + springAnimations[property]?.cancel() + } + } + /** Cancels all in progress animations on all properties. */ fun cancel() { - for (dynamicAnim in flingAnimations.values.union(springAnimations.values)) { - dynamicAnim.cancel() - } + cancelAction(flingAnimations.keys) + cancelAction(springAnimations.keys) + } + + /** Cancels in progress animations on the provided properties only. */ + fun cancel(vararg properties: FloatPropertyCompat<in T>) { + cancelAction(properties.toSet()) } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt index 965decd255a0..c50eeac80d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt @@ -363,8 +363,12 @@ object PhysicsAnimatorTestUtils { private val testEndListeners = ArrayList<PhysicsAnimator.EndListener<T>>() private val testUpdateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>() + /** Whether we're currently in the middle of executing startInternal(). */ + private var currentlyRunningStartInternal = false + init { animator.startAction = ::startForTest + animator.cancelAction = ::cancelForTest } internal fun addTestEndListener(listener: PhysicsAnimator.EndListener<T>) { @@ -437,7 +441,29 @@ object PhysicsAnimatorTestUtils { } }) + currentlyRunningStartInternal = true animator.startInternal() + currentlyRunningStartInternal = false + unblockLatch.countDown() + } + + unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS) + } + + private fun cancelForTest(properties: Set<FloatPropertyCompat<in T>>) { + // If this was called from startInternal, we are already on the animation thread, and + // should just call cancelInternal rather than posting it. If we post it, the + // cancellation will occur after the rest of startInternal() and we'll immediately + // cancel the animation we worked so hard to start! + if (currentlyRunningStartInternal) { + animator.cancelInternal(properties) + return + } + + val unblockLatch = CountDownLatch(1) + + animationThreadHandler.post { + animator.cancelInternal(properties) unblockLatch.countDown() } diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt new file mode 100644 index 000000000000..2276ba19e432 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.magnetictarget + +import android.annotation.SuppressLint +import android.content.Context +import android.database.ContentObserver +import android.graphics.PointF +import android.os.Handler +import android.os.UserHandle +import android.os.VibrationEffect +import android.os.Vibrator +import android.provider.Settings +import android.view.MotionEvent +import android.view.VelocityTracker +import android.view.View +import androidx.dynamicanimation.animation.DynamicAnimation +import androidx.dynamicanimation.animation.FloatPropertyCompat +import androidx.dynamicanimation.animation.SpringForce +import com.android.systemui.util.animation.PhysicsAnimator +import kotlin.math.hypot + +/** + * Utility class for creating 'magnetized' objects that are attracted to one or more magnetic + * targets. Magnetic targets attract objects that are dragged near them, and hold them there unless + * they're moved away or released. Releasing objects inside a magnetic target typically performs an + * action on the object. + * + * MagnetizedObject also supports flinging to targets, which will result in the object being pulled + * into the target and released as if it was dragged into it. + * + * To use this class, either construct an instance with an object of arbitrary type, or use the + * [MagnetizedObject.magnetizeView] shortcut method if you're magnetizing a view. Then, set + * [magnetListener] to receive event callbacks. In your touch handler, pass all MotionEvents + * that move this object to [maybeConsumeMotionEvent]. If that method returns true, consider the + * event consumed by the MagnetizedObject and don't move the object unless it begins returning false + * again. + * + * @param context Context, used to retrieve a Vibrator instance for vibration effects. + * @param underlyingObject The actual object that we're magnetizing. + * @param xProperty Property that sets the x value of the object's position. + * @param yProperty Property that sets the y value of the object's position. + */ +abstract class MagnetizedObject<T : Any>( + val context: Context, + + /** The actual object that is animated. */ + val underlyingObject: T, + + /** Property that gets/sets the object's X value. */ + val xProperty: FloatPropertyCompat<in T>, + + /** Property that gets/sets the object's Y value. */ + val yProperty: FloatPropertyCompat<in T> +) { + + /** Return the width of the object. */ + abstract fun getWidth(underlyingObject: T): Float + + /** Return the height of the object. */ + abstract fun getHeight(underlyingObject: T): Float + + /** + * Fill the provided array with the location of the top-left of the object, relative to the + * entire screen. Compare to [View.getLocationOnScreen]. + */ + abstract fun getLocationOnScreen(underlyingObject: T, loc: IntArray) + + /** Methods for listening to events involving a magnetized object. */ + interface MagnetListener { + + /** + * Called when touch events move within the magnetic field of a target, causing the + * object to animate to the target and become 'stuck' there. The animation happens + * automatically here - you should not move the object. You can, however, change its state + * to indicate to the user that it's inside the target and releasing it will have an effect. + * + * [maybeConsumeMotionEvent] is now returning true and will continue to do so until a call + * to [onUnstuckFromTarget] or [onReleasedInTarget]. + * + * @param target The target that the object is now stuck to. + */ + fun onStuckToTarget(target: MagneticTarget) + + /** + * Called when the object is no longer stuck to a target. This means that either touch + * events moved outside of the magnetic field radius, or that a forceful fling out of the + * target was detected. + * + * The object won't be automatically animated out of the target, since you're responsible + * for moving the object again. You should move it (or animate it) using your own + * movement/animation logic. + * + * Reverse any effects applied in [onStuckToTarget] here. + * + * If [wasFlungOut] is true, [maybeConsumeMotionEvent] returned true for the ACTION_UP event + * that concluded the fling. If [wasFlungOut] is false, that means a drag gesture is ongoing + * and [maybeConsumeMotionEvent] is now returning false. + * + * @param target The target that this object was just unstuck from. + * @param velX The X velocity of the touch gesture when it exited the magnetic field. + * @param velY The Y velocity of the touch gesture when it exited the magnetic field. + * @param wasFlungOut Whether the object was unstuck via a fling gesture. This means that + * an ACTION_UP event was received, and that the gesture velocity was sufficient to conclude + * that the user wants to un-stick the object despite no touch events occurring outside of + * the magnetic field radius. + */ + fun onUnstuckFromTarget( + target: MagneticTarget, + velX: Float, + velY: Float, + wasFlungOut: Boolean + ) + + /** + * Called when the object is released inside a target, or flung towards it with enough + * velocity to reach it. + * + * @param target The target that the object was released in. + */ + fun onReleasedInTarget(target: MagneticTarget) + } + + private val animator: PhysicsAnimator<T> = PhysicsAnimator.getInstance(underlyingObject) + private val objectLocationOnScreen = IntArray(2) + + /** + * Targets that have been added to this object. These will all be considered when determining + * magnetic fields and fling trajectories. + */ + private val associatedTargets = ArrayList<MagneticTarget>() + + private val velocityTracker: VelocityTracker = VelocityTracker.obtain() + private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + + /** Whether touch events are presently occurring within the magnetic field area of a target. */ + val objectStuckToTarget: Boolean + get() = targetObjectIsStuckTo != null + + /** The target the object is stuck to, or null if the object is not stuck to any target. */ + private var targetObjectIsStuckTo: MagneticTarget? = null + + /** + * Sets the listener to receive events. This must be set, or [maybeConsumeMotionEvent] + * will always return false and no magnetic effects will occur. + */ + lateinit var magnetListener: MagnetizedObject.MagnetListener + + /** + * Sets whether forcefully flinging the object vertically towards a target causes it to be + * attracted to the target and then released immediately, despite never being dragged within the + * magnetic field. + */ + var flingToTargetEnabled = true + + /** + * If fling to target is enabled, forcefully flinging the object towards a target will cause + * it to be attracted to the target and then released immediately, despite never being dragged + * within the magnetic field. + * + * This sets the width of the area considered 'near' enough a target to be considered a fling, + * in terms of percent of the target view's width. For example, setting this to 3f means that + * flings towards a 100px-wide target will be considered 'near' enough if they're towards the + * 300px-wide area around the target. + * + * Flings whose trajectory intersects the area will be attracted and released - even if the + * target view itself isn't intersected: + * + * | | + * | 0 | + * | / | + * | / | + * | X / | + * |.....###.....| + * + * + * Flings towards the target whose trajectories do not intersect the area will be treated as + * normal flings and the magnet will leave the object alone: + * + * | | + * | | + * | 0 | + * | / | + * | / X | + * |.....###.....| + * + */ + var flingToTargetWidthPercent = 3f + + /** + * Sets the minimum velocity (in pixels per second) required to fling an object to the target + * without dragging it into the magnetic field. + */ + var flingToTargetMinVelocity = 4000f + + /** + * Sets the minimum velocity (in pixels per second) required to fling un-stuck an object stuck + * to the target. If this velocity is reached, the object will be freed even if it wasn't moved + * outside the magnetic field radius. + */ + var flingUnstuckFromTargetMinVelocity = 1000f + + /** + * Sets the maximum velocity above which the object will not stick to the target. Even if the + * object is dragged through the magnetic field, it will not stick to the target until the + * velocity is below this value. + */ + var stickToTargetMaxVelocity = 2000f + + /** + * Enable or disable haptic vibration effects when the object interacts with the magnetic field. + * + * If you're experiencing crashes when the object enters targets, ensure that you have the + * android.permission.VIBRATE permission! + */ + var hapticsEnabled = true + + /** Whether the HAPTIC_FEEDBACK_ENABLED setting is true. */ + private var systemHapticsEnabled = false + + /** Default spring configuration to use for animating the object into a target. */ + var springConfig = PhysicsAnimator.SpringConfig( + SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY) + + /** + * Spring configuration to use to spring the object into a target specifically when it's flung + * towards (rather than dragged near) it. + */ + var flungIntoTargetSpringConfig = springConfig + + init { + val hapticSettingObserver = + object : ContentObserver(Handler.getMain()) { + override fun onChange(selfChange: Boolean) { + systemHapticsEnabled = + Settings.System.getIntForUser( + context.contentResolver, + Settings.System.HAPTIC_FEEDBACK_ENABLED, + 0, + UserHandle.USER_CURRENT) != 0 + } + } + + context.contentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED), + true /* notifyForDescendants */, hapticSettingObserver) + + // Trigger the observer once to initialize systemHapticsEnabled. + hapticSettingObserver.onChange(false /* selfChange */) + } + + /** + * Adds the provided MagneticTarget to this object. The object will now be attracted to the + * target if it strays within its magnetic field or is flung towards it. + * + * If this target (or its magnetic field) overlaps another target added to this object, the + * prior target will take priority. + */ + fun addTarget(target: MagneticTarget) { + associatedTargets.add(target) + target.updateLocationOnScreen() + } + + /** + * Shortcut that accepts a View and a magnetic field radius and adds it as a magnetic target. + * + * @return The MagneticTarget instance for the given View. This can be used to change the + * target's magnetic field radius after it's been added. It can also be added to other + * magnetized objects. + */ + fun addTarget(target: View, magneticFieldRadiusPx: Int): MagneticTarget { + return MagneticTarget(target, magneticFieldRadiusPx).also { addTarget(it) } + } + + /** + * Removes the given target from this object. The target will no longer attract the object. + */ + fun removeTarget(target: MagneticTarget) { + associatedTargets.remove(target) + } + + /** + * Provide this method with all motion events that move the magnetized object. If the + * location of the motion events moves within the magnetic field of a target, or indicate a + * fling-to-target gesture, this method will return true and you should not move the object + * yourself until it returns false again. + * + * Note that even when this method returns true, you should continue to pass along new motion + * events so that we know when the events move back outside the magnetic field area. + * + * This method will always return false if you haven't set a [magnetListener]. + */ + fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean { + // Short-circuit if we don't have a listener or any targets, since those are required. + if (associatedTargets.size == 0) { + return false + } + + // When a gesture begins, recalculate target views' positions on the screen in case they + // have changed. Also, clear state. + if (ev.action == MotionEvent.ACTION_DOWN) { + updateTargetViewLocations() + + // Clear the velocity tracker and assume we're not stuck to a target yet. + velocityTracker.clear() + targetObjectIsStuckTo = null + } + + addMovement(ev) + + val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target -> + val distanceFromTargetCenter = hypot( + ev.rawX - target.centerOnScreen.x, + ev.rawY - target.centerOnScreen.y) + distanceFromTargetCenter < target.magneticFieldRadiusPx + } + + // If we aren't currently stuck to a target, and we're in the magnetic field of a target, + // we're newly stuck. + val objectNewlyStuckToTarget = + !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null + + // If we are currently stuck to a target, we're in the magnetic field of a target, and that + // target isn't the one we're currently stuck to, then touch events have moved into a + // adjacent target's magnetic field. + val objectMovedIntoDifferentTarget = + objectStuckToTarget && + targetObjectIsInMagneticFieldOf != null && + targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf + + if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) { + velocityTracker.computeCurrentVelocity(1000) + val velX = velocityTracker.xVelocity + val velY = velocityTracker.yVelocity + + // If the object is moving too quickly within the magnetic field, do not stick it. This + // only applies to objects newly stuck to a target. If the object is moved into a new + // target, it wasn't moving at all (since it was stuck to the previous one). + if (objectNewlyStuckToTarget && hypot(velX, velY) > stickToTargetMaxVelocity) { + return false + } + + // This touch event is newly within the magnetic field - let the listener know, and + // animate sticking to the magnet. + targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf + cancelAnimations() + magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!) + animateStuckToTarget(targetObjectIsInMagneticFieldOf!!, velX, velY, false) + + vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) + } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) { + velocityTracker.computeCurrentVelocity(1000) + + // This touch event is newly outside the magnetic field - let the listener know. It will + // move the object out of the target using its own movement logic. + cancelAnimations() + magnetListener.onUnstuckFromTarget( + targetObjectIsStuckTo!!, velocityTracker.xVelocity, velocityTracker.yVelocity, + wasFlungOut = false) + targetObjectIsStuckTo = null + + vibrateIfEnabled(VibrationEffect.EFFECT_TICK) + } + + // First, check for relevant gestures concluding with an ACTION_UP. + if (ev.action == MotionEvent.ACTION_UP) { + + velocityTracker.computeCurrentVelocity(1000 /* units */) + val velX = velocityTracker.xVelocity + val velY = velocityTracker.yVelocity + + // Cancel the magnetic animation since we might still be springing into the magnetic + // target, but we're about to fling away or release. + cancelAnimations() + + if (objectStuckToTarget) { + if (hypot(velX, velY) > flingUnstuckFromTargetMinVelocity) { + // If the object is stuck, but it was forcefully flung away from the target, + // tell the listener so the object can be animated out of the target. + magnetListener.onUnstuckFromTarget( + targetObjectIsStuckTo!!, velX, velY, wasFlungOut = true) + } else { + // If the object is stuck and not flung away, it was released inside the target. + magnetListener.onReleasedInTarget(targetObjectIsStuckTo!!) + vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) + } + + // Either way, we're no longer stuck. + targetObjectIsStuckTo = null + return true + } + + // The target we're flinging towards, or null if we're not flinging towards any target. + val flungToTarget = associatedTargets.firstOrNull { target -> + isForcefulFlingTowardsTarget(target, ev.rawX, ev.rawY, velX, velY) + } + + if (flungToTarget != null) { + // If this is a fling-to-target, animate the object to the magnet and then release + // it. + magnetListener.onStuckToTarget(flungToTarget) + targetObjectIsStuckTo = flungToTarget + + animateStuckToTarget(flungToTarget, velX, velY, true) { + targetObjectIsStuckTo = null + magnetListener.onReleasedInTarget(flungToTarget) + vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) + } + + return true + } + + // If it's not either of those things, we are not interested. + return false + } + + return objectStuckToTarget // Always consume touch events if the object is stuck. + } + + /** Plays the given vibration effect if haptics are enabled. */ + @SuppressLint("MissingPermission") + private fun vibrateIfEnabled(effect: Int) { + if (hapticsEnabled && systemHapticsEnabled) { + vibrator.vibrate(effect.toLong()) + } + } + + /** Adds the movement to the velocity tracker using raw coordinates. */ + private fun addMovement(event: MotionEvent) { + // Add movement to velocity tracker using raw screen X and Y coordinates instead + // of window coordinates because the window frame may be moving at the same time. + val deltaX = event.rawX - event.x + val deltaY = event.rawY - event.y + event.offsetLocation(deltaX, deltaY) + velocityTracker.addMovement(event) + event.offsetLocation(-deltaX, -deltaY) + } + + /** Animates sticking the object to the provided target with the given start velocities. */ + private fun animateStuckToTarget( + target: MagneticTarget, + velX: Float, + velY: Float, + flung: Boolean, + after: (() -> Unit)? = null + ) { + target.updateLocationOnScreen() + getLocationOnScreen(underlyingObject, objectLocationOnScreen) + + // Calculate the difference between the target's center coordinates and the object's. + // Animating the object's x/y properties by these values will center the object on top + // of the magnetic target. + val xDiff = target.centerOnScreen.x - + getWidth(underlyingObject) / 2f - objectLocationOnScreen[0] + val yDiff = target.centerOnScreen.y - + getHeight(underlyingObject) / 2f - objectLocationOnScreen[1] + + val springConfig = if (flung) flungIntoTargetSpringConfig else springConfig + + cancelAnimations() + + // Animate to the center of the target. + animator + .spring(xProperty, xProperty.getValue(underlyingObject) + xDiff, velX, + springConfig) + .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY, + springConfig) + + if (after != null) { + animator.withEndActions(after) + } + + animator.start() + } + + /** + * Whether or not the provided values match a 'fast fling' towards the provided target. If it + * does, we consider it a fling-to-target gesture. + */ + private fun isForcefulFlingTowardsTarget( + target: MagneticTarget, + rawX: Float, + rawY: Float, + velX: Float, + velY: Float + ): Boolean { + if (!flingToTargetEnabled) { + return false + } + + // Whether velocity is sufficient, depending on whether we're flinging into a target at the + // top or the bottom of the screen. + val velocitySufficient = + if (rawY < target.centerOnScreen.y) velY > flingToTargetMinVelocity + else velY < flingToTargetMinVelocity + + if (!velocitySufficient) { + return false + } + + // Whether the trajectory of the fling intersects the target area. + var targetCenterXIntercept = rawX + + // Only do math if the X velocity is non-zero, otherwise X won't change. + if (velX != 0f) { + // Rise over run... + val slope = velY / velX + // ...y = mx + b, b = y / mx... + val yIntercept = rawY - slope * rawX + + // ...calculate the x value when y = the target's y-coordinate. + targetCenterXIntercept = (target.centerOnScreen.y - yIntercept) / slope + } + + // The width of the area we're looking for a fling towards. + val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent + + // Velocity was sufficient, so return true if the intercept is within the target area. + return targetCenterXIntercept > target.centerOnScreen.x - targetAreaWidth / 2 && + targetCenterXIntercept < target.centerOnScreen.x + targetAreaWidth / 2 + } + + /** Cancel animations on this object's x/y properties. */ + internal fun cancelAnimations() { + animator.cancel(xProperty, yProperty) + } + + /** Updates the locations on screen of all of the [associatedTargets]. */ + internal fun updateTargetViewLocations() { + associatedTargets.forEach { it.updateLocationOnScreen() } + } + + /** + * Represents a target view with a magnetic field radius and cached center-on-screen + * coordinates. + * + * Instances of MagneticTarget are passed to a MagnetizedObject's [addTarget], and can then + * attract the object if it's dragged near or flung towards it. MagneticTargets can be added to + * multiple objects. + */ + class MagneticTarget( + internal val targetView: View, + var magneticFieldRadiusPx: Int + ) { + internal val centerOnScreen = PointF() + + private val tempLoc = IntArray(2) + + fun updateLocationOnScreen() { + targetView.getLocationOnScreen(tempLoc) + + // Add half of the target size to get the center, and subtract translation since the + // target could be animating in while we're doing this calculation. + centerOnScreen.set( + tempLoc[0] + targetView.width / 2f - targetView.translationX, + tempLoc[1] + targetView.height / 2f - targetView.translationY) + } + } + + companion object { + + /** + * Magnetizes the given view. Magnetized views are attracted to one or more magnetic + * targets. Magnetic targets attract objects that are dragged near them, and hold them there + * unless they're moved away or released. Releasing objects inside a magnetic target + * typically performs an action on the object. + * + * Magnetized views can also be flung to targets, which will result in the view being pulled + * into the target and released as if it was dragged into it. + * + * To use the returned MagnetizedObject<View> instance, first set [magnetListener] to + * receive event callbacks. In your touch handler, pass all MotionEvents that move this view + * to [maybeConsumeMotionEvent]. If that method returns true, consider the event consumed by + * MagnetizedObject and don't move the view unless it begins returning false again. + * + * The view will be moved via translationX/Y properties, and its + * width/height will be determined via getWidth()/getHeight(). If you are animating + * something other than a view, or want to position your view using properties other than + * translationX/Y, implement an instance of [MagnetizedObject]. + * + * Note that the magnetic library can't re-order your view automatically. If the view + * renders on top of the target views, it will obscure the target when it sticks to it. + * You'll want to bring the view to the front in [MagnetListener.onStuckToTarget]. + */ + @JvmStatic + fun <T : View> magnetizeView(view: T): MagnetizedObject<T> { + return object : MagnetizedObject<T>( + view.context, + view, + DynamicAnimation.TRANSLATION_X, + DynamicAnimation.TRANSLATION_Y) { + override fun getWidth(underlyingObject: T): Float { + return underlyingObject.width.toFloat() + } + + override fun getHeight(underlyingObject: T): Float { + return underlyingObject.height.toFloat() } + + override fun getLocationOnScreen(underlyingObject: T, loc: IntArray) { + underlyingObject.getLocationOnScreen(loc) + } + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java new file mode 100644 index 000000000000..a94af24fc843 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wifi; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.debug.IAdbManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.EventLog; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.Toast; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; +import com.android.systemui.R; + +/** + * Alerts the user of an untrusted network when enabling wireless debugging. + * The user can either deny, allow, or allow with the "always allow on this + * network" checked. + */ +public class WifiDebuggingActivity extends AlertActivity + implements DialogInterface.OnClickListener { + private static final String TAG = "WifiDebuggingActivity"; + + private CheckBox mAlwaysAllow; + // Notifies when wifi is disabled, or the network changed + private WifiChangeReceiver mWifiChangeReceiver; + private WifiManager mWifiManager; + private String mBssid; + private boolean mClicked = false; + + @Override + public void onCreate(Bundle icicle) { + Window window = getWindow(); + window.addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + + super.onCreate(icicle); + + + mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); + mWifiChangeReceiver = new WifiChangeReceiver(this); + + Intent intent = getIntent(); + String ssid = intent.getStringExtra("ssid"); + mBssid = intent.getStringExtra("bssid"); + + if (ssid == null || mBssid == null) { + finish(); + return; + } + + final AlertController.AlertParams ap = mAlertParams; + ap.mTitle = getString(R.string.wifi_debugging_title); + ap.mMessage = getString(R.string.wifi_debugging_message, ssid, mBssid); + ap.mPositiveButtonText = getString(R.string.wifi_debugging_allow); + ap.mNegativeButtonText = getString(android.R.string.cancel); + ap.mPositiveButtonListener = this; + ap.mNegativeButtonListener = this; + + // add "always allow" checkbox + LayoutInflater inflater = LayoutInflater.from(ap.mContext); + View checkbox = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); + mAlwaysAllow = (CheckBox) checkbox.findViewById(com.android.internal.R.id.alwaysUse); + mAlwaysAllow.setText(getString(R.string.wifi_debugging_always)); + ap.mView = checkbox; + window.setCloseOnTouchOutside(false); + + setupAlert(); + + // adding touch listener on affirmative button - checks if window is obscured + // if obscured, do not let user give permissions (could be tapjacking involved) + final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> { + // Filter obscured touches by consuming them. + if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) + || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) { + if (event.getAction() == MotionEvent.ACTION_UP) { + // TODO: need a different value for safety net? + EventLog.writeEvent(0x534e4554, "62187985"); // safety net logging + Toast.makeText(v.getContext(), + R.string.touch_filtered_warning, + Toast.LENGTH_SHORT).show(); + } + return true; + } + return false; + }; + mAlert.getButton(BUTTON_POSITIVE).setOnTouchListener(filterTouchListener); + + } + + @Override + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { + super.onWindowAttributesChanged(params); + } + + private class WifiChangeReceiver extends BroadcastReceiver { + private final Activity mActivity; + WifiChangeReceiver(Activity activity) { + mActivity = activity; + } + + @Override + public void onReceive(Context content, Intent intent) { + String action = intent.getAction(); + if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { + int state = intent.getIntExtra( + WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); + if (state == WifiManager.WIFI_STATE_DISABLED) { + mActivity.finish(); + } + } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { + NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + if (!networkInfo.isConnected()) { + mActivity.finish(); + return; + } + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + if (wifiInfo == null || wifiInfo.getNetworkId() == -1) { + mActivity.finish(); + return; + } + String bssid = wifiInfo.getBSSID(); + if (bssid == null || bssid.isEmpty()) { + mActivity.finish(); + return; + } + if (!bssid.equals(mBssid)) { + mActivity.finish(); + return; + } + } + } + } + } + + @Override + public void onStart() { + super.onStart(); + IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + registerReceiver(mWifiChangeReceiver, filter); + } + + @Override + protected void onStop() { + if (mWifiChangeReceiver != null) { + unregisterReceiver(mWifiChangeReceiver); + } + super.onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + // In the case where user dismissed the dialog, we don't get an onClick event. + // In that case, tell adb to deny the network connection. + if (!mClicked) { + try { + IBinder b = ServiceManager.getService(ADB_SERVICE); + IAdbManager service = IAdbManager.Stub.asInterface(b); + service.denyWirelessDebugging(); + } catch (Exception e) { + Log.e(TAG, "Unable to notify Adb service", e); + } + } + } + + @Override + public void onClick(DialogInterface dialog, int which) { + mClicked = true; + boolean allow = (which == AlertDialog.BUTTON_POSITIVE); + boolean alwaysAllow = allow && mAlwaysAllow.isChecked(); + try { + IBinder b = ServiceManager.getService(ADB_SERVICE); + IAdbManager service = IAdbManager.Stub.asInterface(b); + if (allow) { + service.allowWirelessDebugging(alwaysAllow, mBssid); + } else { + service.denyWirelessDebugging(); + } + } catch (Exception e) { + Log.e(TAG, "Unable to notify Adb service", e); + } + finish(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java new file mode 100644 index 000000000000..0266a84503a1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wifi; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Bundle; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; +import com.android.systemui.R; + +/** + * Alerts the user that wireless debugging cannot be enabled by a secondary user. + */ +public class WifiDebuggingSecondaryUserActivity extends AlertActivity + implements DialogInterface.OnClickListener { + private WifiChangeReceiver mWifiChangeReceiver; + private WifiManager mWifiManager; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); + mWifiChangeReceiver = new WifiChangeReceiver(this); + + final AlertController.AlertParams ap = mAlertParams; + ap.mTitle = getString(R.string.wifi_debugging_secondary_user_title); + ap.mMessage = getString(R.string.wifi_debugging_secondary_user_message); + ap.mPositiveButtonText = getString(android.R.string.ok); + ap.mPositiveButtonListener = this; + + setupAlert(); + } + + private class WifiChangeReceiver extends BroadcastReceiver { + private final Activity mActivity; + WifiChangeReceiver(Activity activity) { + mActivity = activity; + } + + @Override + public void onReceive(Context content, Intent intent) { + String action = intent.getAction(); + if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { + int state = intent.getIntExtra( + WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); + if (state == WifiManager.WIFI_STATE_DISABLED) { + mActivity.finish(); + } + } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { + NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + if (!networkInfo.isConnected()) { + mActivity.finish(); + return; + } + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + if (wifiInfo == null || wifiInfo.getNetworkId() == -1) { + mActivity.finish(); + return; + } + } + } + } + } + + @Override + public void onStart() { + super.onStart(); + + IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + registerReceiver(mWifiChangeReceiver, filter); + } + + @Override + protected void onStop() { + if (mWifiChangeReceiver != null) { + unregisterReceiver(mWifiChangeReceiver); + } + super.onStop(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index befe3e12b64e..6a0939630bf2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -22,6 +22,7 @@ import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -75,6 +76,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @SmallTest @@ -117,6 +119,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private SubscriptionManager mSubscriptionManager; @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private Executor mBackgroundExecutor; private TestableLooper mTestableLooper; private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -137,7 +141,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true); when(mUserManager.isPrimaryUser()).thenReturn(true); - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mStrongAuthTracker + .isUnlockingWithBiometricAllowed(anyBoolean() /* isStrongBiometric */)) + .thenReturn(true); context.addMockSystemService(TrustManager.class, mTrustManager); context.addMockSystemService(FingerprintManager.class, mFingerprintManager); context.addMockSystemService(BiometricManager.class, mBiometricManager); @@ -450,7 +456,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() { - mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser()); + // test whether face will be skipped if authenticated, so the value of isStrongBiometric + // doesn't matter here + mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(), + true /* isStrongBiometric */); mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true); mTestableLooper.processAllMessages(); @@ -460,18 +469,36 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testGetUserCanSkipBouncer_whenFace() { int user = KeyguardUpdateMonitor.getCurrentUser(); - mKeyguardUpdateMonitor.onFaceAuthenticated(user); + mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isStrongBiometric */); assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue(); } @Test + public void testGetUserCanSkipBouncer_whenFace_nonStrongAndDisallowed() { + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */)) + .thenReturn(false); + int user = KeyguardUpdateMonitor.getCurrentUser(); + mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isStrongBiometric */); + assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse(); + } + + @Test public void testGetUserCanSkipBouncer_whenFingerprint() { int user = KeyguardUpdateMonitor.getCurrentUser(); - mKeyguardUpdateMonitor.onFingerprintAuthenticated(user); + mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isStrongBiometric */); assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue(); } @Test + public void testGetUserCanSkipBouncer_whenFingerprint_nonStrongAndDisallowed() { + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */)) + .thenReturn(false); + int user = KeyguardUpdateMonitor.getCurrentUser(); + mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isStrongBiometric */); + assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse(); + } + + @Test public void testGetUserCanSkipBouncer_whenTrust() { int user = KeyguardUpdateMonitor.getCurrentUser(); mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */); @@ -585,7 +612,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { protected TestableKeyguardUpdateMonitor(Context context) { super(context, TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(), - mBroadcastDispatcher, mDumpController); + mBroadcastDispatcher, mDumpController, mBackgroundExecutor); mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker; } 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 3330d1e6d0a8..61991816a407 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -93,6 +93,8 @@ public final class ClockManagerTest extends SysuiTestCase { mMockPluginManager, mMockColorExtractor, mMockContentResolver, mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager); + mClockManager.addBuiltinClock(() -> new BubbleClockController( + getContext().getResources(), inflater, mMockColorExtractor)); mClockManager.addOnClockChangedListener(mMockListener1); mClockManager.addOnClockChangedListener(mMockListener2); reset(mMockListener1, mMockListener2); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 1d4b4be9e683..581d795af3df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -124,7 +125,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); - when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt index b3d0d22445b6..6388fe1a69c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt @@ -63,7 +63,7 @@ class NotificationSectionsFeatureManagerTest : SysuiTestCase() { DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false) assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled()) - assertTrue("Expecting 3 buckets when people filtering is enabled", - manager!!.getNumberOfBuckets() == 3) + assertTrue("Expecting 4 buckets when people filtering is enabled", + manager!!.getNumberOfBuckets() == 4) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 45c51d42c250..f11c42bda6cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationGroupManager @@ -148,6 +149,53 @@ class NotificationRankingManagerTest : SysuiTestCase() { } @Test + fun testSort_headsUp_trumpsPeople() { + whenever(sectionsManager.isFilteringEnabled()).thenReturn(true) + val aN = Notification.Builder(mContext, "test") + .setStyle(Notification.MessagingStyle("")) + .build() + val a = NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(aN) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build() + + whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) + .thenReturn(true) + whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking)) + .thenReturn(true) + + val bN = Notification.Builder(mContext, "test") + .setStyle(Notification.MessagingStyle("")) + .build() + val b = NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .setPkg("pkg2") + .setOpPkg("pkg2") + .setTag("tag") + .setNotification(bN) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build() + b.row = mock(ExpandableNotificationRow::class.java).also { + whenever(it.isHeadsUp).thenReturn(true) + } + + whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) + .thenReturn(false) + whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking)) + .thenReturn(false) + + assertEquals(listOf(b, a), rankingManager.updateRanking(null, listOf(a, b), "test")) + } + + @Test fun testSort_importantPeople() { whenever(sectionsManager.isFilteringEnabled()).thenReturn(true) val aN = Notification.Builder(mContext, "test") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt index 867a9b97d622..abce8b517dfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt @@ -43,7 +43,7 @@ class PeopleHubViewControllerTest : SysuiTestCase() { @JvmField @Rule val mockito: MockitoRule = MockitoJUnit.rule() - @Mock private lateinit var mockViewBoundary: PeopleHubSectionFooterViewBoundary + @Mock private lateinit var mockViewBoundary: PeopleHubViewBoundary @Mock private lateinit var mockActivityStarter: ActivityStarter @Test @@ -67,7 +67,7 @@ class PeopleHubViewControllerTest : SysuiTestCase() { return mockSubscription } } - val adapter = PeopleHubSectionFooterViewAdapterImpl(fakeFactoryDataSource) + val adapter = PeopleHubViewAdapterImpl(fakeFactoryDataSource) adapter.bindView(mockViewBoundary) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index 51f214d8cda2..abfbcd99167b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -45,8 +45,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; -import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; @@ -71,7 +70,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Mock private ActivityStarterDelegate mActivityStarterDelegate; @Mock private StatusBarStateController mStatusBarStateController; @Mock private ConfigurationController mConfigurationController; - @Mock private PeopleHubSectionFooterViewAdapter mPeopleHubAdapter; + @Mock private PeopleHubViewAdapter mPeopleHubAdapter; @Mock private NotificationSectionsFeatureManager mSectionsFeatureManager; @Mock private NotificationRowComponent mNotificationRowComponent; @Mock private ActivatableNotificationViewController mActivatableNotificationViewController; @@ -90,18 +89,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { mStatusBarStateController, mConfigurationController, mPeopleHubAdapter, - mSectionsFeatureManager, - new NotificationRowComponent.Builder() { - @Override - public NotificationRowComponent.Builder activatableNotificationView( - ActivatableNotificationView view) { - return this; - } - - @Override - public NotificationRowComponent build() { - return mNotificationRowComponent; - }}); + mSectionsFeatureManager + ); // Required in order for the header inflation to work properly when(mNssl.generateLayoutParams(any(AttributeSet.class))) .thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 769b774aeec2..813923d44216 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -97,7 +97,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true); - when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true); + when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean())) + .thenReturn(true); when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true); mContext.addMockSystemService(PowerManager.class, mPowerManager); mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager); @@ -112,11 +113,28 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Test public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() { + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) + .thenReturn(false); mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FINGERPRINT); + BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager).showBouncer(eq(false)); verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); + } + + @Test + public void onBiometricAuthenticated_whenFingerprint_nonStrongBioDisallowed_showBouncer() { + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */)) + .thenReturn(false); + mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, + BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */); + verify(mStatusBarKeyguardViewManager).showBouncer(eq(false)); + verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), + anyFloat()); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); } @Test @@ -124,44 +142,60 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { reset(mUpdateMonitor); reset(mStatusBarKeyguardViewManager); when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true); - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mDozeScrimController.isPulsing()).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FINGERPRINT); + BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); verify(mKeyguardViewMediator).onWakeAndUnlocking(); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING); } @Test public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() { - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FINGERPRINT); + BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING); } @Test public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() { - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FINGERPRINT); + BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER); } @Test public void onBiometricAuthenticated_whenFace_dontDismissKeyguard() { - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean()); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_NONE); } @Test @@ -169,13 +203,17 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_UNLOCK_FADING); } @Test @@ -184,9 +222,11 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); // Wake up before showing the bouncer verify(mStatusBarKeyguardViewManager, never()).showBouncer(eq(false)); @@ -202,9 +242,11 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { reset(mUpdateMonitor); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(), @@ -215,23 +257,30 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Test public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() { - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER); } @Test public void onBiometricAuthenticated_whenBypassOnBouncer_dismissBouncer() { reset(mKeyguardBypassController); - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); - when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true); + when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean())) + .thenReturn(true); when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); assertThat(mBiometricUnlockController.getMode()) @@ -240,11 +289,13 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Test public void onBiometricAuthenticated_whenBypassOnBouncer_respectsCanPlaySubtleAnim() { - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); assertThat(mBiometricUnlockController.getMode()) @@ -255,13 +306,17 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { public void onBiometricAuthenticated_whenFaceAndPulsing_dontDismissKeyguard() { reset(mUpdateMonitor); reset(mStatusBarKeyguardViewManager); - when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mDozeScrimController.isPulsing()).thenReturn(true); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_ONLY_WAKE); } @Test @@ -270,8 +325,10 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onFinishedGoingToSleep(-1); verify(mHandler, never()).post(any()); + // the value of isStrongBiometric doesn't matter here since we only care about the returned + // value of isUnlockingWithBiometricAllowed() mBiometricUnlockController.onBiometricAuthenticated(1 /* userId */, - BiometricSourceType.FACE); + BiometricSourceType.FACE, true /* isStrongBiometric */); mBiometricUnlockController.onFinishedGoingToSleep(-1); verify(mHandler).post(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 2e6fbe7d1ddb..408dfc0e1dee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -36,7 +36,6 @@ import static org.mockito.Mockito.when; import android.animation.Animator; import android.app.AlarmManager; -import android.content.res.Resources; import android.graphics.Color; import android.os.Handler; import android.testing.AndroidTestingRunner; @@ -91,8 +90,6 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock LightBarController mLightBarController; @Mock - Resources mResources; - @Mock DelayedWakeLock.Builder mDelayedWakeLockBuilder; @Mock private DelayedWakeLock mWakeLock; @@ -216,8 +213,7 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDockManager.isDocked()).thenReturn(false); mScrimController = new ScrimController(mLightBarController, - mDozeParamenters, mAlarmManager, mKeyguardStateController, - mResources, mDelayedWakeLockBuilder, + mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor, mDockManager); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt new file mode 100644 index 000000000000..f1672b1c644d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.magnetictarget + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.MotionEvent +import android.view.View +import androidx.dynamicanimation.animation.FloatPropertyCompat +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.animation.PhysicsAnimatorTestUtils +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions + +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +@SmallTest +class MagnetizedObjectTest : SysuiTestCase() { + /** Incrementing value for fake MotionEvent timestamps. */ + private var time = 0L + + /** Value to add to each new MotionEvent's timestamp. */ + private var timeStep = 100 + + private val underlyingObject = this + + private lateinit var targetView: View + + private val targetSize = 200 + private val targetCenterX = 500 + private val targetCenterY = 900 + private val magneticFieldRadius = 200 + + private var objectX = 0f + private var objectY = 0f + private val objectSize = 50f + + private lateinit var magneticTarget: MagnetizedObject.MagneticTarget + private lateinit var magnetizedObject: MagnetizedObject<*> + private lateinit var magnetListener: MagnetizedObject.MagnetListener + + private val xProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") { + override fun setValue(target: MagnetizedObjectTest?, value: Float) { + objectX = value + } + override fun getValue(target: MagnetizedObjectTest?): Float { + return objectX + } + } + + private val yProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") { + override fun setValue(target: MagnetizedObjectTest?, value: Float) { + objectY = value + } + + override fun getValue(target: MagnetizedObjectTest?): Float { + return objectY + } + } + + @Before + fun setup() { + PhysicsAnimatorTestUtils.prepareForTest() + + // Mock the view since a real view's getLocationOnScreen() won't work unless it's attached + // to a real window (it'll always return x = 0, y = 0). + targetView = mock(View::class.java) + `when`(targetView.context).thenReturn(context) + + // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's + // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900). + `when`(targetView.width).thenReturn(targetSize) // width = 200 + `when`(targetView.height).thenReturn(targetSize) // height = 200 + doAnswer { invocation -> + (invocation.arguments[0] as IntArray).also { location -> + // Return the top left of the target. + location[0] = targetCenterX - targetSize / 2 // x = 400 + location[1] = targetCenterY - targetSize / 2 // y = 800 + } + }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any()) + `when`(targetView.context).thenReturn(context) + + magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius) + + magnetListener = mock(MagnetizedObject.MagnetListener::class.java) + magnetizedObject = object : MagnetizedObject<MagnetizedObjectTest>( + context, underlyingObject, xProperty, yProperty) { + override fun getWidth(underlyingObject: MagnetizedObjectTest): Float { + return objectSize + } + + override fun getHeight(underlyingObject: MagnetizedObjectTest): Float { + return objectSize + } + + override fun getLocationOnScreen( + underlyingObject: MagnetizedObjectTest, + loc: IntArray + ) { + loc[0] = objectX.toInt() + loc[1] = objectY.toInt() } + } + + magnetizedObject.magnetListener = magnetListener + magnetizedObject.addTarget(magneticTarget) + + timeStep = 100 + } + + @Test + fun testMotionEventConsumption() { + // Start at (0, 0). No magnetic field here. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 0, y = 0, action = MotionEvent.ACTION_DOWN))) + + // Move to (400, 400), which is solidly outside the magnetic field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 200, y = 200))) + + // Move to (305, 705). This would be in the magnetic field radius if magnetic fields were + // square. It's not, because they're not. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX - magneticFieldRadius + 5, + y = targetCenterY - magneticFieldRadius + 5))) + + // Move to (400, 800). That's solidly in the radius so the magnetic target should begin + // consuming events. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX - 100, + y = targetCenterY - 100))) + + // Release at (400, 800). Since we're in the magnetic target, it should return true and + // consume the ACTION_UP. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 400, y = 800, action = MotionEvent.ACTION_UP))) + + // ACTION_DOWN outside the field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 200, y = 200, action = MotionEvent.ACTION_DOWN))) + + // Move to the center. We absolutely should consume events there. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, + y = targetCenterY))) + + // Drag out to (0, 0) and we should be returning false again. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 0, y = 0))) + + // The ACTION_UP event shouldn't be consumed either since it's outside the field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 0, y = 0, action = MotionEvent.ACTION_UP))) + } + + @Test + fun testMotionEventConsumption_downInMagneticField() { + // We should consume DOWN events if they occur in the field. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN))) + } + + @Test + fun testMoveIntoAroundAndOutOfMagneticField() { + // Move around but don't touch the magnetic field. + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 100, y = 100), + getMotionEvent(x = 200, y = 200)) + + // You can't become unstuck if you were never stuck in the first place. + verify(magnetListener, never()).onStuckToTarget(magneticTarget) + verify(magnetListener, never()).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + + // Move into and then around inside the magnetic field. + dispatchMotionEvents( + getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100), + getMotionEvent(x = targetCenterX, y = targetCenterY), + getMotionEvent(x = targetCenterX + 100, y = targetCenterY + 100)) + + // We should only have received one call to onStuckToTarget and none to unstuck. + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + verify(magnetListener, never()).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + + // Move out of the field and then release. + dispatchMotionEvents( + getMotionEvent(x = 100, y = 100), + getMotionEvent(x = 100, y = 100, action = MotionEvent.ACTION_UP)) + + // We should have received one unstuck call and no more stuck calls. We also should never + // have received an onReleasedInTarget call. + verify(magnetListener, times(1)).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMoveIntoOutOfAndBackIntoMagneticField() { + // Move into the field + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX - magneticFieldRadius, + y = targetCenterY - magneticFieldRadius, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = targetCenterX, y = targetCenterY)) + + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + verify(magnetListener, never()).onReleasedInTarget(magneticTarget) + + // Move back out. + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX - magneticFieldRadius, + y = targetCenterY - magneticFieldRadius)) + + verify(magnetListener, times(1)).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + verify(magnetListener, never()).onReleasedInTarget(magneticTarget) + + // Move in again and release in the magnetic field. + dispatchMotionEvents( + getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100), + getMotionEvent(x = targetCenterX + 50, y = targetCenterY + 50), + getMotionEvent(x = targetCenterX, y = targetCenterY), + getMotionEvent( + x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_UP)) + + verify(magnetListener, times(2)).onStuckToTarget(magneticTarget) + verify(magnetListener).onReleasedInTarget(magneticTarget) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testFlingTowardsTarget_towardsTarget() { + timeStep = 10 + + // Forcefully fling the object towards the target (but never touch the magnetic field). + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX, + y = 0, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = targetCenterX, + y = targetCenterY / 2), + getMotionEvent( + x = targetCenterX, + y = targetCenterY - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP)) + + // Nevertheless it should have ended up stuck to the target. + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + } + + @Test + fun testFlingTowardsTarget_towardsButTooSlow() { + // Very, very slowly fling the object towards the target (but never touch the magnetic + // field). This value is only used to create MotionEvent timestamps, it will not block the + // test for 10 seconds. + timeStep = 10000 + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX, + y = 0, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = targetCenterX, + y = targetCenterY / 2), + getMotionEvent( + x = targetCenterX, + y = targetCenterY - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP)) + + // No sticking should have occurred. + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testFlingTowardsTarget_missTarget() { + timeStep = 10 + // Forcefully fling the object down, but not towards the target. + dispatchMotionEvents( + getMotionEvent( + x = 0, + y = 0, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = 0, + y = targetCenterY / 2), + getMotionEvent( + x = 0, + y = targetCenterY - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP)) + + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMagnetAnimation() { + // Make sure the object starts at (0, 0). + assertEquals(0f, objectX) + assertEquals(0f, objectY) + + // Trigger the magnet animation, and block the test until it ends. + PhysicsAnimatorTestUtils.setAllAnimationsBlock(true) + magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, + y = targetCenterY, + action = MotionEvent.ACTION_DOWN)) + + // The object's (top-left) position should now position it centered over the target. + assertEquals(targetCenterX - objectSize / 2, objectX) + assertEquals(targetCenterY - objectSize / 2, objectY) + } + + @Test + fun testMultipleTargets() { + val secondMagneticTarget = getSecondMagneticTarget() + + // Drag into the second target. + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 100, y = 900)) + + // Verify that we received an onStuck for the second target, and no others. + verify(magnetListener).onStuckToTarget(secondMagneticTarget) + verifyNoMoreInteractions(magnetListener) + + // Drag into the original target. + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0), + getMotionEvent(x = 500, y = 900)) + + // We should have unstuck from the second one and stuck into the original one. + verify(magnetListener).onUnstuckFromTarget( + eq(secondMagneticTarget), anyFloat(), anyFloat(), eq(false)) + verify(magnetListener).onStuckToTarget(magneticTarget) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMultipleTargets_flingIntoSecond() { + val secondMagneticTarget = getSecondMagneticTarget() + + timeStep = 10 + + // Fling towards the second target. + dispatchMotionEvents( + getMotionEvent(x = 100, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 100, y = 350), + getMotionEvent(x = 100, y = 650, action = MotionEvent.ACTION_UP)) + + // Verify that we received an onStuck for the second target. + verify(magnetListener).onStuckToTarget(secondMagneticTarget) + + // Fling towards the first target. + dispatchMotionEvents( + getMotionEvent(x = 300, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 400, y = 350), + getMotionEvent(x = 500, y = 650, action = MotionEvent.ACTION_UP)) + + // Verify that we received onStuck for the original target. + verify(magnetListener).onStuckToTarget(magneticTarget) + } + + private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget { + // The first target view is at bounds (400, 800, 600, 1000) and it has a center of + // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900). + val secondTargetView = mock(View::class.java) + var secondTargetCenterX = 100 + var secondTargetCenterY = 900 + + `when`(secondTargetView.context).thenReturn(context) + `when`(secondTargetView.width).thenReturn(targetSize) // width = 200 + `when`(secondTargetView.height).thenReturn(targetSize) // height = 200 + doAnswer { invocation -> + (invocation.arguments[0] as IntArray).also { location -> + // Return the top left of the target. + location[0] = secondTargetCenterX - targetSize / 2 // x = 0 + location[1] = secondTargetCenterY - targetSize / 2 // y = 800 + } + }.`when`(secondTargetView).getLocationOnScreen(ArgumentMatchers.any()) + + return magnetizedObject.addTarget(secondTargetView, magneticFieldRadius) + } + + /** + * Return a MotionEvent at the given coordinates, with the given action (or MOVE by default). + * The event's time fields will be incremented by 10ms each time this is called, so tha + * VelocityTracker works. + */ + private fun getMotionEvent( + x: Int, + y: Int, + action: Int = MotionEvent.ACTION_MOVE + ): MotionEvent { + return MotionEvent.obtain(time, time, action, x.toFloat(), y.toFloat(), 0) + .also { time += timeStep } + } + + /** Dispatch all of the provided events to the target view. */ + private fun dispatchMotionEvents(vararg events: MotionEvent) { + events.forEach { magnetizedObject.maybeConsumeMotionEvent(it) } + } + + /** Prevents Kotlin from being mad that eq() is nullable. */ + private fun <T> eq(value: T): T = Mockito.eq(value) ?: value +}
\ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index df87ac994d42..a18f5da60cad 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -33,6 +33,9 @@ import android.os.ResultReceiver; */ @SystemApi(client = MODULE_LIBRARIES) public class TetheringConstants { + /** An explicit private class to avoid exposing constructor.*/ + private TetheringConstants() { } + /** * Extra used for communicating with the TetherService. Includes the type of tethering to * enable if any. diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index f9ef0b7b8e9d..a4bea661583b 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -11,6 +11,7 @@ LOCAL_SHARED_LIBRARIES := \ libnativehelper \ libRS \ libcutils \ + libhwui \ liblog \ libutils \ libui \ diff --git a/services/Android.bp b/services/Android.bp index db6e21a62ff3..ef47867eed48 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -29,6 +29,7 @@ filegroup { ":services.usage-sources", ":services.usb-sources", ":services.voiceinteraction-sources", + ":services.wifi-sources", ":service-permission-sources", ":service-statsd-sources", ], @@ -71,6 +72,7 @@ java_library { "services.usage", "services.usb", "services.voiceinteraction", + "services.wifi", "android.hidl.base-V1.0-java", ], diff --git a/services/api/current.txt b/services/api/current.txt index 26a65f21ed83..4a0a0d8e5aef 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -74,3 +74,12 @@ package com.android.server { } +package com.android.server.wifi { + + public class SupplicantManager { + method public static void start(); + method public static void stop(); + } + +} + diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index f544517a79e3..317ce4cb5fee 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1158,6 +1158,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } catch (RemoteException e) { Slog.e(TAG, "Error requesting to hide fill UI", e); } + try { + final InlineSuggestionSession.ImeResponse imeResponse = + mInlineSuggestionSession.waitAndGetImeResponse(); + if (imeResponse == null) { + Log.w(TAG, "Session input method callback is not set yet"); + return; + } + imeResponse.getCallback().onInlineSuggestionsResponse( + new InlineSuggestionsResponse(Collections.EMPTY_LIST)); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException hiding inline suggestions"); + } } } 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 a8886fc06c5a..0d8c89b4124d 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -72,32 +72,35 @@ public final class InlineSuggestionFactory { */ public static InlineSuggestionsResponse createInlineSuggestionsResponse( @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response, - @Nullable String filterText, - @Nullable List<InlinePresentation> inlineActions, - @NonNull AutofillId autofillId, - @NonNull Context context, - @NonNull AutoFillUI.AutoFillUiCallback client, - @NonNull Runnable onErrorCallback, + @Nullable String filterText, @Nullable List<InlinePresentation> inlineActions, + @NonNull AutofillId autofillId, @NonNull Context context, + @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called"); final BiConsumer<Dataset, Integer> onClickFactory; if (response.getAuthentication() != null) { - onClickFactory = (dataset, datasetIndex) -> client.authenticate(response.getRequestId(), - datasetIndex, response.getAuthentication(), response.getClientState(), - /* authenticateInline= */ true); + onClickFactory = (dataset, datasetIndex) -> { + client.requestHideFillUi(autofillId); + client.authenticate(response.getRequestId(), + datasetIndex, response.getAuthentication(), response.getClientState(), + /* authenticateInline= */ true); + }; } else { - onClickFactory = (dataset, datasetIndex) -> - client.fill(response.getRequestId(), datasetIndex, dataset); + onClickFactory = (dataset, datasetIndex) -> { + client.requestHideFillUi(autofillId); + client.fill(response.getRequestId(), datasetIndex, dataset); + }; } final List<Dataset> datasetList = response.getDatasets(); final Dataset[] datasets = datasetList == null ? null : datasetList.toArray(new Dataset[]{}); - - return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, response, - datasets, filterText, inlineActions, autofillId, context, onErrorCallback, - onClickFactory, remoteRenderService); + final InlinePresentation inlineAuthentication = + response.getAuthentication() == null ? null : response.getInlinePresentation(); + return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, + datasets, filterText, inlineAuthentication, inlineActions, autofillId, context, + onErrorCallback, onClickFactory, remoteRenderService); } /** @@ -105,33 +108,31 @@ public final class InlineSuggestionFactory { * autofill service. */ public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse( - @NonNull InlineSuggestionsRequest request, - @NonNull Dataset[] datasets, - @NonNull AutofillId autofillId, - @NonNull Context context, + @NonNull InlineSuggestionsRequest request, @NonNull Dataset[] datasets, + @NonNull AutofillId autofillId, @NonNull Context context, @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called"); return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request, - /* fillResponse= */ null, datasets, /* filterText= */ null, + datasets, /* filterText= */ null, /* inlineAuthentication= */ null, /* inlineActions= */ null, autofillId, context, onErrorCallback, - (dataset, fieldIndex) -> + (dataset, datasetIndex) -> inlineSuggestionUiCallback.autofill(dataset), remoteRenderService); } private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal( boolean isAugmented, @NonNull InlineSuggestionsRequest request, - @Nullable FillResponse response, @Nullable Dataset[] datasets, - @Nullable String filterText, + @Nullable Dataset[] datasets, @Nullable String filterText, + @Nullable InlinePresentation inlineAuthentication, @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId, @NonNull Context context, @NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); - if (response.getAuthentication() != null) { - InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(response, + if (inlineAuthentication != null) { + InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication, remoteRenderService, onClickFactory, onErrorCallback, request.getHostInputToken()); inlineSuggestions.add(inlineAuthSuggestion); @@ -250,11 +251,11 @@ public final class InlineSuggestionFactory { return inlineSuggestion; } - private static InlineSuggestion createInlineAuthSuggestion(@NonNull FillResponse response, + private static InlineSuggestion createInlineAuthSuggestion( + @NonNull InlinePresentation inlinePresentation, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) { - final InlinePresentation inlinePresentation = response.getInlinePresentation(); final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION); diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index 0bcf45d4a526..5e10916c4491 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -47,7 +47,6 @@ import android.os.RemoteException; import android.os.SELinux; import android.os.UserHandle; import android.os.WorkSource; -import android.util.FeatureFlagUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -400,12 +399,6 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { * the transport have no data. */ private void informTransportOfUnchangedApps(Set<String> appsBackedUp) { - // If the feautre is not enabled then we just exit early. - if (!FeatureFlagUtils.isEnabled(mBackupManagerService.getContext(), - FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS)) { - return; - } - String[] succeedingPackages = getSucceedingPackages(); if (succeedingPackages == null) { // Nothing is succeeding, so end early. diff --git a/services/core/Android.bp b/services/core/Android.bp index 228d9bebaae6..4da60b8e4ffd 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -126,6 +126,7 @@ java_library_static { "android.hidl.manager-V1.2-java", "dnsresolver_aidl_interface-V2-java", "netd_event_listener_interface-java", + "ike-stubs", ], plugins: [ diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 0f8d57e3d9e3..8b2bfa10a029 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -237,6 +237,33 @@ public abstract class PackageManagerInternal { public abstract boolean isPackageSuspended(String packageName, int userId); /** + * Removes all package suspensions imposed by any non-system packages. + */ + public abstract void removeAllNonSystemPackageSuspensions(int userId); + + /** + * Removes all suspensions imposed on the given package by non-system packages. + */ + public abstract void removeNonSystemPackageSuspensions(String packageName, int userId); + + /** + * Removes all {@link PackageManager.DistractionRestriction restrictions} set on the given + * package + */ + public abstract void removeDistractingPackageRestrictions(String packageName, int userId); + + /** + * Removes all {@link PackageManager.DistractionRestriction restrictions} set on all the + * packages. + */ + public abstract void removeAllDistractingPackageRestrictions(int userId); + + /** + * Flushes package restrictions for the given user immediately to disk. + */ + public abstract void flushPackageRestrictions(int userId); + + /** * Get the name of the package that suspended the given package. Packages can be suspended by * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or * {@link android.Manifest.permission#SUSPEND_APPS}. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e48ef5a528be..f84d35fb7776 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3302,7 +3302,6 @@ public class ConnectivityService extends IConnectivityManager.Stub for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest request = nai.requestAt(i); final NetworkRequestInfo nri = mNetworkRequests.get(request); - ensureRunningOnConnectivityServiceThread(); final NetworkAgentInfo currentNetwork = nri.mSatisfier; if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { nri.mSatisfier = null; @@ -3454,18 +3453,17 @@ public class ConnectivityService extends IConnectivityManager.Stub // If this Network is already the highest scoring Network for a request, or if // there is hope for it to become one if it validated, then it is needed. - ensureRunningOnConnectivityServiceThread(); if (nri.request.isRequest() && nai.satisfies(nri.request) && - (nai.isSatisfyingRequest(nri.request.requestId) - // Note that canPossiblyBeat catches two important cases: - // 1. Unvalidated slow networks will not be reaped when an unvalidated fast - // network is currently satisfying the request. This is desirable for example - // when cellular ends up validating but WiFi/Ethernet does not. - // 2. Fast networks will not be reaped when a validated slow network is - // currently satisfying the request. This is desirable for example when - // Ethernet ends up validating and out scoring WiFi, or WiFi/Ethernet ends - // up validating and out scoring cellular. - || nai.canPossiblyBeat(nri.mSatisfier))) { + (nai.isSatisfyingRequest(nri.request.requestId) || + // Note that this catches two important cases: + // 1. Unvalidated cellular will not be reaped when unvalidated WiFi + // is currently satisfying the request. This is desirable when + // cellular ends up validating but WiFi does not. + // 2. Unvalidated WiFi will not be reaped when validated cellular + // is currently satisfying the request. This is desirable when + // WiFi ends up validating and out scoring cellular. + nri.mSatisfier.getCurrentScore() + < nai.getCurrentScoreAsValidated())) { return false; } } @@ -3493,7 +3491,6 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mNetworkRequests.get(nri.request) == null) { return; } - ensureRunningOnConnectivityServiceThread(); if (nri.mSatisfier != null) { return; } @@ -3531,7 +3528,6 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("RELEASE " + nri); if (nri.request.isRequest()) { boolean wasKept = false; - ensureRunningOnConnectivityServiceThread(); final NetworkAgentInfo nai = nri.mSatisfier; if (nai != null) { boolean wasBackgroundNetwork = nai.isBackgroundNetwork(); @@ -3843,9 +3839,8 @@ public class ConnectivityService extends IConnectivityManager.Stub return avoidBadWifi(); } + private void rematchForAvoidBadWifiUpdate() { - ensureRunningOnConnectivityServiceThread(); - mixInAllNetworkScores(); rematchAllNetworksAndRequests(); for (NetworkAgentInfo nai: mNetworkAgentInfos.values()) { if (nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { @@ -7071,45 +7066,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - /** - * Re-mixin all network scores. - * This is called when some global setting like avoidBadWifi has changed. - * TODO : remove this when all usages have been removed. - */ - private void mixInAllNetworkScores() { - ensureRunningOnConnectivityServiceThread(); - for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) { - nai.setNetworkScore(mixInNetworkScore(nai, nai.getNetworkScore())); - } - } - - /** - * Mix in the Connectivity-managed parts of the NetworkScore. - * @param nai The NAI this score applies to. - * @param sourceScore the score sent by the network agent, or the previous score of this NAI. - * @return A new score with the Connectivity-managed parts mixed in. - */ - @NonNull - private NetworkScore mixInNetworkScore(@NonNull final NetworkAgentInfo nai, - @NonNull final NetworkScore sourceScore) { - final NetworkScore.Builder score = new NetworkScore.Builder(sourceScore); - - // TODO : this should be done in Telephony. It should be handled per-network because - // it's a carrier-dependent config. - if (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { - if (mMultinetworkPolicyTracker.getAvoidBadWifi()) { - score.clearPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI); - } else { - score.addPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI); - } - } - - return score.build(); - } - private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) { if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + ns); - nai.setNetworkScore(mixInNetworkScore(nai, ns)); + nai.setNetworkScore(ns); rematchAllNetworksAndRequests(); sendUpdatedScoreToFactories(nai); } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 5db5115b4afe..2804d79ef56f 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -149,7 +149,13 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onStart() { + // enable client caches by doing the first invalidate + LocationManager.invalidateLocalLocationEnabledCaches(); + publishBinderService(Context.LOCATION_SERVICE, mService); + // disable caching for whatever process contains LocationManagerService + ((LocationManager) mService.mContext.getSystemService(LocationManager.class)) + .disableLocalLocationEnabledCaches(); } @Override @@ -439,6 +445,7 @@ public class LocationManagerService extends ILocationManager.Stub { private void onLocationModeChanged(int userId) { boolean enabled = mSettingsHelper.isLocationEnabled(userId); + LocationManager.invalidateLocalLocationEnabledCaches(); if (D) { Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); @@ -862,10 +869,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - public void requestSetAllowed(boolean allowed) { - mProvider.requestSetAllowed(allowed); - } - public void onUserStarted(int userId) { synchronized (mLock) { // clear the user's enabled state in order to force a reevalution of whether the @@ -2538,6 +2541,8 @@ public class LocationManagerService extends ILocationManager.Stub { } mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS, "Requires WRITE_SECURE_SETTINGS permission"); + + LocationManager.invalidateLocalLocationEnabledCaches(); mSettingsHelper.setLocationEnabled(enabled, userId); } @@ -2931,18 +2936,6 @@ public class LocationManagerService extends ILocationManager.Stub { private class LocalService extends LocationManagerInternal { @Override - public void requestSetProviderAllowed(String provider, boolean allowed) { - Preconditions.checkArgument(provider != null, "invalid null provider"); - - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(provider); - if (manager != null) { - manager.requestSetAllowed(allowed); - } - } - } - - @Override public boolean isProviderEnabledForUser(@NonNull String provider, int userId) { synchronized (mLock) { LocationProviderManager manager = getLocationProviderManager(provider); diff --git a/services/core/java/com/android/server/MemoryPressureUtil.java b/services/core/java/com/android/server/MemoryPressureUtil.java new file mode 100644 index 000000000000..c34dc2f5a9b1 --- /dev/null +++ b/services/core/java/com/android/server/MemoryPressureUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.os.StrictMode; +import android.util.Slog; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; + +/** + * Utility method for memory pressure (PSI). + */ +public final class MemoryPressureUtil { + private static final String FILE = "/proc/pressure/memory"; + private static final String TAG = "MemoryPressure"; + + /** + * @return a stanza about memory PSI to add to a report. + */ + public static String currentPsiState() { + final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); + StringWriter contents = new StringWriter(); + try { + if (new File(FILE).exists()) { + contents.append("----- Output from /proc/pressure/memory -----\n"); + contents.append(IoUtils.readFileAsString(FILE)); + contents.append("----- End output from /proc/pressure/memory -----\n\n"); + } + } catch (IOException e) { + Slog.e(TAG, "Could not read " + FILE, e); + } finally { + StrictMode.setThreadPolicy(savedPolicy); + } + return contents.toString(); + } + + private MemoryPressureUtil(){} +} diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java index 179780d88084..8f914fe6f59f 100644 --- a/services/core/java/com/android/server/SystemServerInitThreadPool.java +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -175,6 +175,6 @@ public class SystemServerInitThreadPool { final ArrayList<Integer> pids = new ArrayList<>(); pids.add(Process.myPid()); ActivityManagerService.dumpStackTraces(pids, null, null, - Watchdog.getInterestingNativePids()); + Watchdog.getInterestingNativePids(), null); } } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 8900eee6f50f..ac7867f29a79 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -50,6 +50,7 @@ import com.android.server.wm.SurfaceAnimationThread; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -583,7 +584,7 @@ public class Watchdog extends Thread { ArrayList<Integer> pids = new ArrayList<Integer>(); pids.add(Process.myPid()); ActivityManagerService.dumpStackTraces(pids, null, null, - getInterestingNativePids()); + getInterestingNativePids(), null); waitedHalf = true; } continue; @@ -609,16 +610,21 @@ public class Watchdog extends Thread { if (mPhonePid > 0) pids.add(mPhonePid); long anrTime = SystemClock.uptimeMillis(); + StringBuilder report = new StringBuilder(); + report.append(MemoryPressureUtil.currentPsiState()); ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false); + StringWriter tracesFileException = new StringWriter(); final File stack = ActivityManagerService.dumpStackTraces( - pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids()); + pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(), + tracesFileException); // Give some extra time to make sure the stack traces get written. // The system's been hanging for a minute, another second or two won't hurt much. SystemClock.sleep(5000); processCpuTracker.update(); - String cpuInfo = processCpuTracker.printCurrentState(anrTime); + report.append(processCpuTracker.printCurrentState(anrTime)); + report.append(tracesFileException.getBuffer()); // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log doSysRq('w'); @@ -634,7 +640,7 @@ public class Watchdog extends Thread { if (mActivity != null) { mActivity.addErrorToDropBox( "watchdog", null, "system_server", null, null, null, - subject, cpuInfo, stack, null); + subject, report.toString(), stack, null); } FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject); diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index f2a8615dca88..0d161b943d15 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -134,9 +134,13 @@ public class AdbService extends IAdbManager.Stub { mIsAdbWifiEnabled = false; // register observer to listen for settings changes + mObserver = new AdbSettingsObserver(); mContentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), - false, new AdbSettingsObserver()); + false, mObserver); + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), + false, mObserver); } catch (Exception e) { Slog.e(TAG, "Error in initAdbState", e); } @@ -153,6 +157,7 @@ public class AdbService extends IAdbManager.Stub { private class AdbSettingsObserver extends ContentObserver { private final Uri mAdbUsbUri = Settings.Global.getUriFor(Settings.Global.ADB_ENABLED); + private final Uri mAdbWifiUri = Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED); AdbSettingsObserver() { super(null); @@ -166,8 +171,13 @@ public class AdbService extends IAdbManager.Stub { FgThread.getHandler().sendMessage(obtainMessage( AdbService::setAdbEnabled, AdbService.this, shouldEnable, AdbTransportType.USB)); + } else if (mAdbWifiUri.equals(uri)) { + boolean shouldEnable = (Settings.Global.getInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, 0) > 0); + FgThread.getHandler().sendMessage(obtainMessage( + AdbService::setAdbEnabled, AdbService.this, shouldEnable, + AdbTransportType.WIFI)); } - // TODO(joshuaduong): Add condition for WIFI transport } } @@ -188,6 +198,8 @@ public class AdbService extends IAdbManager.Stub { private boolean mIsAdbWifiEnabled; private AdbDebuggingManager mDebuggingManager; + private ContentObserver mObserver; + private AdbService(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -213,6 +225,8 @@ public class AdbService extends IAdbManager.Stub { try { Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, mIsAdbUsbEnabled ? 1 : 0); + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_WIFI_ENABLED, mIsAdbWifiEnabled ? 1 : 0); } catch (SecurityException e) { // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed. Slog.d(TAG, "ADB_ENABLED is restricted."); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0d8eff5c11cc..6c0623021025 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3851,10 +3851,11 @@ public class ActivityManagerService extends IActivityManager.Stub * @param firstPids of dalvik VM processes to dump stack traces for first * @param lastPids of dalvik VM processes to dump stack traces for last * @param nativePids optional list of native pids to dump stack crawls + * @param logExceptionCreatingFile optional writer to which we log errors creating the file */ public static File dumpStackTraces(ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, - ArrayList<Integer> nativePids) { + ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile) { ArrayList<Integer> extraPids = null; Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids); @@ -3894,8 +3895,15 @@ public class ActivityManagerService extends IActivityManager.Stub // NOTE: We should consider creating the file in native code atomically once we've // gotten rid of the old scheme of dumping and lot of the code that deals with paths // can be removed. - File tracesFile = createAnrDumpFile(tracesDir); - if (tracesFile == null) { + File tracesFile; + try { + tracesFile = createAnrDumpFile(tracesDir); + } catch (IOException e) { + Slog.w(TAG, "Exception creating ANR dump file:", e); + if (logExceptionCreatingFile != null) { + logExceptionCreatingFile.append("----- Exception creating ANR dump file -----\n"); + e.printStackTrace(new PrintWriter(logExceptionCreatingFile)); + } return null; } @@ -3906,7 +3914,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("ActivityManagerService.class") private static SimpleDateFormat sAnrFileDateFormat; - private static synchronized File createAnrDumpFile(File tracesDir) { + private static synchronized File createAnrDumpFile(File tracesDir) throws IOException { if (sAnrFileDateFormat == null) { sAnrFileDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS"); } @@ -3914,18 +3922,12 @@ public class ActivityManagerService extends IActivityManager.Stub final String formattedDate = sAnrFileDateFormat.format(new Date()); final File anrFile = new File(tracesDir, "anr_" + formattedDate); - try { - if (anrFile.createNewFile()) { - FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw------- - return anrFile; - } else { - Slog.w(TAG, "Unable to create ANR dump file: createNewFile failed"); - } - } catch (IOException ioe) { - Slog.w(TAG, "Exception creating ANR dump file:", ioe); + if (anrFile.createNewFile()) { + FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw------- + return anrFile; + } else { + throw new IOException("Unable to create ANR dump file: createNewFile failed"); } - - return null; } /** @@ -7271,7 +7273,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Wait for the provider to be published... final long timeout = - SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS; + SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS; boolean timedOut = false; synchronized (cpr) { while (cpr.provider == null) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index a2ae6786d95d..c239feb155c6 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1128,6 +1128,7 @@ public final class OomAdjuster { app.setCurRawAdj(app.maxAdj); app.setHasForegroundActivities(false); app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT); + app.curCapability = PROCESS_CAPABILITY_ALL; app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT); // System processes can do UI, and when they do we want to have // them trim their memory after the user leaves the UI. To diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index e63da9b1e80b..c2f03ec8d6c4 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -66,11 +66,13 @@ import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.Zygote; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.MemoryPressureUtil; import com.android.server.wm.WindowProcessController; import com.android.server.wm.WindowProcessListener; import java.io.File; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -1581,6 +1583,8 @@ class ProcessRecord implements WindowProcessListener { info.append("Parent: ").append(parentShortComponentName).append("\n"); } + StringBuilder report = new StringBuilder(); + report.append(MemoryPressureUtil.currentPsiState()); ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); // don't dump native PIDs for background ANRs unless it is the process of interest @@ -1608,19 +1612,20 @@ class ProcessRecord implements WindowProcessListener { // For background ANRs, don't pass the ProcessCpuTracker to // avoid spending 1/2 second collecting stats to rank lastPids. + StringWriter tracesFileException = new StringWriter(); File tracesFile = ActivityManagerService.dumpStackTraces(firstPids, (isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids, - nativePids); + nativePids, tracesFileException); - String cpuInfo = null; if (isMonitorCpuUsage()) { mService.updateCpuStatsNow(); synchronized (mService.mProcessCpuTracker) { - cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime); + report.append(mService.mProcessCpuTracker.printCurrentState(anrTime)); } info.append(processCpuTracker.printCurrentLoad()); - info.append(cpuInfo); + info.append(report); } + report.append(tracesFileException.getBuffer()); info.append(processCpuTracker.printCurrentState(anrTime)); @@ -1645,7 +1650,8 @@ class ProcessRecord implements WindowProcessListener { final ProcessRecord parentPr = parentProcess != null ? (ProcessRecord) parentProcess.mOwner : null; mService.addErrorToDropBox("anr", this, processName, activityShortComponentName, - parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null); + parentShortComponentName, parentPr, annotation, report.toString(), tracesFile, + null); if (mWindowProcessController.appNotResponding(info.toString(), () -> kill("anr", ApplicationExitInfo.REASON_ANR, true), diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 5d8a0f6161cd..4670d58ca63b 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -401,10 +401,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts="); pw.println(mHasStartedWhitelistingBgActivityStarts); } - if (mAllowWhileInUsePermissionInFgs) { - pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); - pw.println(mAllowWhileInUsePermissionInFgs); - } + pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); + pw.println(mAllowWhileInUsePermissionInFgs); + pw.print(prefix); pw.print("recentCallingPackage="); + pw.println(mRecentCallingPackage); if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); } diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 204f072a72fc..8ed221d943ef 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -311,6 +311,7 @@ public class AuthService extends SystemService { } authenticator = new FingerprintAuthenticator(fingerprintService); + fingerprintService.initConfiguredStrength(config.mStrength); break; case TYPE_FACE: @@ -322,6 +323,7 @@ public class AuthService extends SystemService { } authenticator = new FaceAuthenticator(faceService); + faceService.initConfiguredStrength(config.mStrength); break; case TYPE_IRIS: @@ -333,6 +335,7 @@ public class AuthService extends SystemService { } authenticator = new IrisAuthenticator(irisService); + irisService.initConfiguredStrength(config.mStrength); break; default: diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 0e709944b03f..74c70df6224c 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricService; @@ -106,6 +107,7 @@ public abstract class BiometricServiceBase extends SystemService private PerformanceStats mPerformanceStats; protected int mCurrentUserId = UserHandle.USER_NULL; protected long mHalDeviceId; + private int mOEMStrength; // Tracks the OEM configured biometric modality strength // Tracks if the current authentication makes use of CryptoObjects. protected boolean mIsCrypto; // Normal authentications are tracked by mPerformanceMap. @@ -681,6 +683,20 @@ public abstract class BiometricServiceBase extends SystemService statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH); } + protected void initConfiguredStrengthInternal(int strength) { + if (DEBUG) { + Slog.d(getTag(), "initConfiguredStrengthInternal(" + strength + ")"); + } + mOEMStrength = strength; + } + + protected boolean isStrongBiometric() { + // TODO(b/141025588): need to calculate actual strength when downgrading tiers + final int biometricBits = mOEMStrength + & BiometricManager.Authenticators.BIOMETRIC_MIN_STRENGTH; + return biometricBits == BiometricManager.Authenticators.BIOMETRIC_STRONG; + } + protected ClientMonitor getCurrentClient() { return mCurrentClient; } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 31c3d4d7b24e..a87a455fa2c1 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -740,6 +740,12 @@ public class FaceService extends BiometricServiceBase { } return 0; } + + @Override // Binder call + public void initConfiguredStrength(int strength) { + checkPermission(USE_BIOMETRIC_INTERNAL); + initConfiguredStrengthInternal(strength); + } } /** @@ -809,7 +815,7 @@ public class FaceService extends BiometricServiceBase { if (mFaceServiceReceiver != null) { if (biometric == null || biometric instanceof Face) { mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face) biometric, - userId); + userId, isStrongBiometric()); } else { Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric"); } diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 0a6198863b00..83aa9cf5ad2c 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FINGERPRINT; import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.USE_BIOMETRIC; +import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; @@ -462,6 +463,12 @@ public class FingerprintService extends BiometricServiceBase { checkPermission(MANAGE_FINGERPRINT); mClientActiveCallbacks.remove(callback); } + + @Override // Binder call + public void initConfiguredStrength(int strength) { + checkPermission(USE_BIOMETRIC_INTERNAL); + initConfiguredStrengthInternal(strength); + } } /** @@ -526,8 +533,8 @@ public class FingerprintService extends BiometricServiceBase { throws RemoteException { if (mFingerprintServiceReceiver != null) { if (biometric == null || biometric instanceof Fingerprint) { - mFingerprintServiceReceiver - .onAuthenticationSucceeded(deviceId, (Fingerprint) biometric, userId); + mFingerprintServiceReceiver.onAuthenticationSucceeded(deviceId, + (Fingerprint) biometric, userId, isStrongBiometric()); } else { Slog.e(TAG, "onAuthenticationSucceeded received non-fingerprint biometric"); } diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java index 2817315ac175..903ae6b205b9 100644 --- a/services/core/java/com/android/server/biometrics/iris/IrisService.java +++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java @@ -16,9 +16,12 @@ package com.android.server.biometrics.iris; +import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; + import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.iris.IIrisService; import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; @@ -42,6 +45,17 @@ public class IrisService extends BiometricServiceBase { private static final String TAG = "IrisService"; /** + * Receives the incoming binder calls from IrisManager. + */ + private final class IrisServiceWrapper extends IIrisService.Stub { + @Override // Binder call + public void initConfiguredStrength(int strength) { + checkPermission(USE_BIOMETRIC_INTERNAL); + initConfiguredStrengthInternal(strength); + } + } + + /** * Initializes the system service. * <p> * Subclasses must define a single argument constructor that accepts the context @@ -57,6 +71,7 @@ public class IrisService extends BiometricServiceBase { @Override public void onStart() { super.onStart(); + publishBinderService(Context.IRIS_SERVICE, new IrisServiceWrapper()); } @Override diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 3860904a3fed..4612cfd0f7cb 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -16,8 +16,6 @@ package com.android.server.connectivity; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.transportNamesOf; import android.annotation.NonNull; @@ -477,16 +475,24 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN); } - /** Gets the current score */ - public int getCurrentScore() { + private int getCurrentScore(boolean pretendValidated) { + // TODO: We may want to refactor this into a NetworkScore class that takes a base score from + // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the + // score. The NetworkScore class would provide a nice place to centralize score constants + // so they are not scattered about the transports. + // If this network is explicitly selected and the user has decided to use it even if it's - // unvalidated, give it the maximum score. - if (networkAgentConfig.explicitlySelected && networkAgentConfig.acceptUnvalidated) { + // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly + // selected and we're trying to see what its score could be. This ensures that we don't tear + // down an explicitly selected network before the user gets a chance to prefer it when + // a higher-scoring network (e.g., Ethernet) is available. + if (networkAgentConfig.explicitlySelected + && (networkAgentConfig.acceptUnvalidated || pretendValidated)) { return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; } int score = mNetworkScore.getLegacyScore(); - if (!lastValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { + if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; } if (score < 0) score = 0; @@ -502,6 +508,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return isWifi && !avoidBadWifi && everValidated; } + // Get the current score for this Network. This may be modified from what the + // NetworkAgent sent, as it has modifiers applied to it. + public int getCurrentScore() { + return getCurrentScore(false); + } + + // Get the current score for this Network as if it was validated. This may be modified from + // what the NetworkAgent sent, as it has modifiers applied to it. + public int getCurrentScoreAsValidated() { + return getCurrentScore(true); + } + public void setNetworkScore(@NonNull NetworkScore ns) { mNetworkScore = ns; } @@ -611,41 +629,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mLingering = false; } - /** - * Returns whether this NAI has any chance of ever beating this other agent. - * - * The chief use case of this is the decision to tear down this network. ConnectivityService - * tears down networks that don't satisfy any request, unless they have a chance to beat any - * existing satisfier. - * - * @param other the agent to beat - * @return whether this should be given more time to try and beat the other agent - * TODO : remove this and migrate to a ranker-based approach - */ - public boolean canPossiblyBeat(@NonNull final NetworkAgentInfo other) { - // Any explicitly selected network should be held on. - if (networkAgentConfig.explicitlySelected) return true; - // An outscored exiting network should be torn down. - if (mNetworkScore.isExiting()) return false; - // If this network is validated it can be torn down as it can't hope to be better than - // it already is. - if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return false; - // If neither network is validated, keep both until at least one does. - if (!other.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return true; - // If this network is not metered but the other is, it should be preferable if it validates. - if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) - && !other.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { - return true; - } - - // If the control comes here : - // • This network is neither exiting or explicitly selected - // • This network is not validated, but the other is - // • This network is metered, or both networks are unmetered - // Keep it if it's expected to be faster than the other., should it validate. - return mNetworkScore.probablyFasterThan(other.mNetworkScore); - } - public void dumpLingerTimers(PrintWriter pw) { for (LingerTimer timer : mLingerTimers) { pw.println(timer); } } diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java index 80d46e0370b4..d0aabf95d572 100644 --- a/services/core/java/com/android/server/connectivity/NetworkRanker.java +++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java @@ -16,19 +16,10 @@ package com.android.server.connectivity; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkScore.POLICY_IGNORE_ON_WIFI; - -import static com.android.internal.util.FunctionalUtils.findFirst; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.NetworkCapabilities; import android.net.NetworkRequest; -import java.util.ArrayList; import java.util.Collection; /** @@ -40,75 +31,20 @@ public class NetworkRanker { /** * Find the best network satisfying this request among the list of passed networks. */ + // Almost equivalent to Collections.max(nais), but allows returning null if no network + // satisfies the request. @Nullable public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request, @NonNull final Collection<NetworkAgentInfo> nais) { - final ArrayList<NetworkAgentInfo> candidates = new ArrayList<>(nais); - candidates.removeIf(nai -> !nai.satisfies(request)); - - // Enforce policy. The order in which the policy is computed is essential, because each - // step may remove some of the candidates. For example, filterValidated drops non-validated - // networks in presence of validated networks for INTERNET requests, but the bad wifi - // avoidance policy takes priority over this, so it must be done before. - filterVpn(candidates); - filterExplicitlySelected(candidates); - filterBadWifiAvoidance(candidates); - filterValidated(request, candidates); - NetworkAgentInfo bestNetwork = null; int bestScore = Integer.MIN_VALUE; - for (final NetworkAgentInfo nai : candidates) { - final int score = nai.getNetworkScore().getLegacyScore(); - if (score > bestScore) { + for (final NetworkAgentInfo nai : nais) { + if (!nai.satisfies(request)) continue; + if (nai.getCurrentScore() > bestScore) { bestNetwork = nai; - bestScore = score; + bestScore = nai.getCurrentScore(); } } return bestNetwork; } - - // If a network is a VPN it has priority. - private void filterVpn(@NonNull final ArrayList<NetworkAgentInfo> candidates) { - final NetworkAgentInfo vpn = findFirst(candidates, - nai -> nai.networkCapabilities.hasTransport(TRANSPORT_VPN)); - if (null == vpn) return; // No VPN : this policy doesn't apply. - candidates.removeIf(nai -> !nai.networkCapabilities.hasTransport(TRANSPORT_VPN)); - } - - // If some network is explicitly selected and set to accept unvalidated connectivity, then - // drop all networks that are not explicitly selected. - private void filterExplicitlySelected( - @NonNull final ArrayList<NetworkAgentInfo> candidates) { - final NetworkAgentInfo explicitlySelected = findFirst(candidates, - nai -> nai.networkAgentConfig.explicitlySelected - && nai.networkAgentConfig.acceptUnvalidated); - if (null == explicitlySelected) return; // No explicitly selected network accepting unvalid - candidates.removeIf(nai -> !nai.networkAgentConfig.explicitlySelected); - } - - // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI. - private void filterBadWifiAvoidance(@NonNull final ArrayList<NetworkAgentInfo> candidates) { - final NetworkAgentInfo wifi = findFirst(candidates, - nai -> nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) - && nai.everValidated - // Horrible hack : there is old UI that will let a user say they want to - // override the policy only for this network only at this time and it - // feeds into the following member. This old UI should probably be removed - // but for now keep backward compatibility. - && !nai.avoidUnvalidated); - if (null == wifi) return; // No wifi : this policy doesn't apply - candidates.removeIf(nai -> nai.getNetworkScore().hasPolicy(POLICY_IGNORE_ON_WIFI)); - } - - // If some network is validated and the request asks for INTERNET, drop all networks that are - // not validated. - private void filterValidated(@NonNull final NetworkRequest request, - @NonNull final ArrayList<NetworkAgentInfo> candidates) { - if (!request.hasCapability(NET_CAPABILITY_INTERNET)) return; - final NetworkAgentInfo validated = findFirst(candidates, - nai -> nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)); - if (null == validated) return; // No validated network - candidates.removeIf(nai -> - !nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)); - } } diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 8bbeabf779d2..7cb84585a57c 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -73,7 +73,7 @@ public class DisplayModeDirector { private static final int GLOBAL_ID = -1; // The tolerance within which we consider something approximately equals. - private static final float EPSILON = 0.01f; + private static final float FLOAT_TOLERANCE = 0.01f; private final Object mLock = new Object(); private final Context mContext; @@ -267,8 +267,8 @@ public class DisplayModeDirector { // Some refresh rates are calculated based on frame timings, so they aren't *exactly* // equal to expected refresh rate. Given that, we apply a bit of tolerance to this // comparison. - if (refreshRate < (minRefreshRate - EPSILON) - || refreshRate > (maxRefreshRate + EPSILON)) { + if (refreshRate < (minRefreshRate - FLOAT_TOLERANCE) + || refreshRate > (maxRefreshRate + FLOAT_TOLERANCE)) { if (DEBUG) { Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", outside refresh rate bounds" @@ -487,12 +487,18 @@ public class DisplayModeDirector { public RefreshRateRange() {} public RefreshRateRange(float min, float max) { - if (min < 0 || max < 0 || min > max) { + if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " + min + " " + max); this.min = this.max = 0; return; } + if (min > max) { + // Min and max are within epsilon of each other, but in the wrong order. + float t = min; + min = max; + max = t; + } this.min = min; this.max = max; } diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index 433ec435c8d1..e9d94a5430fe 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -366,21 +366,6 @@ public abstract class AbstractLocationProvider { protected abstract void onExtraCommand(int uid, int pid, String command, Bundle extras); /** - * Requests a provider to enable itself for the given user id. - */ - public final void requestSetAllowed(boolean allowed) { - // all calls into the provider must be moved onto the provider thread to prevent deadlock - mExecutor.execute( - obtainRunnable(AbstractLocationProvider::onRequestSetAllowed, this, allowed) - .recycleOnUse()); - } - - /** - * Always invoked on the provider executor. - */ - protected abstract void onRequestSetAllowed(boolean allowed); - - /** * Dumps debug or log information. May be invoked from any thread. */ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 5f44e042b5e9..685fb9eb38f5 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -1229,11 +1229,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } - @Override - protected void onRequestSetAllowed(boolean allowed) { - // do nothing - the gnss provider is always allowed - } - private void deleteAidingData(Bundle extras) { int flags; diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 96ffaa6c0bff..87208a7f36f5 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -205,14 +205,6 @@ public class LocationProviderProxy extends AbstractLocationProvider { } @Override - public void onRequestSetAllowed(boolean allowed) { - mServiceWatcher.runOnBinder(binder -> { - ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - service.requestSetAllowed(allowed); - }); - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mServiceWatcher.dump(fd, pw, args); } diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index b45b66017062..5ec06ca25581 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -64,11 +64,6 @@ public class MockProvider extends AbstractLocationProvider { protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} @Override - protected void onRequestSetAllowed(boolean allowed) { - setAllowed(allowed); - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("last mock location=" + mLocation); } diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java index f43669e488d1..0f358e97bc98 100644 --- a/services/core/java/com/android/server/location/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/MockableLocationProvider.java @@ -224,15 +224,6 @@ public class MockableLocationProvider extends AbstractLocationProvider { } } - @Override - protected void onRequestSetAllowed(boolean allowed) { - synchronized (mOwnerLock) { - if (mProvider != null) { - mProvider.onRequestSetAllowed(allowed); - } - } - } - /** * Dumps the current provider implementation. */ diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java index 54dffff8b1df..1ba38cc4d999 100644 --- a/services/core/java/com/android/server/location/PassiveProvider.java +++ b/services/core/java/com/android/server/location/PassiveProvider.java @@ -79,10 +79,5 @@ public class PassiveProvider extends AbstractLocationProvider { protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} @Override - protected void onRequestSetAllowed(boolean allowed) { - // do nothing - the passive provider is always allowed - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {} } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 1f4048f821e5..15dfab93ed27 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -17,6 +17,7 @@ package com.android.server.locksettings; import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; +import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.READ_CONTACTS; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -178,6 +179,7 @@ import javax.crypto.spec.GCMParameterSpec; public class LockSettingsService extends ILockSettings.Stub { private static final String TAG = "LockSettingsService"; private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE; + private static final String BIOMETRIC_PERMISSION = MANAGE_BIOMETRIC; private static final boolean DEBUG = false; private static final int PROFILE_KEY_IV_SIZE = 12; @@ -1050,6 +1052,10 @@ public class LockSettingsService extends ILockSettings.Stub { } } + private final void checkBiometricPermission() { + mContext.enforceCallingOrSelfPermission(BIOMETRIC_PERMISSION, "LockSettingsBiometric"); + } + @Override public boolean hasSecureLockScreen() { return mHasSecureLockScreen; @@ -2304,6 +2310,18 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override + public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) { + checkBiometricPermission(); + mStrongAuth.reportSuccessfulBiometricUnlock(isStrongBiometric, userId); + } + + @Override + public void scheduleNonStrongBiometricIdleTimeout(int userId) { + checkBiometricPermission(); + mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId); + } + + @Override public void userPresent(int userId) { checkWritePermission(userId); mStrongAuth.reportUnlock(userId); @@ -3191,6 +3209,12 @@ public class LockSettingsService extends ILockSettings.Stub { mStorage.dump(pw); pw.println(); pw.decreaseIndent(); + + pw.println("StrongAuth:"); + pw.increaseIndent(); + mStrongAuth.dump(pw); + pw.println(); + pw.decreaseIndent(); } /** diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java index 91cf53ee9a1f..fbee6f4bcbf0 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -17,6 +17,7 @@ package com.android.server.locksettings; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; import android.app.AlarmManager; @@ -32,8 +33,10 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; +import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; /** @@ -42,6 +45,7 @@ import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; public class LockSettingsStrongAuth { private static final String TAG = "LockSettings"; + private static final boolean DEBUG = false; private static final int MSG_REQUIRE_STRONG_AUTH = 1; private static final int MSG_REGISTER_TRACKER = 2; @@ -49,15 +53,40 @@ public class LockSettingsStrongAuth { private static final int MSG_REMOVE_USER = 4; private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5; private static final int MSG_NO_LONGER_REQUIRE_STRONG_AUTH = 6; + private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT = 7; + private static final int MSG_STRONG_BIOMETRIC_UNLOCK = 8; + private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT = 9; private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG = "LockSettingsStrongAuth.timeoutForUser"; + private static final String NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG = + "LockSettingsPrimaryAuth.nonStrongBiometricTimeoutForUser"; + private static final String NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG = + "LockSettingsPrimaryAuth.nonStrongBiometricIdleTimeoutForUser"; + + /** + * Default and maximum timeout in milliseconds after which unlocking with weak auth times out, + * i.e. the user has to use a strong authentication method like password, PIN or pattern. + */ + public static final long DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS = 24 * 60 * 60 * 1000; // 24h + public static final long DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS = + 4 * 60 * 60 * 1000; // 4h private final RemoteCallbackList<IStrongAuthTracker> mTrackers = new RemoteCallbackList<>(); private final SparseIntArray mStrongAuthForUser = new SparseIntArray(); + private final SparseBooleanArray mIsNonStrongBiometricAllowedForUser = new SparseBooleanArray(); private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener> mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>(); + // Track non-strong biometric timeout + private final ArrayMap<Integer, NonStrongBiometricTimeoutAlarmListener> + mNonStrongBiometricTimeoutAlarmListener = new ArrayMap<>(); + // Track non-strong biometric idle timeout + private final ArrayMap<Integer, NonStrongBiometricIdleTimeoutAlarmListener> + mNonStrongBiometricIdleTimeoutAlarmListener = new ArrayMap<>(); + private final int mDefaultStrongAuthFlags; + private final boolean mDefaultIsNonStrongBiometricAllowed = true; + private final Context mContext; private AlarmManager mAlarmManager; @@ -80,6 +109,17 @@ public class LockSettingsStrongAuth { Slog.e(TAG, "Exception while adding StrongAuthTracker.", e); } } + + for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) { + int key = mIsNonStrongBiometricAllowedForUser.keyAt(i); + boolean value = mIsNonStrongBiometricAllowedForUser.valueAt(i); + try { + tracker.onIsNonStrongBiometricAllowedChanged(value, key); + } catch (RemoteException e) { + Slog.e(TAG, "Exception while adding StrongAuthTracker: " + + "IsNonStrongBiometricAllowedChanged.", e); + } + } } private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) { @@ -134,6 +174,13 @@ public class LockSettingsStrongAuth { mStrongAuthForUser.removeAt(index); notifyStrongAuthTrackers(mDefaultStrongAuthFlags, userId); } + + index = mIsNonStrongBiometricAllowedForUser.indexOfKey(userId); + if (index >= 0) { + mIsNonStrongBiometricAllowedForUser.removeAt(index); + notifyStrongAuthTrackersForIsNonStrongBiometricAllowed( + mDefaultIsNonStrongBiometricAllowed, userId); + } } private void handleScheduleStrongAuthTimeout(int userId) { @@ -151,6 +198,125 @@ public class LockSettingsStrongAuth { // schedule a new alarm listener for the user mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm, mHandler); + + // cancel current non-strong biometric alarm listener for the user (if there was one) + cancelNonStrongBiometricAlarmListener(userId); + // cancel current non-strong biometric idle alarm listener for the user (if there was one) + cancelNonStrongBiometricIdleAlarmListener(userId); + // re-allow unlock with non-strong biometrics + setIsNonStrongBiometricAllowed(true, userId); + } + + private void handleScheduleNonStrongBiometricTimeout(int userId) { + if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricTimeout for userId=" + userId); + long when = SystemClock.elapsedRealtime() + DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS; + NonStrongBiometricTimeoutAlarmListener alarm = mNonStrongBiometricTimeoutAlarmListener + .get(userId); + if (alarm != null) { + // Unlock with non-strong biometric will not affect the existing non-strong biometric + // timeout alarm + if (DEBUG) { + Slog.d(TAG, "There is an existing alarm for non-strong biometric" + + " fallback timeout, so do not re-schedule"); + } + } else { + if (DEBUG) { + Slog.d(TAG, "Schedule a new alarm for non-strong biometric fallback timeout"); + } + alarm = new NonStrongBiometricTimeoutAlarmListener(userId); + mNonStrongBiometricTimeoutAlarmListener.put(userId, alarm); + // schedule a new alarm listener for the user + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, + NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG, alarm, mHandler); + } + + // cancel current non-strong biometric idle alarm listener for the user (if there was one) + cancelNonStrongBiometricIdleAlarmListener(userId); + } + + private void handleStrongBiometricUnlock(int userId) { + if (DEBUG) Slog.d(TAG, "handleStrongBiometricUnlock for userId=" + userId); + // cancel current non-strong biometric alarm listener for the user (if there was one) + cancelNonStrongBiometricAlarmListener(userId); + // cancel current non-strong biometric idle alarm listener for the user (if there was one) + cancelNonStrongBiometricIdleAlarmListener(userId); + // re-allow unlock with non-strong biometrics + setIsNonStrongBiometricAllowed(true, userId); + } + + private void cancelNonStrongBiometricAlarmListener(int userId) { + if (DEBUG) Slog.d(TAG, "cancelNonStrongBiometricAlarmListener for userId=" + userId); + NonStrongBiometricTimeoutAlarmListener alarm = mNonStrongBiometricTimeoutAlarmListener + .get(userId); + if (alarm != null) { + if (DEBUG) Slog.d(TAG, "Cancel alarm for non-strong biometric fallback timeout"); + mAlarmManager.cancel(alarm); + // need to remove the alarm when cancelled by primary auth or strong biometric + mNonStrongBiometricTimeoutAlarmListener.remove(userId); + } + } + + private void cancelNonStrongBiometricIdleAlarmListener(int userId) { + if (DEBUG) Slog.d(TAG, "cancelNonStrongBiometricIdleAlarmListener for userId=" + userId); + // cancel idle alarm listener by any unlocks (i.e. primary auth, strong biometric, + // non-strong biometric) + NonStrongBiometricIdleTimeoutAlarmListener alarm = + mNonStrongBiometricIdleTimeoutAlarmListener.get(userId); + if (alarm != null) { + if (DEBUG) Slog.d(TAG, "Cancel alarm for non-strong biometric idle timeout"); + mAlarmManager.cancel(alarm); + } + } + + private void setIsNonStrongBiometricAllowed(boolean allowed, int userId) { + if (DEBUG) { + Slog.d(TAG, "setIsNonStrongBiometricAllowed for allowed=" + allowed + + ", userId=" + userId); + } + if (userId == UserHandle.USER_ALL) { + for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) { + int key = mIsNonStrongBiometricAllowedForUser.keyAt(i); + setIsNonStrongBiometricAllowedOneUser(allowed, key); + } + } else { + setIsNonStrongBiometricAllowedOneUser(allowed, userId); + } + } + + private void setIsNonStrongBiometricAllowedOneUser(boolean allowed, int userId) { + if (DEBUG) { + Slog.d(TAG, "setIsNonStrongBiometricAllowedOneUser for allowed=" + allowed + + ", userId=" + userId); + } + boolean oldValue = mIsNonStrongBiometricAllowedForUser.get(userId, + mDefaultIsNonStrongBiometricAllowed); + if (allowed != oldValue) { + if (DEBUG) { + Slog.d(TAG, "mIsNonStrongBiometricAllowedForUser value changed:" + + " oldValue=" + oldValue + ", allowed=" + allowed); + } + mIsNonStrongBiometricAllowedForUser.put(userId, allowed); + notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(allowed, userId); + } + } + + private void handleScheduleNonStrongBiometricIdleTimeout(int userId) { + if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricIdleTimeout for userId=" + userId); + long when = SystemClock.elapsedRealtime() + DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS; + // cancel current alarm listener for the user (if there was one) + NonStrongBiometricIdleTimeoutAlarmListener alarm = + mNonStrongBiometricIdleTimeoutAlarmListener.get(userId); + if (alarm != null) { + if (DEBUG) Slog.d(TAG, "Cancel existing alarm for non-strong biometric idle timeout"); + mAlarmManager.cancel(alarm); + } else { + alarm = new NonStrongBiometricIdleTimeoutAlarmListener(userId); + mNonStrongBiometricIdleTimeoutAlarmListener.put(userId, alarm); + } + // schedule a new alarm listener for the user + if (DEBUG) Slog.d(TAG, "Schedule a new alarm for non-strong biometric idle timeout"); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, + NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG, alarm, mHandler); } private void notifyStrongAuthTrackers(int strongAuthReason, int userId) { @@ -170,6 +336,29 @@ public class LockSettingsStrongAuth { } } + private void notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(boolean allowed, + int userId) { + if (DEBUG) { + Slog.d(TAG, "notifyStrongAuthTrackersForIsNonStrongBiometricAllowed" + + " for allowed=" + allowed + ", userId=" + userId); + } + int i = mTrackers.beginBroadcast(); + try { + while (i > 0) { + i--; + try { + mTrackers.getBroadcastItem(i).onIsNonStrongBiometricAllowedChanged( + allowed, userId); + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying StrongAuthTracker: " + + "IsNonStrongBiometricAllowedChanged.", e); + } + } + } finally { + mTrackers.finishBroadcast(); + } + } + public void registerStrongAuthTracker(IStrongAuthTracker tracker) { mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget(); } @@ -207,11 +396,45 @@ public class LockSettingsStrongAuth { requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId); } + /** + * Report successful unlocking with primary auth + */ public void reportSuccessfulStrongAuthUnlock(int userId) { final int argNotUsed = 0; mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget(); } + /** + * Report successful unlocking with biometric + */ + public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) { + if (DEBUG) { + Slog.d(TAG, "reportSuccessfulBiometricUnlock for isStrongBiometric=" + + isStrongBiometric + ", userId=" + userId); + } + final int argNotUsed = 0; + if (isStrongBiometric) { // unlock with strong biometric + mHandler.obtainMessage(MSG_STRONG_BIOMETRIC_UNLOCK, userId, argNotUsed) + .sendToTarget(); + } else { // unlock with non-strong biometric (i.e. weak or convenience) + mHandler.obtainMessage(MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT, userId, argNotUsed) + .sendToTarget(); + } + } + + /** + * Schedule idle timeout for non-strong biometric (i.e. weak or convenience) + */ + public void scheduleNonStrongBiometricIdleTimeout(int userId) { + if (DEBUG) Slog.d(TAG, "scheduleNonStrongBiometricIdleTimeout for userId=" + userId); + final int argNotUsed = 0; + mHandler.obtainMessage(MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT, userId, argNotUsed) + .sendToTarget(); + } + + /** + * Alarm of fallback timeout for primary auth + */ private class StrongAuthTimeoutAlarmListener implements OnAlarmListener { private final int mUserId; @@ -226,6 +449,41 @@ public class LockSettingsStrongAuth { } } + /** + * Alarm of fallback timeout for non-strong biometric (i.e. weak or convenience) + */ + private class NonStrongBiometricTimeoutAlarmListener implements OnAlarmListener { + + private final int mUserId; + + NonStrongBiometricTimeoutAlarmListener(int userId) { + mUserId = userId; + } + + @Override + public void onAlarm() { + requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, mUserId); + } + } + + /** + * Alarm of idle timeout for non-strong biometric (i.e. weak or convenience biometric) + */ + private class NonStrongBiometricIdleTimeoutAlarmListener implements OnAlarmListener { + + private final int mUserId; + + NonStrongBiometricIdleTimeoutAlarmListener(int userId) { + mUserId = userId; + } + + @Override + public void onAlarm() { + // disallow unlock with non-strong biometrics + setIsNonStrongBiometricAllowed(false, mUserId); + } + } + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -248,8 +506,38 @@ public class LockSettingsStrongAuth { case MSG_NO_LONGER_REQUIRE_STRONG_AUTH: handleNoLongerRequireStrongAuth(msg.arg1, msg.arg2); break; + case MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT: + handleScheduleNonStrongBiometricTimeout(msg.arg1); + break; + case MSG_STRONG_BIOMETRIC_UNLOCK: + handleStrongBiometricUnlock(msg.arg1); + break; + case MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT: + handleScheduleNonStrongBiometricIdleTimeout(msg.arg1); + break; } } }; + public void dump(IndentingPrintWriter pw) { + pw.println("PrimaryAuthFlags state:"); + pw.increaseIndent(); + for (int i = 0; i < mStrongAuthForUser.size(); i++) { + final int key = mStrongAuthForUser.keyAt(i); + final int value = mStrongAuthForUser.valueAt(i); + pw.println("userId=" + key + ", primaryAuthFlags=" + Integer.toHexString(value)); + } + pw.println(); + pw.decreaseIndent(); + + pw.println("NonStrongBiometricAllowed state:"); + pw.increaseIndent(); + for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) { + final int key = mIsNonStrongBiometricAllowedForUser.keyAt(i); + final boolean value = mIsNonStrongBiometricAllowedForUser.valueAt(i); + pw.println("userId=" + key + ", allowed=" + value); + } + pw.println(); + pw.decreaseIndent(); + } } diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 33e01bd21105..265b9ad10561 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -213,7 +213,8 @@ class BluetoothRouteProvider { .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED) .setDescription(mContext.getResources().getText( R.string.bluetooth_a2dp_audio_route_name).toString()) - .setDeviceType(MediaRoute2Info.DEVICE_TYPE_BLUETOOTH) + //TODO: Set type correctly (BLUETOOTH_A2DP or HEARING_AID) + .setType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP) .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .build(); newBtRoute.connectedProfiles = new SparseBooleanArray(); diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 9b1824f7d7aa..f144405436f9 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -53,15 +53,15 @@ abstract class MediaRoute2Provider { public abstract void requestCreateSession(String packageName, String routeId, long requestId, @Nullable Bundle sessionHints); - public abstract void releaseSession(String sessionId); + public abstract void releaseSession(String sessionId, long requestId); public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference); - public abstract void selectRoute(String sessionId, String routeId); - public abstract void deselectRoute(String sessionId, String routeId); - public abstract void transferToRoute(String sessionId, String routeId); + public abstract void selectRoute(String sessionId, String routeId, long requestId); + public abstract void deselectRoute(String sessionId, String routeId, long requestId); + public abstract void transferToRoute(String sessionId, String routeId, long requestId); - public abstract void setRouteVolume(String routeId, int volume); - public abstract void setSessionVolume(String sessionId, int volume); + public abstract void setRouteVolume(String routeId, int volume, long requestId); + public abstract void setSessionVolume(String sessionId, int volume, long requestId); @NonNull public String getUniqueId() { @@ -116,5 +116,6 @@ abstract class MediaRoute2Provider { @NonNull RoutingSessionInfo sessionInfo); void onSessionReleased(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo); + void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, int reason); } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 60934e0cdfc6..e64776cc6e35 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -87,9 +87,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void releaseSession(String sessionId) { + public void releaseSession(String sessionId, long requestId) { if (mConnectionReady) { - mActiveConnection.releaseSession(sessionId); + mActiveConnection.releaseSession(sessionId, requestId); updateBinding(); } } @@ -103,38 +103,38 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void selectRoute(String sessionId, String routeId) { + public void selectRoute(String sessionId, String routeId, long requestId) { if (mConnectionReady) { - mActiveConnection.selectRoute(sessionId, routeId); + mActiveConnection.selectRoute(sessionId, routeId, requestId); } } @Override - public void deselectRoute(String sessionId, String routeId) { + public void deselectRoute(String sessionId, String routeId, long requestId) { if (mConnectionReady) { - mActiveConnection.deselectRoute(sessionId, routeId); + mActiveConnection.deselectRoute(sessionId, routeId, requestId); } } @Override - public void transferToRoute(String sessionId, String routeId) { + public void transferToRoute(String sessionId, String routeId, long requestId) { if (mConnectionReady) { - mActiveConnection.transferToRoute(sessionId, routeId); + mActiveConnection.transferToRoute(sessionId, routeId, requestId); } } @Override - public void setRouteVolume(String routeId, int volume) { + public void setRouteVolume(String routeId, int volume, long requestId) { if (mConnectionReady) { - mActiveConnection.setRouteVolume(routeId, volume); + mActiveConnection.setRouteVolume(routeId, volume, requestId); updateBinding(); } } @Override - public void setSessionVolume(String sessionId, int volume) { + public void setSessionVolume(String sessionId, int volume, long requestId) { if (mConnectionReady) { - mActiveConnection.setSessionVolume(sessionId, volume); + mActiveConnection.setSessionVolume(sessionId, volume, requestId); updateBinding(); } } @@ -333,8 +333,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider return; } - if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) { - Slog.w(TAG, "onSessionCreationFailed: Ignoring requestId REQUEST_ID_UNKNOWN"); + if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) { + Slog.w(TAG, "onSessionCreationFailed: Ignoring requestId REQUEST_ID_NONE"); return; } @@ -406,6 +406,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mCallback.onSessionReleased(this, sessionInfo); } + private void onRequestFailed(Connection connection, long requestId, int reason) { + if (mActiveConnection != connection) { + return; + } + + if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) { + Slog.w(TAG, "onRequestFailed: Ignoring requestId REQUEST_ID_NONE"); + return; + } + + mCallback.onRequestFailed(this, requestId, reason); + } + private void disconnect() { if (mActiveConnection != null) { mConnectionReady = false; @@ -461,9 +474,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } } - public void releaseSession(String sessionId) { + public void releaseSession(String sessionId, long requestId) { try { - mService.releaseSession(sessionId); + mService.releaseSession(sessionId, requestId); } catch (RemoteException ex) { Slog.e(TAG, "releaseSession: Failed to deliver request."); } @@ -477,41 +490,41 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } } - public void selectRoute(String sessionId, String routeId) { + public void selectRoute(String sessionId, String routeId, long requestId) { try { - mService.selectRoute(sessionId, routeId); + mService.selectRoute(sessionId, routeId, requestId); } catch (RemoteException ex) { Slog.e(TAG, "selectRoute: Failed to deliver request.", ex); } } - public void deselectRoute(String sessionId, String routeId) { + public void deselectRoute(String sessionId, String routeId, long requestId) { try { - mService.deselectRoute(sessionId, routeId); + mService.deselectRoute(sessionId, routeId, requestId); } catch (RemoteException ex) { Slog.e(TAG, "deselectRoute: Failed to deliver request.", ex); } } - public void transferToRoute(String sessionId, String routeId) { + public void transferToRoute(String sessionId, String routeId, long requestId) { try { - mService.transferToRoute(sessionId, routeId); + mService.transferToRoute(sessionId, routeId, requestId); } catch (RemoteException ex) { Slog.e(TAG, "transferToRoute: Failed to deliver request.", ex); } } - public void setRouteVolume(String routeId, int volume) { + public void setRouteVolume(String routeId, int volume, long requestId) { try { - mService.setRouteVolume(routeId, volume); + mService.setRouteVolume(routeId, volume, requestId); } catch (RemoteException ex) { Slog.e(TAG, "setRouteVolume: Failed to deliver request.", ex); } } - public void setSessionVolume(String sessionId, int volume) { + public void setSessionVolume(String sessionId, int volume, long requestId) { try { - mService.setSessionVolume(sessionId, volume); + mService.setSessionVolume(sessionId, volume, requestId); } catch (RemoteException ex) { Slog.e(TAG, "setSessionVolume: Failed to deliver request.", ex); } @@ -541,6 +554,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider void postSessionReleased(RoutingSessionInfo sessionInfo) { mHandler.post(() -> onSessionReleased(Connection.this, sessionInfo)); } + + void postSessionReleased(long requestId, int reason) { + mHandler.post(() -> onRequestFailed(Connection.this, requestId, reason)); + } } private static final class ServiceCallbackStub extends @@ -594,5 +611,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider connection.postSessionReleased(sessionInfo); } } + + @Override + public void notifyRequestFailed(long requestId, int reason) { + Connection connection = mConnectionRef.get(); + if (connection != null) { + connection.postSessionReleased(requestId, reason); + } + } } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 358891675912..e78a35cd88a4 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.media; +import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE; import static android.media.MediaRouter2Utils.getOriginalId; import static android.media.MediaRouter2Utils.getProviderId; @@ -30,7 +31,6 @@ import android.media.IMediaRouter2; import android.media.IMediaRouter2Manager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; -import android.media.MediaRoute2ProviderService; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; import android.os.Binder; @@ -70,6 +70,12 @@ class MediaRouter2ServiceImpl { private static final String TAG = "MR2ServiceImpl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + /** + * TODO: Change this with the real request ID from MediaRouter2 when + * MediaRouter2 needs to get notified for the failures. + */ + private static final long DUMMY_REQUEST_ID = -1; + private final Context mContext; private final Object mLock = new Object(); final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); @@ -365,14 +371,14 @@ class MediaRouter2ServiceImpl { } public void setRouteVolumeWithManager(IMediaRouter2Manager manager, - MediaRoute2Info route, int volume) { + MediaRoute2Info route, int volume, int requestId) { Objects.requireNonNull(manager, "manager must not be null"); Objects.requireNonNull(route, "route must not be null"); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - setRouteVolumeWithManagerLocked(manager, route, volume); + setRouteVolumeWithManagerLocked(manager, route, volume, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -397,7 +403,7 @@ class MediaRouter2ServiceImpl { } public void selectRouteWithManager(IMediaRouter2Manager manager, String uniqueSessionId, - MediaRoute2Info route) { + MediaRoute2Info route, int requestId) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -407,7 +413,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - selectRouteWithManagerLocked(manager, uniqueSessionId, route); + selectRouteWithManagerLocked(manager, uniqueSessionId, route, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -415,7 +421,7 @@ class MediaRouter2ServiceImpl { } public void deselectRouteWithManager(IMediaRouter2Manager manager, String uniqueSessionId, - MediaRoute2Info route) { + MediaRoute2Info route, int requestId) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -425,7 +431,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - deselectRouteWithManagerLocked(manager, uniqueSessionId, route); + deselectRouteWithManagerLocked(manager, uniqueSessionId, route, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -433,7 +439,7 @@ class MediaRouter2ServiceImpl { } public void transferToRouteWithManager(IMediaRouter2Manager manager, String uniqueSessionId, - MediaRoute2Info route) { + MediaRoute2Info route, int requestId) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -443,7 +449,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - transferToRouteWithManagerLocked(manager, uniqueSessionId, route); + transferToRouteWithManagerLocked(manager, uniqueSessionId, route, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -451,7 +457,7 @@ class MediaRouter2ServiceImpl { } public void setSessionVolumeWithManager(IMediaRouter2Manager manager, - String uniqueSessionId, int volume) { + String uniqueSessionId, int volume, int requestId) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -460,14 +466,15 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - setSessionVolumeWithManagerLocked(manager, uniqueSessionId, volume); + setSessionVolumeWithManagerLocked(manager, uniqueSessionId, volume, requestId); } } finally { Binder.restoreCallingIdentity(token); } } - public void releaseSessionWithManager(IMediaRouter2Manager manager, String uniqueSessionId) { + public void releaseSessionWithManager(IMediaRouter2Manager manager, String uniqueSessionId, + int requestId) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); @@ -476,7 +483,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - releaseSessionWithManagerLocked(manager, uniqueSessionId); + releaseSessionWithManagerLocked(manager, uniqueSessionId, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -572,7 +579,7 @@ class MediaRouter2ServiceImpl { obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers, routerRecord.mUserRecord.mHandler, routerRecord)); routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::updateDiscoveryPreference, + obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, routerRecord.mUserRecord.mHandler)); } @@ -584,7 +591,7 @@ class MediaRouter2ServiceImpl { if (routerRecord != null) { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setRouteVolumeOnHandler, - routerRecord.mUserRecord.mHandler, route, volume)); + routerRecord.mUserRecord.mHandler, route, volume, DUMMY_REQUEST_ID)); } } @@ -615,7 +622,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::selectRouteOnHandler, - routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route)); + routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route, + DUMMY_REQUEST_ID)); } private void deselectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, @@ -629,7 +637,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::deselectRouteOnHandler, - routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route)); + routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route, + DUMMY_REQUEST_ID)); } private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router, @@ -643,7 +652,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::transferToRouteOnHandler, - routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route)); + routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route, + DUMMY_REQUEST_ID)); } private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, @@ -657,7 +667,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setSessionVolumeOnHandler, - routerRecord.mUserRecord.mHandler, uniqueSessionId, volume)); + routerRecord.mUserRecord.mHandler, uniqueSessionId, volume, + DUMMY_REQUEST_ID)); } private void releaseSessionWithRouter2Locked(@NonNull IMediaRouter2 router, @@ -671,7 +682,8 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::releaseSessionOnHandler, - routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId)); + routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, + DUMMY_REQUEST_ID)); } //////////////////////////////////////////////////////////// @@ -744,16 +756,18 @@ class MediaRouter2ServiceImpl { } private void setRouteVolumeWithManagerLocked(@NonNull IMediaRouter2Manager manager, - @NonNull MediaRoute2Info route, int volume) { + @NonNull MediaRoute2Info route, int volume, int requestId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); if (managerRecord == null) { return; } + + long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setRouteVolumeOnHandler, - managerRecord.mUserRecord.mHandler, route, volume)); + managerRecord.mUserRecord.mHandler, route, volume, uniqueRequestId)); } private void requestCreateSessionWithManagerLocked(@NonNull IMediaRouter2Manager manager, @@ -778,7 +792,7 @@ class MediaRouter2ServiceImpl { } private void selectRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, int requestId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -790,13 +804,15 @@ class MediaRouter2ServiceImpl { RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterforSessionLocked(uniqueSessionId); + long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::selectRouteOnHandler, - managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route)); + managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route, + uniqueRequestId)); } private void deselectRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, int requestId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -808,13 +824,15 @@ class MediaRouter2ServiceImpl { RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterforSessionLocked(uniqueSessionId); + long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::deselectRouteOnHandler, - managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route)); + managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route, + uniqueRequestId)); } private void transferToRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, int requestId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -826,13 +844,15 @@ class MediaRouter2ServiceImpl { RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterforSessionLocked(uniqueSessionId); + long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::transferToRouteOnHandler, - managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route)); + managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route, + uniqueRequestId)); } private void setSessionVolumeWithManagerLocked(@NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId, int volume) { + @NonNull String uniqueSessionId, int volume, int requestId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -840,13 +860,15 @@ class MediaRouter2ServiceImpl { return; } + long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setSessionVolumeOnHandler, - managerRecord.mUserRecord.mHandler, uniqueSessionId, volume)); + managerRecord.mUserRecord.mHandler, uniqueSessionId, volume, + uniqueRequestId)); } private void releaseSessionWithManagerLocked(@NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId) { + @NonNull String uniqueSessionId, int requestId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -856,10 +878,15 @@ class MediaRouter2ServiceImpl { RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterforSessionLocked(uniqueSessionId); + if (routerRecord == null) { + return; + } + long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::releaseSessionOnHandler, - managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId)); + managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, + uniqueRequestId)); } //////////////////////////////////////////////////////////// @@ -1100,6 +1127,13 @@ class MediaRouter2ServiceImpl { this, provider, sessionInfo)); } + @Override + public void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, + int reason) { + sendMessage(PooledLambda.obtainMessage(UserHandler::onRequestFailedOnHandler, + this, provider, requestId, reason)); + } + @Nullable public RouterRecord findRouterforSessionLocked(@NonNull String uniqueSessionId) { return mSessionToRouterMap.get(uniqueSessionId); @@ -1195,7 +1229,7 @@ class MediaRouter2ServiceImpl { if (provider == null) { Slog.w(TAG, "Ignoring session creation request since no provider found for" + " given route=" + route); - notifySessionCreationFailed(routerRecord, toOriginalRequestId(requestId)); + notifySessionCreationFailedToRouter(routerRecord, toOriginalRequestId(requestId)); return; } @@ -1210,7 +1244,7 @@ class MediaRouter2ServiceImpl { // routerRecord can be null if the session is system's. private void selectRouteOnHandler(@Nullable RouterRecord routerRecord, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, long requestId) { if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, "selecting")) { return; @@ -1222,12 +1256,12 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); + provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId(), requestId); } // routerRecord can be null if the session is system's. private void deselectRouteOnHandler(@Nullable RouterRecord routerRecord, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, long requestId) { if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, "deselecting")) { return; @@ -1239,12 +1273,13 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); + provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId(), + requestId); } // routerRecord can be null if the session is system's. private void transferToRouteOnHandler(@Nullable RouterRecord routerRecord, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, long requestId) { if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, "transferring to")) { return; @@ -1256,7 +1291,8 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.transferToRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); + provider.transferToRoute(getOriginalId(uniqueSessionId), route.getOriginalId(), + requestId); } private boolean checkArgumentsForSessionControl(@Nullable RouterRecord routerRecord, @@ -1299,7 +1335,7 @@ class MediaRouter2ServiceImpl { } private void releaseSessionOnHandler(@NonNull RouterRecord routerRecord, - @NonNull String uniqueSessionId) { + @NonNull String uniqueSessionId, long uniqueRequestId) { final RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId); if (matchingRecord != routerRecord) { Slog.w(TAG, "Ignoring releasing session from non-matching router." @@ -1329,14 +1365,14 @@ class MediaRouter2ServiceImpl { return; } - provider.releaseSession(sessionId); + provider.releaseSession(sessionId, uniqueRequestId); } private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo, long requestId) { notifySessionCreatedToManagers(getManagers(), sessionInfo); - if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) { + if (requestId == REQUEST_ID_NONE) { // The session is created without any matching request. return; } @@ -1362,7 +1398,7 @@ class MediaRouter2ServiceImpl { if (sessionInfo == null) { // Failed - notifySessionCreationFailed(matchingRequest.mRouterRecord, + notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord, toOriginalRequestId(requestId)); return; } @@ -1374,13 +1410,13 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "Created session doesn't match the original request." + " originalRouteId=" + originalRouteId + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo); - notifySessionCreationFailed(matchingRequest.mRouterRecord, + notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord, toOriginalRequestId(requestId)); return; } // Succeeded - notifySessionCreated(matchingRequest.mRouterRecord, + notifySessionCreatedToRouter(matchingRequest.mRouterRecord, sessionInfo, toOriginalRequestId(requestId)); mSessionToRouterMap.put(sessionInfo.getId(), routerRecord); } @@ -1405,7 +1441,7 @@ class MediaRouter2ServiceImpl { } mSessionCreationRequests.remove(matchingRequest); - notifySessionCreationFailed(matchingRequest.mRouterRecord, + notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord, toOriginalRequestId(requestId)); } @@ -1429,7 +1465,7 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "No matching router found for session=" + sessionInfo); return; } - notifySessionInfoChanged(routerRecord, sessionInfo); + notifySessionInfoChangedToRouter(routerRecord, sessionInfo); } private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, @@ -1442,10 +1478,38 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "No matching router found for session=" + sessionInfo); return; } - notifySessionReleased(routerRecord, sessionInfo); + notifySessionReleasedToRouter(routerRecord, sessionInfo); } - private void notifySessionCreated(@NonNull RouterRecord routerRecord, + private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider, + long requestId, int reason) { + final int managerId = toRouterOrManagerId(requestId); + + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return; + } + + ManagerRecord managerToNotifyFailure = null; + synchronized (service.mLock) { + for (ManagerRecord manager : mUserRecord.mManagerRecords) { + if (manager.mManagerId == managerId) { + managerToNotifyFailure = manager; + break; + } + } + } + + if (managerToNotifyFailure == null) { + Slog.w(TAG, "No matching managerRecord found for managerId=" + managerId); + return; + } + + notifyRequestFailedToManager( + managerToNotifyFailure.mManager, toOriginalRequestId(requestId), reason); + } + + private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord, @NonNull RoutingSessionInfo sessionInfo, int requestId) { try { routerRecord.mRouter.notifySessionCreated(sessionInfo, requestId); @@ -1455,7 +1519,7 @@ class MediaRouter2ServiceImpl { } } - private void notifySessionCreationFailed(@NonNull RouterRecord routerRecord, + private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord, int requestId) { try { routerRecord.mRouter.notifySessionCreated(/* sessionInfo= */ null, requestId); @@ -1465,7 +1529,7 @@ class MediaRouter2ServiceImpl { } } - private void notifySessionInfoChanged(@NonNull RouterRecord routerRecord, + private void notifySessionInfoChangedToRouter(@NonNull RouterRecord routerRecord, @NonNull RoutingSessionInfo sessionInfo) { try { routerRecord.mRouter.notifySessionInfoChanged(sessionInfo); @@ -1475,7 +1539,7 @@ class MediaRouter2ServiceImpl { } } - private void notifySessionReleased(@NonNull RouterRecord routerRecord, + private void notifySessionReleasedToRouter(@NonNull RouterRecord routerRecord, @NonNull RoutingSessionInfo sessionInfo) { try { routerRecord.mRouter.notifySessionReleased(sessionInfo); @@ -1485,21 +1549,23 @@ class MediaRouter2ServiceImpl { } } - private void setRouteVolumeOnHandler(@NonNull MediaRoute2Info route, int volume) { + private void setRouteVolumeOnHandler(@NonNull MediaRoute2Info route, int volume, + long requestId) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider != null) { - provider.setRouteVolume(route.getOriginalId(), volume); + provider.setRouteVolume(route.getOriginalId(), volume, requestId); } } - private void setSessionVolumeOnHandler(@NonNull String uniqueSessionId, int volume) { + private void setSessionVolumeOnHandler(@NonNull String uniqueSessionId, int volume, + long requestId) { final MediaRoute2Provider provider = findProvider(getProviderId(uniqueSessionId)); if (provider == null) { Slog.w(TAG, "setSessionVolume: couldn't find provider for session " + "id=" + uniqueSessionId); return; } - provider.setSessionVolume(getOriginalId(uniqueSessionId), volume); + provider.setSessionVolume(getOriginalId(uniqueSessionId), volume, requestId); } private List<IMediaRouter2> getRouters() { @@ -1683,7 +1749,17 @@ class MediaRouter2ServiceImpl { } } - private void updateDiscoveryPreference() { + private void notifyRequestFailedToManager(@NonNull IMediaRouter2Manager manager, + int requestId, int reason) { + try { + manager.notifyRequestFailed(requestId, reason); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify manager of the request failure." + + " Manager probably died.", ex); + } + } + + private void updateDiscoveryPreferenceOnHandler() { MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return; diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 580fc5250e35..a13ee1058a26 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -543,8 +543,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override public void setRouteVolumeWithManager(IMediaRouter2Manager manager, - MediaRoute2Info route, int volume) { - mService2.setRouteVolumeWithManager(manager, route, volume); + MediaRoute2Info route, int volume, int requestId) { + mService2.setRouteVolumeWithManager(manager, route, volume, requestId); } // Binder call @@ -557,35 +557,36 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override public void selectRouteWithManager(IMediaRouter2Manager manager, String sessionId, - MediaRoute2Info route) { - mService2.selectRouteWithManager(manager, sessionId, route); + MediaRoute2Info route, int requestId) { + mService2.selectRouteWithManager(manager, sessionId, route, requestId); } // Binder call @Override public void deselectRouteWithManager(IMediaRouter2Manager manager, String sessionId, - MediaRoute2Info route) { - mService2.deselectRouteWithManager(manager, sessionId, route); + MediaRoute2Info route, int requestId) { + mService2.deselectRouteWithManager(manager, sessionId, route, requestId); } // Binder call @Override public void transferToRouteWithManager(IMediaRouter2Manager manager, String sessionId, - MediaRoute2Info route) { - mService2.transferToRouteWithManager(manager, sessionId, route); + MediaRoute2Info route, int requestId) { + mService2.transferToRouteWithManager(manager, sessionId, route, requestId); } // Binder call @Override public void setSessionVolumeWithManager(IMediaRouter2Manager manager, - String sessionId, int volume) { - mService2.setSessionVolumeWithManager(manager, sessionId, volume); + String sessionId, int volume, int requestId) { + mService2.setSessionVolumeWithManager(manager, sessionId, volume, requestId); } // Binder call @Override - public void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId) { - mService2.releaseSessionWithManager(manager, sessionId); + public void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId, + int requestId) { + mService2.releaseSessionWithManager(manager, sessionId, requestId); } void restoreBluetoothA2dp() { diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index f7e1398a2bd3..da9c27e5bf0a 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -18,6 +18,7 @@ package com.android.server.media; import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; +import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -130,26 +131,27 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void releaseSession(String sessionId) { + public void releaseSession(String sessionId, long requestId) { // Do nothing } + @Override public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { // Do nothing } @Override - public void selectRoute(String sessionId, String routeId) { + public void selectRoute(String sessionId, String routeId, long requestId) { // Do nothing since we don't support multiple BT yet. } @Override - public void deselectRoute(String sessionId, String routeId) { + public void deselectRoute(String sessionId, String routeId, long requestId) { // Do nothing since we don't support multiple BT yet. } @Override - public void transferToRoute(String sessionId, String routeId) { + public void transferToRoute(String sessionId, String routeId, long requestId) { if (TextUtils.equals(routeId, mDefaultRoute.getId())) { mBtRouteProvider.transferTo(null); } else { @@ -158,7 +160,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void setRouteVolume(String routeId, int volume) { + public void setRouteVolume(String routeId, int volume, long requestId) { if (!TextUtils.equals(routeId, mSelectedRouteId)) { return; } @@ -166,7 +168,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void setSessionVolume(String sessionId, int volume) { + public void setSessionVolume(String sessionId, int volume, long requestId) { // Do nothing since we don't support grouping volume yet. } @@ -192,6 +194,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)) + //TODO: Guess the exact type using AudioDevice + .setType(TYPE_BUILTIN_SPEAKER) .addFeature(FEATURE_LIVE_AUDIO) .addFeature(FEATURE_LIVE_VIDEO) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index fce10e62bc5d..c301cd2339ec 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -27,4 +27,6 @@ public interface NotificationManagerInternal { String tag, int id, int userId); void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId); + + void onConversationRemoved(String pkg, int uid, String conversationId); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0d402e57d335..475f229562dd 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -772,7 +772,7 @@ public class NotificationManagerService extends SystemService { parser, mAllowedManagedServicePackages, forRestore, userId); migratedManagedServices = true; } else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) { - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, System.currentTimeMillis()); } if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) { if (forRestore && userId != UserHandle.USER_SYSTEM) { @@ -2322,6 +2322,8 @@ public class NotificationManagerService extends SystemService { mConditionProviders.onBootPhaseAppsCanStart(); mHistoryManager.onBootPhaseAppsCanStart(); registerDeviceConfigChange(); + } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { + mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis()); } } @@ -5474,6 +5476,11 @@ public class NotificationManagerService extends SystemService { }); } + @Override + public void onConversationRemoved(String pkg, int uid, String conversationId) { + onConversationRemovedInternal(pkg, uid, conversationId); + } + @GuardedBy("mNotificationLock") private void removeForegroundServiceFlagLocked(NotificationRecord r) { if (r == null) { @@ -5676,7 +5683,7 @@ public class NotificationManagerService extends SystemService { mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground)); } - public void onConversationRemoved(String pkg, int uid, String conversationId) { + private void onConversationRemovedInternal(String pkg, int uid, String conversationId) { checkCallerIsSystem(); Preconditions.checkStringNotEmpty(pkg); Preconditions.checkStringNotEmpty(conversationId); diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index bae1dd3c6cb6..d60c29119c18 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -36,6 +36,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -43,9 +44,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; -import java.sql.Array; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -172,7 +171,7 @@ public class SnoozeHelper { for (int i = 0; i < allRecords.size(); i++) { NotificationRecord r = allRecords.valueAt(i); String currentGroupKey = r.getSbn().getGroup(); - if (currentGroupKey.equals(groupKey)) { + if (Objects.equals(currentGroupKey, groupKey)) { records.add(r); } } @@ -217,7 +216,7 @@ public class SnoozeHelper { snooze(record); scheduleRepost(pkg, key, userId, duration); - Long activateAt = System.currentTimeMillis() + duration; + Long activateAt = SystemClock.elapsedRealtime() + duration; synchronized (mPersistedSnoozedNotifications) { storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); } @@ -244,8 +243,6 @@ public class SnoozeHelper { } storeRecord(record.getSbn().getPackageName(), record.getKey(), userId, mSnoozedNotifications, record); - mPackages.put(record.getKey(), record.getSbn().getPackageName()); - mUsers.put(record.getKey(), userId); } private <T> void storeRecord(String pkg, String key, Integer userId, @@ -258,6 +255,8 @@ public class SnoozeHelper { keyToValue.put(key, object); targets.put(getPkgKey(userId, pkg), keyToValue); + mPackages.put(key, pkg); + mUsers.put(key, userId); } private <T> T removeRecord(String pkg, String key, Integer userId, @@ -425,12 +424,34 @@ public class SnoozeHelper { PendingIntent.FLAG_UPDATE_CURRENT); } + public void scheduleRepostsForPersistedNotifications(long currentTime) { + for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) { + for (int i = 0; i < snoozed.size(); i++) { + String key = snoozed.keyAt(i); + Long time = snoozed.valueAt(i); + String pkg = mPackages.get(key); + Integer userId = mUsers.get(key); + if (time == null || pkg == null || userId == null) { + Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId); + continue; + } + if (time != null && time > currentTime) { + scheduleRepostAtTime(pkg, key, userId, time); + } + } + + } + } + private void scheduleRepost(String pkg, String key, int userId, long duration) { + scheduleRepostAtTime(pkg, key, userId, SystemClock.elapsedRealtime() + duration); + } + + private void scheduleRepostAtTime(String pkg, String key, int userId, long time) { long identity = Binder.clearCallingIdentity(); try { final PendingIntent pi = createPendingIntent(pkg, key, userId); mAm.cancel(pi); - long time = SystemClock.elapsedRealtime() + duration; if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time)); mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi); } finally { @@ -496,6 +517,7 @@ public class SnoozeHelper { private interface Inserter<T> { void insert(T t) throws IOException; } + private <T> void writeXml(XmlSerializer out, ArrayMap<String, ArrayMap<String, T>> targets, String tag, Inserter<T> attributeInserter) @@ -503,12 +525,13 @@ public class SnoozeHelper { synchronized (targets) { final int M = targets.size(); for (int i = 0; i < M; i++) { - String userIdPkgKey = targets.keyAt(i); // T is a String (snoozed until context) or Long (snoozed until time) ArrayMap<String, T> keyToValue = targets.valueAt(i); for (int j = 0; j < keyToValue.size(); j++) { String key = keyToValue.keyAt(j); T value = keyToValue.valueAt(j); + String pkg = mPackages.get(key); + Integer userId = mUsers.get(key); out.startTag(null, tag); @@ -518,8 +541,7 @@ public class SnoozeHelper { XML_SNOOZED_NOTIFICATION_VERSION); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - String pkg = mPackages.get(key); - int userId = mUsers.get(key); + out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, String.valueOf(userId)); @@ -530,7 +552,7 @@ public class SnoozeHelper { } } - protected void readXml(XmlPullParser parser) + protected void readXml(XmlPullParser parser, long currentTime) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -547,16 +569,15 @@ public class SnoozeHelper { try { final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY); final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG); - final int userId = Integer.parseInt( - parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID)); + final int userId = XmlUtils.readIntAttribute( + parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); if (tag.equals(XML_SNOOZED_NOTIFICATION)) { - final Long time = Long.parseLong( - parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME)); - if (time > System.currentTimeMillis()) { //only read new stuff + final Long time = XmlUtils.readLongAttribute( + parser, XML_SNOOZED_NOTIFICATION_TIME, 0); + if (time > currentTime) { //only read new stuff synchronized (mPersistedSnoozedNotifications) { storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time); } - scheduleRepost(pkg, key, userId, time - System.currentTimeMillis()); } } if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) { diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 8ad3e9df8bdf..f37af3aef657 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -234,11 +234,11 @@ public class Installer extends SystemService { } public void moveCompleteApp(String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seInfo, int targetSdkVersion, + int appId, String seInfo, int targetSdkVersion, String fromCodePath) throws InstallerException { if (!checkBeforeRemote()) return; try { - mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo, + mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, appId, seInfo, targetSdkVersion, fromCodePath); } catch (Exception e) { throw InstallerException.from(e); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 261caba98e4f..8031eaa62084 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -72,6 +72,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -134,6 +135,8 @@ public class LauncherAppsService extends SystemService { private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); + private final ShortcutChangeHandler mShortcutChangeHandler; + private final Handler mCallbackHandler; private PackageInstallerService mPackageInstallerService; @@ -153,6 +156,8 @@ public class LauncherAppsService extends SystemService { mShortcutServiceInternal = Objects.requireNonNull( LocalServices.getService(ShortcutServiceInternal.class)); mShortcutServiceInternal.addListener(mPackageMonitor); + mShortcutChangeHandler = new ShortcutChangeHandler(mUserManagerInternal); + mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler); mCallbackHandler = BackgroundThread.getHandler(); mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); } @@ -720,12 +725,37 @@ public class LauncherAppsService extends SystemService { @Override public void registerShortcutChangeCallback(String callingPackage, long changedSince, String packageName, List shortcutIds, List<LocusId> locusIds, - ComponentName componentName, int flags, IShortcutChangeCallback callback, - int callbackId) { + ComponentName componentName, int flags, IShortcutChangeCallback callback) { + ensureShortcutPermission(callingPackage); + + if (shortcutIds != null && packageName == null) { + throw new IllegalArgumentException( + "To query by shortcut ID, package name must also be set"); + } + if (locusIds != null && packageName == null) { + throw new IllegalArgumentException( + "To query by locus ID, package name must also be set"); + } + + UserHandle user = UserHandle.of(injectCallingUserId()); + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_GRANTED) { + user = null; + } + + // TODO: When ShortcutQueryWrapper (ag/10323729) is available, pass that directly. + ShortcutChangeHandler.QueryInfo query = new ShortcutChangeHandler.QueryInfo( + changedSince, packageName, shortcutIds, locusIds, componentName, flags, user); + mShortcutChangeHandler.addShortcutChangeCallback(callback, query); } @Override - public void unregisterShortcutChangeCallback(String callingPackage, int callbackId) { + public void unregisterShortcutChangeCallback(String callingPackage, + IShortcutChangeCallback callback) { + ensureShortcutPermission(callingPackage); + + mShortcutChangeHandler.removeShortcutChangeCallback(callback); } @Override @@ -1005,6 +1035,153 @@ public class LauncherAppsService extends SystemService { mCallbackHandler.post(r); } + public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback { + + static class QueryInfo { + final long mChangedSince; + final String mPackage; + final List<String> mShortcutIds; + final List<LocusId> mLocusIds; + final ComponentName mActivity; + final int mQueryFlags; + final UserHandle mCallbackUser; + + QueryInfo(long changedSince, String packageName, List<String> shortcutIds, + List<LocusId> locusIds, ComponentName activity, int flags, + UserHandle callbackUser) { + mChangedSince = changedSince; + mPackage = packageName; + mShortcutIds = shortcutIds; + mLocusIds = locusIds; + mActivity = activity; + mQueryFlags = flags; + mCallbackUser = callbackUser; + } + } + + private final UserManagerInternal mUserManagerInternal; + + ShortcutChangeHandler(UserManagerInternal userManager) { + mUserManagerInternal = userManager; + } + + private final RemoteCallbackList<IShortcutChangeCallback> mCallbacks = + new RemoteCallbackList<>(); + + public synchronized void addShortcutChangeCallback(IShortcutChangeCallback callback, + QueryInfo query) { + mCallbacks.unregister(callback); + mCallbacks.register(callback, query); + } + + public synchronized void removeShortcutChangeCallback( + IShortcutChangeCallback callback) { + mCallbacks.unregister(callback); + } + + @Override + public void onShortcutsAddedOrUpdated(String packageName, List<ShortcutInfo> shortcuts, + UserHandle user) { + onShortcutEvent(packageName, shortcuts, user, false); + } + + @Override + public void onShortcutsRemoved(String packageName, List<ShortcutInfo> shortcuts, + UserHandle user) { + onShortcutEvent(packageName, shortcuts, user, true); + } + + private void onShortcutEvent(String packageName, + List<ShortcutInfo> shortcuts, UserHandle user, boolean shortcutsRemoved) { + int count = mCallbacks.beginBroadcast(); + + for (int i = 0; i < count; i++) { + final IShortcutChangeCallback callback = mCallbacks.getBroadcastItem(i); + final QueryInfo query = (QueryInfo) mCallbacks.getBroadcastCookie(i); + + if (query.mCallbackUser != null && !hasUserAccess(query.mCallbackUser, user)) { + // Callback owner does not have access to the shortcuts' user. + continue; + } + + // Filter the list by query, if any matches exists, send via callback. + List<ShortcutInfo> matchedList = + filterShortcutsByQuery(packageName, shortcuts, query); + if (!CollectionUtils.isEmpty(matchedList)) { + try { + if (shortcutsRemoved) { + callback.onShortcutsRemoved(packageName, matchedList, user); + } else { + callback.onShortcutsAddedOrUpdated(packageName, matchedList, user); + } + } catch (RemoteException e) { + // The RemoteCallbackList will take care of removing the dead object. + } + } + } + + mCallbacks.finishBroadcast(); + } + + public static List<ShortcutInfo> filterShortcutsByQuery(String packageName, + List<ShortcutInfo> shortcuts, QueryInfo query) { + if (query.mPackage != null && query.mPackage != packageName) { + return null; + } + + List<ShortcutInfo> matches = new ArrayList<>(); + + final boolean matchDynamic = + (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0; + final boolean matchPinned = + (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0; + final boolean matchManifest = + (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0; + final boolean matchCached = + (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0; + final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0) + | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0) + | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0) + | (matchCached ? ShortcutInfo.FLAG_CACHED : 0); + + for (int i = 0; i < shortcuts.size(); i++) { + final ShortcutInfo si = shortcuts.get(i); + + if (query.mActivity != null && !query.mActivity.equals(si.getActivity())) { + continue; + } + + if (query.mChangedSince != 0 + && query.mChangedSince > si.getLastChangedTimestamp()) { + continue; + } + + if (query.mShortcutIds != null && !query.mShortcutIds.contains(si.getId())) { + continue; + } + + if (query.mLocusIds != null && !query.mLocusIds.contains(si.getLocusId())) { + continue; + } + + if ((shortcutFlags & si.getFlags()) != 0) { + matches.add(si); + } + } + + return matches; + } + + private boolean hasUserAccess(UserHandle callbackUser, UserHandle shortcutUser) { + final int callbackUserId = callbackUser.getIdentifier(); + final int shortcutUserId = shortcutUser.getIdentifier(); + + if (shortcutUser == callbackUser) return true; + return mUserManagerInternal.isProfileAccessible(callbackUserId, shortcutUserId, + null, false); + } + } + private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener { // TODO Simplify with lambdas. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 92507e5a0e25..52052f8dd871 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12760,7 +12760,11 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Calling uid " + callingUid + " cannot call for user " + userId); } - Preconditions.checkNotNull(packageNames, "packageNames cannot be null"); + Objects.requireNonNull(packageNames, "packageNames cannot be null"); + if (restrictionFlags != 0 && !isSuspendAllowedForUser(userId)) { + Slog.w(TAG, "Cannot restrict packages due to restrictions on user " + userId); + return packageNames; + } final List<String> changedPackagesList = new ArrayList<>(packageNames.length); final IntArray changedUids = new IntArray(packageNames.length); @@ -12847,6 +12851,10 @@ public class PackageManagerService extends IPackageManager.Stub if (ArrayUtils.isEmpty(packageNames)) { return packageNames; } + if (suspended && !isSuspendAllowedForUser(userId)) { + Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); + return packageNames; + } final List<String> changedPackagesList = new ArrayList<>(packageNames.length); final IntArray changedUids = new IntArray(packageNames.length); @@ -12985,30 +12993,41 @@ public class PackageManagerService extends IPackageManager.Stub } } + void unsuspendForSuspendingPackage(String suspendingPackage, int userId) { + final String[] allPackages; + synchronized (mLock) { + allPackages = mPackages.keySet().toArray(new String[mPackages.size()]); + } + removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId); + } + /** - * Immediately unsuspends any packages suspended by the given package. To be called - * when such a package's data is cleared or it is removed from the device. + * Removes any suspensions on given packages that were added by packages that pass the given + * predicate. * - * <p><b>Should not be used on a frequent code path</b> as it flushes state to disk - * synchronously + * <p> Caller must flush package restrictions if it cares about immediate data consistency. * - * @param suspendingPackage The suspending package + * @param packagesToChange The packages on which the suspension are to be removed. + * @param suspendingPackagePredicate A predicate identifying the suspending packages whose + * suspensions will be removed. * @param userId The user for which the changes are taking place. */ - private void unsuspendForSuspendingPackage(String suspendingPackage, int userId) { + void removeSuspensionsBySuspendingPackage(String[] packagesToChange, + Predicate<String> suspendingPackagePredicate, int userId) { final List<String> unsuspendedPackages = new ArrayList<>(); final IntArray unsuspendedUids = new IntArray(); synchronized (mLock) { - for (PackageSetting ps : mSettings.mPackages.values()) { - final PackageUserState pus = ps.readUserState(userId); - if (pus.suspended) { - ps.removeSuspension(suspendingPackage, userId); + for (String packageName : packagesToChange) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps.getSuspended(userId)) { + ps.removeSuspension(suspendingPackagePredicate, userId); if (!ps.getSuspended(userId)) { unsuspendedPackages.add(ps.name); unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId())); } } } + scheduleWritePackageRestrictionsLocked(userId); } if (!unsuspendedPackages.isEmpty()) { final String[] packageArray = unsuspendedPackages.toArray( @@ -13016,13 +13035,67 @@ public class PackageManagerService extends IPackageManager.Stub sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId); sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false); } - // Write package restrictions immediately to avoid an inconsistent state. - mSettings.writePackageRestrictionsLPr(userId); + } + + void removeAllDistractingPackageRestrictions(int userId) { + final String[] allPackages; + synchronized (mLock) { + allPackages = mPackages.keySet().toArray(new String[mPackages.size()]); + } + PackageManagerService.this.removeDistractingPackageRestrictions(allPackages, userId); + } + + /** + * Removes any {@link android.content.pm.PackageManager.DistractionRestriction restrictions} + * set on given packages. + * + * <p> Caller must flush package restrictions if it cares about immediate data consistency. + * + * @param packagesToChange The packages on which restrictions are to be removed. + * @param userId the user for which changes are taking place. + */ + void removeDistractingPackageRestrictions(String[] packagesToChange, int userId) { + final List<String> changedPackages = new ArrayList<>(); + final IntArray changedUids = new IntArray(); + synchronized (mLock) { + for (String packageName : packagesToChange) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps.getDistractionFlags(userId) != 0) { + ps.setDistractionFlags(0, userId); + changedPackages.add(ps.name); + changedUids.add(UserHandle.getUid(userId, ps.getAppId())); + } + } + if (!changedPackages.isEmpty()) { + final String[] packageArray = changedPackages.toArray( + new String[changedPackages.size()]); + sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId, 0); + scheduleWritePackageRestrictionsLocked(userId); + } + } + } + + private boolean isCallerDeviceOrProfileOwner(int userId) { + final int callingUid = Binder.getCallingUid(); + if (callingUid == Process.SYSTEM_UID) { + return true; + } + final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId); + if (ownerPackage != null) { + return callingUid == getPackageUidInternal(ownerPackage, 0, userId, callingUid); + } + return false; + } + + private boolean isSuspendAllowedForUser(int userId) { + return isCallerDeviceOrProfileOwner(userId) + || (!mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId) + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId)); } @Override public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) { - Preconditions.checkNotNull(packageNames, "packageNames cannot be null"); + Objects.requireNonNull(packageNames, "packageNames cannot be null"); mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, "getUnsuspendablePackagesForUser"); final int callingUid = Binder.getCallingUid(); @@ -13030,11 +13103,23 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Calling uid " + callingUid + " cannot query getUnsuspendablePackagesForUser for user " + userId); } + if (!isSuspendAllowedForUser(userId)) { + Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); + return packageNames; + } final ArraySet<String> unactionablePackages = new ArraySet<>(); final boolean[] canSuspend = canSuspendPackageForUserInternal(packageNames, userId); for (int i = 0; i < packageNames.length; i++) { if (!canSuspend[i]) { unactionablePackages.add(packageNames[i]); + continue; + } + synchronized (mLock) { + final PackageSetting ps = mSettings.mPackages.get(packageNames[i]); + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { + Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]); + unactionablePackages.add(packageNames[i]); + } } } return unactionablePackages.toArray(new String[unactionablePackages.size()]); @@ -13051,6 +13136,7 @@ public class PackageManagerService extends IPackageManager.Stub @NonNull private boolean[] canSuspendPackageForUserInternal(@NonNull String[] packageNames, int userId) { final boolean[] canSuspend = new boolean[packageNames.length]; + final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId); final long callingId = Binder.clearCallingIdentity(); try { final String activeLauncherPackageName = getActiveLauncherPackageName(userId); @@ -13100,6 +13186,11 @@ public class PackageManagerService extends IPackageManager.Stub + "\": protected package"); continue; } + if (!isCallerOwner && mSettings.getBlockUninstallLPr(userId, packageName)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": blocked by admin"); + continue; + } // Cannot suspend static shared libs as they are considered // a part of the using app (emulating static linking). Also @@ -13991,20 +14082,18 @@ public class PackageManagerService extends IPackageManager.Stub final String fromUuid; final String toUuid; final String packageName; - final String dataAppName; final int appId; final String seinfo; final int targetSdkVersion; final String fromCodePath; public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seinfo, int targetSdkVersion, + int appId, String seinfo, int targetSdkVersion, String fromCodePath) { this.moveId = moveId; this.fromUuid = fromUuid; this.toUuid = toUuid; this.packageName = packageName; - this.dataAppName = dataAppName; this.appId = appId; this.seinfo = seinfo; this.targetSdkVersion = targetSdkVersion; @@ -15120,7 +15209,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstaller) { try { mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName, - move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion, + move.appId, move.seinfo, move.targetSdkVersion, move.fromCodePath); } catch (InstallerException e) { Slog.w(TAG, "Failed to move app", e); @@ -15128,7 +15217,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - codeFile = new File(Environment.getDataAppDirectory(move.toUuid), move.dataAppName); + final String toPathName = new File(move.fromCodePath).getName(); + codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName); resourceFile = codeFile; if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile); @@ -15172,8 +15262,9 @@ public class PackageManagerService extends IPackageManager.Stub } private boolean cleanUp(String volumeUuid) { + final String toPathName = new File(move.fromCodePath).getName(); final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), - move.dataAppName); + toPathName); Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid); final int[] userIds = mUserManager.getUserIds(); synchronized (mInstallLock) { @@ -18449,6 +18540,8 @@ public class PackageManagerService extends IPackageManager.Stub if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) == PERMISSION_GRANTED) { unsuspendForSuspendingPackage(packageName, userId); + removeAllDistractingPackageRestrictions(userId); + flushPackageRestrictionsAsUserInternalLocked(userId); } } } else { @@ -19972,6 +20065,16 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { pkgSetting.setEnabled(newState, userId, callingPackage); + if (newState == COMPONENT_ENABLED_STATE_DISABLED_USER + || newState == COMPONENT_ENABLED_STATE_DISABLED + && pkgSetting.getPermissionsState().hasPermission( + Manifest.permission.SUSPEND_APPS, userId)) { + // This app should not generally be allowed to get disabled by the UI, but if it + // ever does, we don't want to end up with some of the user's apps permanently + // blocked + unsuspendForSuspendingPackage(packageName, userId); + removeAllDistractingPackageRestrictions(userId); + } } } else { synchronized (mLock) { @@ -22113,7 +22216,11 @@ public class PackageManagerService extends IPackageManager.Stub targetSdkVersion = pkg.getTargetSdkVersion(); freezer = freezePackage(packageName, "movePackageInternal"); installedUserIds = ps.queryInstalledUsers(mUserManager.getUserIds(), true); - fromCodePath = pkg.getCodePath(); + if (codeFile.getParentFile().getName().startsWith(RANDOM_DIR_PREFIX)) { + fromCodePath = codeFile.getParentFile().getAbsolutePath(); + } else { + fromCodePath = codeFile.getAbsolutePath(); + } } final Bundle extras = new Bundle(); @@ -22240,9 +22347,8 @@ public class PackageManagerService extends IPackageManager.Stub } }).start(); - final String dataAppName = codeFile.getName(); move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName, - dataAppName, appId, seinfo, targetSdkVersion, fromCodePath); + appId, seinfo, targetSdkVersion, fromCodePath); } else { move = null; } @@ -23210,6 +23316,43 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public void removeAllNonSystemPackageSuspensions(int userId) { + final String[] allPackages; + synchronized (mLock) { + allPackages = mPackages.keySet().toArray(new String[mPackages.size()]); + } + PackageManagerService.this.removeSuspensionsBySuspendingPackage(allPackages, + (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage), + userId); + } + + @Override + public void removeNonSystemPackageSuspensions(String packageName, int userId) { + PackageManagerService.this.removeSuspensionsBySuspendingPackage( + new String[]{packageName}, + (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage), + userId); + } + + @Override + public void flushPackageRestrictions(int userId) { + synchronized (mLock) { + PackageManagerService.this.flushPackageRestrictionsAsUserInternalLocked(userId); + } + } + + @Override + public void removeDistractingPackageRestrictions(String packageName, int userId) { + PackageManagerService.this.removeDistractingPackageRestrictions( + new String[]{packageName}, userId); + } + + @Override + public void removeAllDistractingPackageRestrictions(int userId) { + PackageManagerService.this.removeAllDistractingPackageRestrictions(userId); + } + + @Override public String getSuspendingPackage(String suspendedPackage, int userId) { synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 18bc879b978b..318a233b6e14 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; /** * Settings base class for pending and resolved classes. @@ -433,6 +434,22 @@ public abstract class PackageSettingBase extends SettingBase { existingUserState.suspended = (existingUserState.suspendParams != null); } + void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) { + final PackageUserState existingUserState = modifyUserState(userId); + if (existingUserState.suspendParams != null) { + for (int i = existingUserState.suspendParams.size() - 1; i >= 0; i--) { + final String suspendingPackage = existingUserState.suspendParams.keyAt(i); + if (suspendingPackagePredicate.test(suspendingPackage)) { + existingUserState.suspendParams.removeAt(i); + } + } + if (existingUserState.suspendParams.size() == 0) { + existingUserState.suspendParams = null; + } + } + existingUserState.suspended = (existingUserState.suspendParams != null); + } + public boolean getInstantApp(int userId) { return readUserState(userId).instantApp; } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 2de9858d91f9..c8df5c731845 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -36,6 +36,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.ShortcutService.DumpFilter; @@ -365,9 +366,12 @@ class ShortcutPackage extends ShortcutPackageItem { /** * Remove all shortcuts that aren't pinned, cached nor dynamic. + * + * @return List of removed shortcuts. */ - private void removeOrphans() { + private List<ShortcutInfo> removeOrphans() { ArrayList<String> removeList = null; // Lazily initialize. + List<ShortcutInfo> removedShortcuts = null; for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); @@ -376,20 +380,26 @@ class ShortcutPackage extends ShortcutPackageItem { if (removeList == null) { removeList = new ArrayList<>(); + removedShortcuts = new ArrayList<>(); } removeList.add(si.getId()); + removedShortcuts.add(si); } if (removeList != null) { for (int i = removeList.size() - 1; i >= 0; i--) { forceDeleteShortcutInner(removeList.get(i)); } } + + return removedShortcuts; } /** * Remove all dynamic shortcuts. + * + * @return List of shortcuts that actually got removed. */ - public void deleteAllDynamicShortcuts(boolean ignoreInvisible) { + public List<ShortcutInfo> deleteAllDynamicShortcuts(boolean ignoreInvisible) { final long now = mShortcutUser.mService.injectCurrentTimeMillis(); boolean changed = false; @@ -404,8 +414,9 @@ class ShortcutPackage extends ShortcutPackageItem { } } if (changed) { - removeOrphans(); + return removeOrphans(); } + return null; } /** @@ -1028,7 +1039,8 @@ class ShortcutPackage extends ShortcutPackageItem { s.verifyStates(); // This will send a notification to the launcher, and also save . - s.packageShortcutsChanged(getPackageName(), getPackageUserId()); + // TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged() + s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null); return true; // true means changed. } @@ -1299,15 +1311,14 @@ class ShortcutPackage extends ShortcutPackageItem { */ public void resolveResourceStrings() { final ShortcutService s = mShortcutUser.mService; - boolean changed = false; + + List<ShortcutInfo> changedShortcuts = null; Resources publisherRes = null; for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); if (si.hasStringResources()) { - changed = true; - if (publisherRes == null) { publisherRes = getPackageResources(); if (publisherRes == null) { @@ -1317,10 +1328,15 @@ class ShortcutPackage extends ShortcutPackageItem { si.resolveResourceStrings(publisherRes); si.setTimestamp(s.injectCurrentTimeMillis()); + + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); } } - if (changed) { - s.packageShortcutsChanged(getPackageName(), getPackageUserId()); + if (!CollectionUtils.isEmpty(changedShortcuts)) { + s.packageShortcutsChanged(getPackageName(), getPackageUserId(), changedShortcuts, null); } } diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java index 3e44de989c7c..6fd997db8be8 100644 --- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java +++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java @@ -26,7 +26,6 @@ import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.PinItemRequest; import android.content.pm.ShortcutInfo; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Pair; @@ -36,6 +35,9 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import java.util.ArrayList; +import java.util.List; + /** * Handles {@link android.content.pm.ShortcutManager#requestPinShortcut} related tasks. */ @@ -452,6 +454,8 @@ class ShortcutRequestPinProcessor { final String launcherPackage = request.launcherPackage; final String shortcutId = original.getId(); + List<ShortcutInfo> changedShortcuts = null; + synchronized (mLock) { if (!(mService.isUserUnlockedL(appUserId) && mService.isUserUnlockedL(request.launcherUserId))) { @@ -506,8 +510,13 @@ class ShortcutRequestPinProcessor { Slog.d(TAG, "Pinning " + shortcutId); } + launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId, /*forPinRequest=*/ true); + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(original); if (current == null) { if (DEBUG) { @@ -520,7 +529,7 @@ class ShortcutRequestPinProcessor { } mService.verifyStates(); - mService.packageShortcutsChanged(appPackageName, appUserId); + mService.packageShortcutsChanged(appPackageName, appUserId, changedShortcuts, null); return true; } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 54f9f761241f..66f3574d83a0 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -98,6 +98,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; @@ -277,6 +278,10 @@ public class ShortcutService extends IShortcutService.Stub { private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); @GuardedBy("mLock") + private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks = + new ArrayList<>(1); + + @GuardedBy("mLock") private long mRawLastResetTime; /** @@ -1639,8 +1644,12 @@ public class ShortcutService extends IShortcutService.Stub { * - Sends a notification to LauncherApps * - Write to file */ - void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) { + void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId, + @Nullable List<ShortcutInfo> addedOrUpdatedShortcuts, + @Nullable List<ShortcutInfo> removedShortcuts) { notifyListeners(packageName, userId); + notifyShortcutChangeCallbacks(packageName, userId, addedOrUpdatedShortcuts, + removedShortcuts); scheduleSaveUser(userId); } @@ -1668,6 +1677,34 @@ public class ShortcutService extends IShortcutService.Stub { }); } + private void notifyShortcutChangeCallbacks(@NonNull String packageName, @UserIdInt int userId, + @Nullable List<ShortcutInfo> addedOrUpdatedShortcuts, + @Nullable List<ShortcutInfo> removedShortcuts) { + final UserHandle user = UserHandle.of(userId); + injectPostToHandler(() -> { + try { + final ArrayList<LauncherApps.ShortcutChangeCallback> copy; + synchronized (mLock) { + if (!isUserUnlockedL(userId)) { + return; + } + + copy = new ArrayList<>(mShortcutChangeCallbacks); + } + for (int i = copy.size() - 1; i >= 0; i--) { + if (!CollectionUtils.isEmpty(addedOrUpdatedShortcuts)) { + copy.get(i).onShortcutsAddedOrUpdated(packageName, addedOrUpdatedShortcuts, + user); + } + if (!CollectionUtils.isEmpty(removedShortcuts)) { + copy.get(i).onShortcutsRemoved(packageName, removedShortcuts, user); + } + } + } catch (Exception ignore) { + } + }); + } + /** * Clean up / validate an incoming shortcut. * - Make sure all mandatory fields are set. @@ -1762,6 +1799,8 @@ public class ShortcutService extends IShortcutService.Stub { final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( injectBinderCallingPid(), injectBinderCallingUid()); + List<ShortcutInfo> removedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); @@ -1787,7 +1826,7 @@ public class ShortcutService extends IShortcutService.Stub { } // First, remove all un-pinned; dynamic shortcuts - ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); + removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); // Then, add/update all. We need to make sure to take over "pinned" flag. for (int i = 0; i < size; i++) { @@ -1798,7 +1837,7 @@ public class ShortcutService extends IShortcutService.Stub { // Lastly, adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, newShortcuts, removedShortcuts); verifyStates(); @@ -1817,6 +1856,8 @@ public class ShortcutService extends IShortcutService.Stub { final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( injectBinderCallingPid(), injectBinderCallingUid()); + List<ShortcutInfo> changedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); @@ -1879,12 +1920,17 @@ public class ShortcutService extends IShortcutService.Stub { if (replacingIcon || source.hasStringResources()) { fixUpShortcutResourceNamesAndValues(target); } + + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(target); } // Lastly, adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, changedShortcuts, null); verifyStates(); @@ -1903,6 +1949,8 @@ public class ShortcutService extends IShortcutService.Stub { final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( injectBinderCallingPid(), injectBinderCallingUid()); + List<ShortcutInfo> changedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); @@ -1934,12 +1982,17 @@ public class ShortcutService extends IShortcutService.Stub { // Add it. ps.addOrReplaceDynamicShortcut(newShortcut); + + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(newShortcut); } // Lastly, adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, changedShortcuts, null); verifyStates(); @@ -1985,7 +2038,7 @@ public class ShortcutService extends IShortcutService.Stub { // Lastly, adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, Collections.singletonList(shortcut), null); verifyStates(); } @@ -2052,7 +2105,8 @@ public class ShortcutService extends IShortcutService.Stub { ps.updateInvisibleShortcutForPinRequestWith(shortcut); - packageShortcutsChanged(shortcutPackage, userId); + packageShortcutsChanged(shortcutPackage, userId, + Collections.singletonList(shortcut), null); } } @@ -2097,7 +2151,8 @@ public class ShortcutService extends IShortcutService.Stub { // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + // TODO: Disabling dynamic shortcuts will removed them if not pinned. Cover all cases. + packageShortcutsChanged(packageName, userId, null, null); verifyStates(); } @@ -2107,6 +2162,8 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List<ShortcutInfo> changedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); @@ -2121,9 +2178,18 @@ public class ShortcutService extends IShortcutService.Stub { continue; } ps.enableWithId(id); + + final ShortcutInfo si = ps.findShortcutById(id); + if (si != null) { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); + } } } - packageShortcutsChanged(packageName, userId); + + packageShortcutsChanged(packageName, userId, changedShortcuts, null); verifyStates(); } @@ -2134,6 +2200,9 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List<ShortcutInfo> changedShortcuts = null; + List<ShortcutInfo> removedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); @@ -2147,13 +2216,25 @@ public class ShortcutService extends IShortcutService.Stub { if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { continue; } - ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true); + final ShortcutInfo si = ps.findShortcutById(id); + final boolean removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true); + if (removed) { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(si); + } else { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); + } } // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); verifyStates(); } @@ -2162,13 +2243,19 @@ public class ShortcutService extends IShortcutService.Stub { public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); + List<ShortcutInfo> changedShortcuts = null; + List<ShortcutInfo> removedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); - ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); + + removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); } - packageShortcutsChanged(packageName, userId); + + // TODO: Pinned and cached shortcuts are not removed, add those to changedShortcuts list + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); verifyStates(); } @@ -2179,6 +2266,9 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); Objects.requireNonNull(shortcutIds, "shortcutIds must be provided"); + List<ShortcutInfo> changedShortcuts = null; + List<ShortcutInfo> removedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); @@ -2189,13 +2279,29 @@ public class ShortcutService extends IShortcutService.Stub { for (int i = shortcutIds.size() - 1; i >= 0; i--) { final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); - ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); + + final ShortcutInfo si = ps.findShortcutById(id); + final boolean removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); + + if (si != null) { + if (removed) { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(si); + } else { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); + } + } } // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); verifyStates(); } @@ -2787,6 +2893,8 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(shortcutIds, "shortcutIds"); + List<ShortcutInfo> changedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); throwIfUserLockedL(launcherUserId); @@ -2796,8 +2904,23 @@ public class ShortcutService extends IShortcutService.Stub { launcher.attemptToRestoreIfNeededAndSave(); launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false); + + final ShortcutPackage sp = getUserShortcutsLocked(userId) + .getPackageShortcutsIfExists(packageName); + if (sp != null) { + for (int i = 0; i < shortcutIds.size(); i++) { + final ShortcutInfo si = sp.findShortcutById(shortcutIds.get(i)); + if (si != null) { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); + } + } + } } - packageShortcutsChanged(packageName, userId); + // TODO: Include previously pinned shortcuts since they are not pinned anymore. + packageShortcutsChanged(packageName, userId, changedShortcuts, null); verifyStates(); } @@ -2832,6 +2955,9 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(shortcutIds, "shortcutIds"); + List<ShortcutInfo> changedShortcuts = null; + List<ShortcutInfo> removedShortcuts = null; + synchronized (mLock) { throwIfUserLockedL(userId); throwIfUserLockedL(launcherUserId); @@ -2853,20 +2979,36 @@ public class ShortcutService extends IShortcutService.Stub { if (doCache) { if (si.isDynamic() && si.isLongLived()) { si.addFlags(ShortcutInfo.FLAG_CACHED); + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); } else { Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring" + "shortcut " + si.getId()); } } else { + boolean removed = false; if (si.isDynamic()) { si.clearFlags(ShortcutInfo.FLAG_CACHED); } else { - sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); + removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); + } + if (removed) { + if (removedShortcuts == null) { + removedShortcuts = new ArrayList<>(1); + } + removedShortcuts.add(si); + } else { + if (changedShortcuts == null) { + changedShortcuts = new ArrayList<>(1); + } + changedShortcuts.add(si); } } } } - packageShortcutsChanged(packageName, userId); + packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); verifyStates(); } @@ -2912,6 +3054,14 @@ public class ShortcutService extends IShortcutService.Stub { } @Override + public void addShortcutChangeCallback( + @NonNull LauncherApps.ShortcutChangeCallback callback) { + synchronized (mLock) { + mShortcutChangeCallbacks.add(Objects.requireNonNull(callback)); + } + } + + @Override public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId) { Objects.requireNonNull(callingPackage, "callingPackage"); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 67b1008333e1..8d3f32aab9c5 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.Process; @@ -42,6 +43,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.util.Preconditions; +import com.android.server.LocalServices; import com.google.android.collect.Sets; @@ -585,6 +587,9 @@ public class UserRestrictionsUtils { android.provider.Settings.Global.putStringForUser(cr, android.provider.Settings.Global.ADB_ENABLED, "0", userId); + android.provider.Settings.Global.putStringForUser(cr, + android.provider.Settings.Global.ADB_WIFI_ENABLED, "0", + userId); } } break; @@ -676,6 +681,15 @@ public class UserRestrictionsUtils { Global.LOCATION_GLOBAL_KILL_SWITCH, "0"); } break; + case UserManager.DISALLOW_APPS_CONTROL: + // Intentional fall-through + case UserManager.DISALLOW_UNINSTALL_APPS: + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + pmi.removeAllNonSystemPackageSuspensions(userId); + pmi.removeAllDistractingPackageRestrictions(userId); + pmi.flushPackageRestrictions(userId); + break; } } finally { Binder.restoreCallingIdentity(id); @@ -721,6 +735,7 @@ public class UserRestrictionsUtils { break; case android.provider.Settings.Global.ADB_ENABLED: + case android.provider.Settings.Global.ADB_WIFI_ENABLED: if ("0".equals(value)) { return false; } 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 9c945d5a7ea8..48dd9e6f86ec 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -152,8 +152,8 @@ public final class DefaultPermissionGrantPolicy { private static final Set<String> FOREGROUND_LOCATION_PERMISSIONS = new ArraySet<>(); static { - ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION); - ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); + FOREGROUND_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION); + FOREGROUND_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); } private static final Set<String> COARSE_BACKGROUND_LOCATION_PERMISSIONS = new ArraySet<>(); diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index da3cbf9d03b4..74c3a9ec375b 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -370,30 +370,33 @@ public class ThermalManagerService extends SystemService { } @Override - public List<Temperature> getCurrentTemperatures() { + public Temperature[] getCurrentTemperatures() { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final long token = Binder.clearCallingIdentity(); try { if (!mHalReady.get()) { - return new ArrayList<>(); + return new Temperature[0]; } - return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */); + final List<Temperature> curr = mHalWrapper.getCurrentTemperatures( + false, 0 /* not used */); + return curr.toArray(new Temperature[curr.size()]); } finally { Binder.restoreCallingIdentity(token); } } @Override - public List<Temperature> getCurrentTemperaturesWithType(int type) { + public Temperature[] getCurrentTemperaturesWithType(int type) { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final long token = Binder.clearCallingIdentity(); try { if (!mHalReady.get()) { - return new ArrayList<>(); + return new Temperature[0]; } - return mHalWrapper.getCurrentTemperatures(true, type); + final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(true, type); + return curr.toArray(new Temperature[curr.size()]); } finally { Binder.restoreCallingIdentity(token); } @@ -443,30 +446,34 @@ public class ThermalManagerService extends SystemService { } @Override - public List<CoolingDevice> getCurrentCoolingDevices() { + public CoolingDevice[] getCurrentCoolingDevices() { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final long token = Binder.clearCallingIdentity(); try { if (!mHalReady.get()) { - return new ArrayList<>(); + return new CoolingDevice[0]; } - return mHalWrapper.getCurrentCoolingDevices(false, 0); + final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices( + false, 0); + return devList.toArray(new CoolingDevice[devList.size()]); } finally { Binder.restoreCallingIdentity(token); } } @Override - public List<CoolingDevice> getCurrentCoolingDevicesWithType(int type) { + public CoolingDevice[] getCurrentCoolingDevicesWithType(int type) { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final long token = Binder.clearCallingIdentity(); try { if (!mHalReady.get()) { - return new ArrayList<>(); + return new CoolingDevice[0]; } - return mHalWrapper.getCurrentCoolingDevices(true, type); + final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices( + true, type); + return devList.toArray(new CoolingDevice[devList.size()]); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 47a26f576949..aed2d9bb9dc7 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -41,6 +41,7 @@ import android.app.AppOpsManager.HistoricalPackageOps; import android.app.AppOpsManager.HistoricalUidOps; import android.app.INotificationManager; import android.app.ProcessMemoryState; +import android.app.RuntimeAppOpAccessMessage; import android.app.StatsManager; import android.app.StatsManager.PullAtomMetadata; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -81,6 +82,7 @@ import android.os.SynchronousResultReceiver; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Temperature; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.connectivity.WifiActivityEnergyInfo; @@ -270,125 +272,134 @@ public class StatsPullAtomService extends SystemService { private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback { @Override public int onPullAtom(int atomTag, List<StatsEvent> data) { - switch(atomTag) { - case FrameworkStatsLog.WIFI_BYTES_TRANSFER: - return pullWifiBytesTransfer(atomTag, data); - case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: - return pullWifiBytesTransferBackground(atomTag, data); - case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: - return pullMobileBytesTransfer(atomTag, data); - case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: - return pullMobileBytesTransferBackground(atomTag, data); - case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER: - return pullBluetoothBytesTransfer(atomTag, data); - case FrameworkStatsLog.KERNEL_WAKELOCK: - return pullKernelWakelock(atomTag, data); - case FrameworkStatsLog.CPU_TIME_PER_FREQ: - return pullCpuTimePerFreq(atomTag, data); - case FrameworkStatsLog.CPU_TIME_PER_UID: - return pullCpuTimePerUid(atomTag, data); - case FrameworkStatsLog.CPU_TIME_PER_UID_FREQ: - return pullCpuTimeperUidFreq(atomTag, data); - case FrameworkStatsLog.CPU_ACTIVE_TIME: - return pullCpuActiveTime(atomTag, data); - case FrameworkStatsLog.CPU_CLUSTER_TIME: - return pullCpuClusterTime(atomTag, data); - case FrameworkStatsLog.WIFI_ACTIVITY_INFO: - return pullWifiActivityInfo(atomTag, data); - case FrameworkStatsLog.MODEM_ACTIVITY_INFO: - return pullModemActivityInfo(atomTag, data); - case FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO: - return pullBluetoothActivityInfo(atomTag, data); - case FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME: - return pullSystemElapsedRealtime(atomTag, data); - case FrameworkStatsLog.SYSTEM_UPTIME: - return pullSystemUptime(atomTag, data); - case FrameworkStatsLog.PROCESS_MEMORY_STATE: - return pullProcessMemoryState(atomTag, data); - case FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: - return pullProcessMemoryHighWaterMark(atomTag, data); - case FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT: - return pullProcessMemorySnapshot(atomTag, data); - case FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE: - return pullSystemIonHeapSize(atomTag, data); - case FrameworkStatsLog.ION_HEAP_SIZE: - return pullIonHeapSize(atomTag, data); - case FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: - return pullProcessSystemIonHeapSize(atomTag, data); - case FrameworkStatsLog.TEMPERATURE: - return pullTemperature(atomTag, data); - case FrameworkStatsLog.COOLING_DEVICE: - return pullCooldownDevice(atomTag, data); - case FrameworkStatsLog.BINDER_CALLS: - return pullBinderCallsStats(atomTag, data); - case FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS: - return pullBinderCallsStatsExceptions(atomTag, data); - case FrameworkStatsLog.LOOPER_STATS: - return pullLooperStats(atomTag, data); - case FrameworkStatsLog.DISK_STATS: - return pullDiskStats(atomTag, data); - case FrameworkStatsLog.DIRECTORY_USAGE: - return pullDirectoryUsage(atomTag, data); - case FrameworkStatsLog.APP_SIZE: - return pullAppSize(atomTag, data); - case FrameworkStatsLog.CATEGORY_SIZE: - return pullCategorySize(atomTag, data); - case FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED: - return pullNumBiometricsEnrolled( - BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data); - case FrameworkStatsLog.NUM_FACES_ENROLLED: - return pullNumBiometricsEnrolled( - BiometricsProtoEnums.MODALITY_FACE, atomTag, data); - case FrameworkStatsLog.PROC_STATS: - return pullProcStats(ProcessStats.REPORT_ALL, atomTag, data); - case FrameworkStatsLog.PROC_STATS_PKG_PROC: - return pullProcStats(ProcessStats.REPORT_PKG_PROC_STATS, atomTag, data); - case FrameworkStatsLog.DISK_IO: - return pullDiskIO(atomTag, data); - case FrameworkStatsLog.POWER_PROFILE: - return pullPowerProfile(atomTag, data); - case FrameworkStatsLog.PROCESS_CPU_TIME: - return pullProcessCpuTime(atomTag, data); - case FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ: - return pullCpuTimePerThreadFreq(atomTag, data); - case FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE: - return pullDeviceCalculatedPowerUse(atomTag, data); - case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: - return pullDeviceCalculatedPowerBlameUid(atomTag, data); - case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: - return pullDeviceCalculatedPowerBlameOther(atomTag, data); - case FrameworkStatsLog.DEBUG_ELAPSED_CLOCK: - return pullDebugElapsedClock(atomTag, data); - case FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK: - return pullDebugFailingElapsedClock(atomTag, data); - case FrameworkStatsLog.BUILD_INFORMATION: - return pullBuildInformation(atomTag, data); - case FrameworkStatsLog.ROLE_HOLDER: - return pullRoleHolder(atomTag, data); - case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE: - return pullDangerousPermissionState(atomTag, data); - case FrameworkStatsLog.TIME_ZONE_DATA_INFO: - return pullTimeZoneDataInfo(atomTag, data); - case FrameworkStatsLog.EXTERNAL_STORAGE_INFO: - return pullExternalStorageInfo(atomTag, data); - case FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO: - return pullAppsOnExternalStorageInfo(atomTag, data); - case FrameworkStatsLog.FACE_SETTINGS: - return pullFaceSettings(atomTag, data); - case FrameworkStatsLog.APP_OPS: - return pullAppOps(atomTag, data); - case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS: - return pullNotificationRemoteViews(atomTag, data); - case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED: - return pullDangerousPermissionState(atomTag, data); - case FrameworkStatsLog.BATTERY_LEVEL: - case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY: - case FrameworkStatsLog.FULL_BATTERY_CAPACITY: - case FrameworkStatsLog.BATTERY_VOLTAGE: - case FrameworkStatsLog.BATTERY_CYCLE_COUNT: - return pullHealthHal(atomTag, data); - default: - throw new UnsupportedOperationException("Unknown tagId=" + atomTag); + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StatsPull-" + atomTag); + } + try { + switch (atomTag) { + case FrameworkStatsLog.WIFI_BYTES_TRANSFER: + return pullWifiBytesTransfer(atomTag, data); + case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: + return pullWifiBytesTransferBackground(atomTag, data); + case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: + return pullMobileBytesTransfer(atomTag, data); + case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: + return pullMobileBytesTransferBackground(atomTag, data); + case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER: + return pullBluetoothBytesTransfer(atomTag, data); + case FrameworkStatsLog.KERNEL_WAKELOCK: + return pullKernelWakelock(atomTag, data); + case FrameworkStatsLog.CPU_TIME_PER_FREQ: + return pullCpuTimePerFreq(atomTag, data); + case FrameworkStatsLog.CPU_TIME_PER_UID: + return pullCpuTimePerUid(atomTag, data); + case FrameworkStatsLog.CPU_TIME_PER_UID_FREQ: + return pullCpuTimeperUidFreq(atomTag, data); + case FrameworkStatsLog.CPU_ACTIVE_TIME: + return pullCpuActiveTime(atomTag, data); + case FrameworkStatsLog.CPU_CLUSTER_TIME: + return pullCpuClusterTime(atomTag, data); + case FrameworkStatsLog.WIFI_ACTIVITY_INFO: + return pullWifiActivityInfo(atomTag, data); + case FrameworkStatsLog.MODEM_ACTIVITY_INFO: + return pullModemActivityInfo(atomTag, data); + case FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO: + return pullBluetoothActivityInfo(atomTag, data); + case FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME: + return pullSystemElapsedRealtime(atomTag, data); + case FrameworkStatsLog.SYSTEM_UPTIME: + return pullSystemUptime(atomTag, data); + case FrameworkStatsLog.PROCESS_MEMORY_STATE: + return pullProcessMemoryState(atomTag, data); + case FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: + return pullProcessMemoryHighWaterMark(atomTag, data); + case FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT: + return pullProcessMemorySnapshot(atomTag, data); + case FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE: + return pullSystemIonHeapSize(atomTag, data); + case FrameworkStatsLog.ION_HEAP_SIZE: + return pullIonHeapSize(atomTag, data); + case FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: + return pullProcessSystemIonHeapSize(atomTag, data); + case FrameworkStatsLog.TEMPERATURE: + return pullTemperature(atomTag, data); + case FrameworkStatsLog.COOLING_DEVICE: + return pullCooldownDevice(atomTag, data); + case FrameworkStatsLog.BINDER_CALLS: + return pullBinderCallsStats(atomTag, data); + case FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS: + return pullBinderCallsStatsExceptions(atomTag, data); + case FrameworkStatsLog.LOOPER_STATS: + return pullLooperStats(atomTag, data); + case FrameworkStatsLog.DISK_STATS: + return pullDiskStats(atomTag, data); + case FrameworkStatsLog.DIRECTORY_USAGE: + return pullDirectoryUsage(atomTag, data); + case FrameworkStatsLog.APP_SIZE: + return pullAppSize(atomTag, data); + case FrameworkStatsLog.CATEGORY_SIZE: + return pullCategorySize(atomTag, data); + case FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED: + return pullNumBiometricsEnrolled( + BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data); + case FrameworkStatsLog.NUM_FACES_ENROLLED: + return pullNumBiometricsEnrolled( + BiometricsProtoEnums.MODALITY_FACE, atomTag, data); + case FrameworkStatsLog.PROC_STATS: + return pullProcStats(ProcessStats.REPORT_ALL, atomTag, data); + case FrameworkStatsLog.PROC_STATS_PKG_PROC: + return pullProcStats(ProcessStats.REPORT_PKG_PROC_STATS, atomTag, data); + case FrameworkStatsLog.DISK_IO: + return pullDiskIO(atomTag, data); + case FrameworkStatsLog.POWER_PROFILE: + return pullPowerProfile(atomTag, data); + case FrameworkStatsLog.PROCESS_CPU_TIME: + return pullProcessCpuTime(atomTag, data); + case FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ: + return pullCpuTimePerThreadFreq(atomTag, data); + case FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE: + return pullDeviceCalculatedPowerUse(atomTag, data); + case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: + return pullDeviceCalculatedPowerBlameUid(atomTag, data); + case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: + return pullDeviceCalculatedPowerBlameOther(atomTag, data); + case FrameworkStatsLog.DEBUG_ELAPSED_CLOCK: + return pullDebugElapsedClock(atomTag, data); + case FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK: + return pullDebugFailingElapsedClock(atomTag, data); + case FrameworkStatsLog.BUILD_INFORMATION: + return pullBuildInformation(atomTag, data); + case FrameworkStatsLog.ROLE_HOLDER: + return pullRoleHolder(atomTag, data); + case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE: + return pullDangerousPermissionState(atomTag, data); + case FrameworkStatsLog.TIME_ZONE_DATA_INFO: + return pullTimeZoneDataInfo(atomTag, data); + case FrameworkStatsLog.EXTERNAL_STORAGE_INFO: + return pullExternalStorageInfo(atomTag, data); + case FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO: + return pullAppsOnExternalStorageInfo(atomTag, data); + case FrameworkStatsLog.FACE_SETTINGS: + return pullFaceSettings(atomTag, data); + case FrameworkStatsLog.APP_OPS: + return pullAppOps(atomTag, data); + case FrameworkStatsLog.RUNTIME_APP_OP_ACCESS: + return pullRuntimeAppOpAccessMessage(atomTag, data); + case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS: + return pullNotificationRemoteViews(atomTag, data); + case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED: + return pullDangerousPermissionState(atomTag, data); + case FrameworkStatsLog.BATTERY_LEVEL: + case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY: + case FrameworkStatsLog.FULL_BATTERY_CAPACITY: + case FrameworkStatsLog.BATTERY_VOLTAGE: + case FrameworkStatsLog.BATTERY_CYCLE_COUNT: + return pullHealthHal(atomTag, data); + default: + throw new UnsupportedOperationException("Unknown tagId=" + atomTag); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } } @@ -539,6 +550,7 @@ public class StatsPullAtomService extends SystemService { registerAppsOnExternalStorageInfo(); registerFaceSettings(); registerAppOps(); + registerRuntimeAppOpAccessMessage(); registerNotificationRemoteViews(); registerDangerousPermissionState(); registerDangerousPermissionStateSampled(); @@ -1515,7 +1527,7 @@ public class StatsPullAtomService extends SystemService { } final long callingToken = Binder.clearCallingIdentity(); try { - List<Temperature> temperatures = thermalService.getCurrentTemperatures(); + Temperature temperatures[] = thermalService.getCurrentTemperatures(); for (Temperature temp : temperatures) { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) @@ -1553,7 +1565,7 @@ public class StatsPullAtomService extends SystemService { } final long callingToken = Binder.clearCallingIdentity(); try { - List<CoolingDevice> devices = thermalService.getCurrentCoolingDevices(); + CoolingDevice devices[] = thermalService.getCurrentCoolingDevices(); for (CoolingDevice device : devices) { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) @@ -2834,6 +2846,17 @@ public class StatsPullAtomService extends SystemService { } + private void registerRuntimeAppOpAccessMessage() { + int tagId = FrameworkStatsLog.RUNTIME_APP_OP_ACCESS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + BackgroundThread.getExecutor(), + mStatsCallbackImpl + ); + + } + int pullAppOps(int atomTag, List<StatsEvent> pulledData) { final long token = Binder.clearCallingIdentity(); try { @@ -2894,6 +2917,41 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) { + final long token = Binder.clearCallingIdentity(); + try { + AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + + RuntimeAppOpAccessMessage message = appOps.collectRuntimeAppOpAccessMessage(); + if (message == null) { + Slog.i(TAG, "No runtime appop access message collected"); + return StatsManager.PULL_SUCCESS; + } + + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeInt(message.getUid()); + e.writeString(message.getPackageName()); + e.writeString(message.getOp()); + if (message.getFeatureId() == null) { + e.writeString(""); + } else { + e.writeString(message.getFeatureId()); + } + e.writeString(message.getMessage()); + e.writeInt(message.getSamplingStrategy()); + + pulledData.add(e.build()); + } catch (Throwable t) { + // TODO: catch exceptions at a more granular level + Slog.e(TAG, "Could not read runtime appop access message", t); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + static void unpackStreamedData(int atomTag, List<StatsEvent> pulledData, List<ParcelFileDescriptor> statsFiles) throws IOException { InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0)); diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 5b892f808e2f..deaec0ac75ae 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -158,7 +158,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal } final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids, - null /* processCpuTracker */, null /* lastPids */, nativePids); + null /* processCpuTracker */, null /* lastPids */, nativePids, + null /* logExceptionCreatingFile */); if (tracesFile != null) { tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre")); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 68b8348e706f..337764ae32f5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1169,10 +1169,9 @@ public class WindowManagerService extends IWindowManager.Stub mAnimator = new WindowAnimator(this); mRoot = new RootWindowContainer(this); - mUseBLAST = SystemProperties.getBoolean( - String.join(".", "persist.device_config", + mUseBLAST = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, - WM_USE_BLAST_ADAPTER_FLAG), false); + WM_USE_BLAST_ADAPTER_FLAG, false); mWindowPlacerLocked = new WindowSurfacePlacer(this); mTaskSnapshotController = new TaskSnapshotController(this); @@ -2584,11 +2583,18 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void addWindowToken(IBinder binder, int type, int displayId) { addWindowTokenWithOptions(binder, type, displayId, null /* options */, - null /* packageName */); + null /* packageName */, false /* fromClientToken */); } + @Override public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName) { + return addWindowTokenWithOptions(binder, type, displayId, options, packageName, + true /* fromClientToken */); + } + + private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, + String packageName, boolean fromClientToken) { final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()"); if (!callerCanManageAppTokens) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 48c7812afec0..76a03150c8df 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -23,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; +import static com.android.server.wm.ProtoLogGroup.WM_ERROR; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -36,10 +38,12 @@ import static com.android.server.wm.WindowTokenProto.WINDOWS; import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; import android.annotation.CallSuper; +import android.app.IWindowToken; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; +import android.os.RemoteException; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.InsetsState; @@ -91,6 +95,11 @@ class WindowToken extends WindowContainer<WindowState> { private FixedRotationTransformState mFixedRotationTransformState; + private Configuration mLastReportedConfig; + private int mLastReportedDisplay = INVALID_DISPLAY; + + private final boolean mFromClientToken; + /** * Used to fix the transform of the token to be rotated to a rotation different than it's * display. The window frames and surfaces corresponding to this token will be layouted and @@ -167,13 +176,21 @@ class WindowToken extends WindowContainer<WindowState> { } WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { + DisplayContent dc, boolean ownerCanManageAppTokens, boolean fromClientToken) { + this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, + false /* roundedCornersOverlay */, fromClientToken); + } + + WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, + DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay, + boolean fromClientToken) { super(service); token = _token; windowType = type; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; mRoundedCornerOverlay = roundedCornerOverlay; + mFromClientToken = fromClientToken; if (dc != null) { dc.addWindowToken(token, this); } @@ -305,8 +322,47 @@ class WindowToken extends WindowContainer<WindowState> { // up with goodToGo, so we don't move a window // to another display before the window behind // it is ready. - super.onDisplayChanged(dc); + reportConfigToWindowTokenClient(); + } + + @Override + public void onConfigurationChanged(Configuration newParentConfig) { + super.onConfigurationChanged(newParentConfig); + reportConfigToWindowTokenClient(); + } + + void reportConfigToWindowTokenClient() { + if (asActivityRecord() != null) { + // Activities are updated through ATM callbacks. + return; + } + + // Unfortunately, this WindowToken is not from WindowContext so it cannot handle + // its own configuration changes. + if (!mFromClientToken) { + return; + } + + final Configuration config = getConfiguration(); + final int displayId = getDisplayContent().getDisplayId(); + if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) { + // No changes since last reported time. + return; + } + + mLastReportedConfig = config; + mLastReportedDisplay = displayId; + + IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); + if (windowTokenClient != null) { + try { + windowTokenClient.onConfigurationChanged(config, displayId); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, + "Could not report config changes to the window token client."); + } + } } @Override diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 8772edd73845..e0d40452590b 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -27,8 +27,6 @@ #define DEBUG_INPUT_DISPATCHER_POLICY 0 -#include <nativehelper/JNIHelp.h> -#include "jni.h" #include <atomic> #include <cinttypes> #include <limits.h> @@ -50,7 +48,6 @@ #include <inputflinger/InputManager.h> -#include <android/graphics/GraphicsJNI.h> #include <android_os_MessageQueue.h> #include <android_view_InputChannel.h> #include <android_view_InputDevice.h> diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 70a9c09c815d..f445aa810753 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -190,6 +190,7 @@ static inline std::vector<char> readBytes(borrowed_fd fd) { } static inline int32_t skipIdSigHeaders(borrowed_fd fd) { + readBEInt32(fd); // version readBytes(fd); // verityRootHash readBytes(fd); // v3Digest readBytes(fd); // pkcs7SignatureBlock diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index e57543203634..f9238e3977c6 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android/graphics/jni_runtime.h> #include <nativehelper/JNIHelp.h> #include "jni.h" #include "utils/Log.h" @@ -49,7 +50,6 @@ int register_android_server_PersistentDataBlockService(JNIEnv* env); int register_android_server_Watchdog(JNIEnv* env); int register_android_server_HardwarePropertiesManagerService(JNIEnv* env); int register_android_server_SyntheticPasswordManager(JNIEnv* env); -int register_android_graphics_GraphicsStatsService(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); int register_android_server_net_NetworkStatsFactory(JNIEnv* env); int register_android_server_net_NetworkStatsService(JNIEnv* env); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 012fdfc5bbd0..440b779b8566 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -186,7 +186,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Color; import android.location.LocationManager; -import android.location.LocationManagerInternal; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; @@ -445,10 +444,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); /** - * System property whose value is either "true" or "false", indicating whether - * device owner is present. + * System property whose value indicates whether the device is fully owned by an organization: + * it can be either a device owner device, or a device with an organization-owned managed + * profile. + * + * <p>The state is stored as a Boolean string. */ - private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.organization_owned"; + private static final String PROPERTY_ORGANIZATION_OWNED = "ro.organization_owned"; private static final int STATUS_BAR_DISABLE_MASK = StatusBarManager.DISABLE_EXPAND | @@ -481,6 +483,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { GLOBAL_SETTINGS_WHITELIST = new ArraySet<>(); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED); + GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_WIFI_ENABLED); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING); @@ -2131,10 +2134,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(LocationManager.class); } - LocationManagerInternal getLocationManagerInternal() { - return LocalServices.getService(LocationManagerInternal.class); - } - IWindowManager getIWindowManager() { return IWindowManager.Stub .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)); @@ -2555,7 +2554,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { void loadOwners() { synchronized (getLockObject()) { mOwners.load(); - setDeviceOwnerSystemPropertyLocked(); + setDeviceOwnershipSystemPropertyLocked(); findOwnerComponentIfNecessaryLocked(); migrateUserRestrictionsIfNecessaryLocked(); @@ -2757,34 +2756,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void setDeviceOwnerSystemPropertyLocked() { + private void setDeviceOwnershipSystemPropertyLocked() { + // Still at the first stage of CryptKeeper double bounce, nothing can be learnt about + // the real system at this point. + if (StorageManager.inCryptKeeperBounce()) { + return; + } final boolean deviceProvisioned = mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0; final boolean hasDeviceOwner = mOwners.hasDeviceOwner(); - // If the device is not provisioned and there is currently no device owner, do not set the - // read-only system property yet, since Device owner may still be provisioned. - if (!hasDeviceOwner && !deviceProvisioned) { + final boolean hasOrgOwnedProfile = isOrganizationOwnedDeviceWithManagedProfile(); + // If the device is not provisioned and there is currently no management, do not set the + // read-only system property yet, since device owner / org-owned profile may still be + // provisioned. + if (!hasDeviceOwner && !hasOrgOwnedProfile && !deviceProvisioned) { return; } - // Still at the first stage of CryptKeeper double bounce, mOwners.hasDeviceOwner is - // always false at this point. - if (StorageManager.inCryptKeeperBounce()) { - return; - } - - if (!mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT, "").isEmpty()) { - Slog.w(LOG_TAG, "Trying to set ro.organization_owned, but it has already been set?"); - } else { - final String value = Boolean.toString(hasDeviceOwner); - mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, value); + final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile); + final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null); + if (TextUtils.isEmpty(currentVal)) { Slog.i(LOG_TAG, "Set ro.organization_owned property to " + value); + mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value); + } else if (!value.equals(currentVal)) { + Slog.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value); } } private void maybeStartSecurityLogMonitorOnActivityManagerReady() { synchronized (getLockObject()) { if (mInjector.securityLogIsLoggingEnabled()) { - mSecurityLogMonitor.start(); + mSecurityLogMonitor.start(getSecurityLoggingEnabledUser()); mInjector.runCryptoSelfTest(); maybePauseDeviceWideLoggingLocked(); } @@ -8396,7 +8397,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.setDeviceOwner(admin, ownerName, userId); mOwners.writeDeviceOwner(); updateDeviceOwnerLocked(); - setDeviceOwnerSystemPropertyLocked(); + setDeviceOwnershipSystemPropertyLocked(); mInjector.binderWithCleanCallingIdentity(() -> { // Restrict adding a managed profile when a device owner is set on the device. @@ -9076,21 +9077,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return getApplicationLabel(profileOwner.getPackageName(), userHandle); } + private @UserIdInt int getOrganizationOwnedProfileUserId() { + for (UserInfo ui : mUserManagerInternal.getUserInfos()) { + if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) { + return ui.id; + } + } + return UserHandle.USER_NULL; + } + @Override public boolean isOrganizationOwnedDeviceWithManagedProfile() { if (!mHasFeature) { return false; } - - return mInjector.binderWithCleanCallingIdentity(() -> { - for (UserInfo ui : mUserManager.getUsers()) { - if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) { - return true; - } - } - - return false; - }); + return getOrganizationOwnedProfileUserId() != UserHandle.USER_NULL; } @Override @@ -11175,6 +11176,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.binderRestoreCallingIdentity(id); } } + if (uninstallBlocked) { + final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); + pmi.removeNonSystemPackageSuspensions(packageName, userId); + pmi.removeDistractingPackageRestrictions(packageName, userId); + pmi.flushPackageRestrictions(userId); + } final boolean isDelegate = (who == null); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_UNINSTALL_BLOCKED) @@ -11703,17 +11710,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public void requestSetLocationProviderAllowed(ComponentName who, String provider, - boolean providerAllowed) { - Objects.requireNonNull(who, "ComponentName is null"); - enforceDeviceOwner(who); - - mInjector.binderWithCleanCallingIdentity( - () -> mInjector.getLocationManagerInternal().requestSetProviderAllowed(provider, - providerAllowed)); - } - - @Override public boolean setTime(ComponentName who, long millis) { Objects.requireNonNull(who, "ComponentName is null in setTime"); enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who); @@ -12062,7 +12058,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { // Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property // is delayed until device is marked as provisioned. - setDeviceOwnerSystemPropertyLocked(); + setDeviceOwnershipSystemPropertyLocked(); } } else if (mDefaultImeChanged.equals(uri)) { synchronized (getLockObject()) { @@ -13687,6 +13683,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); } + private boolean canStartSecurityLogging() { + synchronized (getLockObject()) { + return isOrganizationOwnedDeviceWithManagedProfile() + || areAllUsersAffiliatedWithDeviceLocked(); + } + } + + private @UserIdInt int getSecurityLoggingEnabledUser() { + synchronized (getLockObject()) { + if (mOwners.hasDeviceOwner()) { + return UserHandle.USER_ALL; + } + } + return getOrganizationOwnedProfileUserId(); + } + @Override public void setSecurityLoggingEnabled(ComponentName admin, boolean enabled) { if (!mHasFeature) { @@ -13695,13 +13707,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + getActiveAdminForCallerLocked(admin, + DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) { return; } mInjector.securityLogSetLoggingEnabledProperty(enabled); if (enabled) { - mSecurityLogMonitor.start(); + mSecurityLogMonitor.start(getSecurityLoggingEnabledUser()); maybePauseDeviceWideLoggingLocked(); } else { mSecurityLogMonitor.stop(); @@ -13723,7 +13736,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { if (!isCallerWithSystemUid()) { Objects.requireNonNull(admin); - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + getActiveAdminForCallerLocked(admin, + DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); } return mInjector.securityLogGetLoggingEnabledProperty(); } @@ -13747,7 +13761,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin); - ensureDeviceOwnerAndAllUsersAffiliated(admin); + enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin); + if (!isOrganizationOwnedDeviceWithManagedProfile()) { + ensureAllUsersAffiliated(); + } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.RETRIEVE_PRE_REBOOT_SECURITY_LOGS) @@ -13763,6 +13780,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ArrayList<SecurityEvent> output = new ArrayList<SecurityEvent>(); try { SecurityLog.readPreviousEvents(output); + int enabledUser = getSecurityLoggingEnabledUser(); + if (enabledUser != UserHandle.USER_ALL) { + SecurityLog.redactEvents(output, enabledUser); + } return new ParceledListSlice<SecurityEvent>(output); } catch (IOException e) { Slog.w(LOG_TAG, "Fail to read previous events" , e); @@ -13777,7 +13798,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin); - ensureDeviceOwnerAndAllUsersAffiliated(admin); + enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin); + if (!isOrganizationOwnedDeviceWithManagedProfile()) { + ensureAllUsersAffiliated(); + } if (!mInjector.securityLogGetLoggingEnabledProperty()) { return null; @@ -14303,26 +14327,34 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @GuardedBy("getLockObject()") private void maybePauseDeviceWideLoggingLocked() { if (!areAllUsersAffiliatedWithDeviceLocked()) { - Slog.i(LOG_TAG, "There are unaffiliated users, security and network logging will be " + Slog.i(LOG_TAG, "There are unaffiliated users, network logging will be " + "paused if enabled."); - mSecurityLogMonitor.pause(); if (mNetworkLogger != null) { mNetworkLogger.pause(); } + if (!isOrganizationOwnedDeviceWithManagedProfile()) { + Slog.i(LOG_TAG, "Not org-owned managed profile device, security logging will be " + + "paused if enabled."); + mSecurityLogMonitor.pause(); + } } } /** Resumes security and network logging (if they are enabled) if all users are affiliated */ @GuardedBy("getLockObject()") private void maybeResumeDeviceWideLoggingLocked() { - if (areAllUsersAffiliatedWithDeviceLocked()) { - mInjector.binderWithCleanCallingIdentity(() -> { + boolean allUsersAffiliated = areAllUsersAffiliatedWithDeviceLocked(); + boolean orgOwnedProfileDevice = isOrganizationOwnedDeviceWithManagedProfile(); + mInjector.binderWithCleanCallingIdentity(() -> { + if (allUsersAffiliated || orgOwnedProfileDevice) { mSecurityLogMonitor.resume(); + } + if (allUsersAffiliated) { if (mNetworkLogger != null) { mNetworkLogger.resume(); } - }); - } + } + }); } /** Deletes any security and network logs that might have been collected so far */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java index 1ab3b98ae78d..3c445ca78520 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java @@ -51,15 +51,17 @@ class SecurityLogMonitor implements Runnable { private final Lock mLock = new ReentrantLock(); + private int mEnabledUser; + SecurityLogMonitor(DevicePolicyManagerService service) { this(service, 0 /* id */); } @VisibleForTesting SecurityLogMonitor(DevicePolicyManagerService service, long id) { - this.mService = service; - this.mId = id; - this.mLastForceNanos = System.nanoTime(); + mService = service; + mId = id; + mLastForceNanos = System.nanoTime(); } private static final boolean DEBUG = false; // STOPSHIP if true. @@ -136,8 +138,15 @@ class SecurityLogMonitor implements Runnable { @GuardedBy("mForceSemaphore") private long mLastForceNanos = 0; - void start() { - Slog.i(TAG, "Starting security logging."); + /** + * Start security logging. + * + * @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all + * users on the device. + */ + void start(int enabledUser) { + Slog.i(TAG, "Starting security logging for user " + enabledUser); + mEnabledUser = enabledUser; SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED); mLock.lock(); try { @@ -286,7 +295,7 @@ class SecurityLogMonitor implements Runnable { break; } } - + SecurityLog.redactEvents(newLogs, mEnabledUser); if (DEBUG) Slog.d(TAG, "Got " + newLogs.size() + " new events."); } diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java index 859cdf24bc7e..41bc3611e085 100644 --- a/services/people/java/com/android/server/people/data/ConversationInfo.java +++ b/services/people/java/com/android/server/people/data/ConversationInfo.java @@ -274,6 +274,10 @@ public class ConversationInfo { } protoOutputStream.write(ConversationInfoProto.SHORTCUT_FLAGS, mShortcutFlags); protoOutputStream.write(ConversationInfoProto.CONVERSATION_FLAGS, mConversationFlags); + if (mContactPhoneNumber != null) { + protoOutputStream.write(ConversationInfoProto.CONTACT_PHONE_NUMBER, + mContactPhoneNumber); + } } /** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */ @@ -315,6 +319,10 @@ public class ConversationInfo { builder.setConversationFlags(protoInputStream.readInt( ConversationInfoProto.CONVERSATION_FLAGS)); break; + case (int) ConversationInfoProto.CONTACT_PHONE_NUMBER: + builder.setContactPhoneNumber(protoInputStream.readString( + ConversationInfoProto.CONTACT_PHONE_NUMBER)); + break; default: Slog.w(TAG, "Could not read undefined field: " + protoInputStream.getFieldNumber()); diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java index 62e9da83869c..89c4972c8ef4 100644 --- a/services/people/java/com/android/server/people/data/ConversationStore.java +++ b/services/people/java/com/android/server/people/data/ConversationStore.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import android.content.LocusId; import android.net.Uri; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; import android.util.proto.ProtoInputStream; @@ -71,16 +70,13 @@ class ConversationStore { private final ScheduledExecutorService mScheduledExecutorService; private final File mPackageDir; - private final ContactsQueryHelper mHelper; private ConversationInfosProtoDiskReadWriter mConversationInfosProtoDiskReadWriter; ConversationStore(@NonNull File packageDir, - @NonNull ScheduledExecutorService scheduledExecutorService, - @NonNull ContactsQueryHelper helper) { + @NonNull ScheduledExecutorService scheduledExecutorService) { mScheduledExecutorService = scheduledExecutorService; mPackageDir = packageDir; - mHelper = helper; } /** @@ -102,7 +98,6 @@ class ConversationStore { return; } for (ConversationInfo conversationInfo : conversationsOnDisk) { - conversationInfo = restoreConversationPhoneNumber(conversationInfo); updateConversationsInMemory(conversationInfo); } } @@ -250,25 +245,6 @@ class ConversationStore { return mConversationInfosProtoDiskReadWriter; } - /** - * Conversation's phone number is not saved on disk, so it has to be fetched. - */ - @WorkerThread - private ConversationInfo restoreConversationPhoneNumber( - @NonNull ConversationInfo conversationInfo) { - if (conversationInfo.getContactUri() != null) { - if (mHelper.query(conversationInfo.getContactUri().toString())) { - String phoneNumber = mHelper.getPhoneNumber(); - if (!TextUtils.isEmpty(phoneNumber)) { - conversationInfo = new ConversationInfo.Builder( - conversationInfo).setContactPhoneNumber( - phoneNumber).build(); - } - } - } - return conversationInfo; - } - /** Reads and writes {@link ConversationInfo}s on disk. */ private static class ConversationInfosProtoDiskReadWriter extends AbstractProtoDiskReadWriter<List<ConversationInfo>> { diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 27c169227637..3a34c6a6be95 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -31,7 +31,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager.ShareShortcutInfo; @@ -53,6 +55,7 @@ import android.service.notification.StatusBarNotification; import android.telecom.TelecomManager; import android.text.format.DateUtils; import android.util.ArraySet; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -61,11 +64,13 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.telephony.SmsApplication; import com.android.server.LocalServices; +import com.android.server.notification.NotificationManagerInternal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -80,8 +85,8 @@ import java.util.function.Function; */ public class DataManager { - private static final int MY_UID = Process.myUid(); - private static final int MY_PID = Process.myPid(); + private static final String TAG = "DataManager"; + private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS; private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L; @@ -102,6 +107,7 @@ public class DataManager { private ShortcutServiceInternal mShortcutServiceInternal; private PackageManagerInternal mPackageManagerInternal; + private NotificationManagerInternal mNotificationManagerInternal; private UserManager mUserManager; public DataManager(Context context) { @@ -120,9 +126,10 @@ public class DataManager { public void initialize() { mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + mNotificationManagerInternal = LocalServices.getService(NotificationManagerInternal.class); mUserManager = mContext.getSystemService(UserManager.class); - mShortcutServiceInternal.addListener(new ShortcutServiceListener()); + mShortcutServiceInternal.addShortcutChangeCallback(new ShortcutServiceCallback()); IntentFilter shutdownIntentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); BroadcastReceiver shutdownBroadcastReceiver = new ShutdownBroadcastReceiver(); @@ -133,8 +140,7 @@ public class DataManager { public void onUserUnlocked(int userId) { UserData userData = mUserDataArray.get(userId); if (userData == null) { - userData = new UserData(userId, mDiskReadWriterExecutor, - mInjector.createContactsQueryHelper(mContext)); + userData = new UserData(userId, mDiskReadWriterExecutor); mUserDataArray.put(userId, userData); } userData.setUserUnlocked(); @@ -363,7 +369,7 @@ public class DataManager { return mShortcutServiceInternal.getShortcuts( UserHandle.USER_SYSTEM, mContext.getPackageName(), /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null, - /*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID); + /*componentName=*/ null, queryFlags, userId, Process.myPid(), Process.myUid()); } private void forAllUnlockedUsers(Consumer<UserData> consumer) { @@ -622,14 +628,12 @@ public class DataManager { } /** Listener for the shortcut data changes. */ - private class ShortcutServiceListener implements - ShortcutServiceInternal.ShortcutChangeListener { + private class ShortcutServiceCallback implements LauncherApps.ShortcutChangeCallback { @Override - public void onShortcutChanged(@NonNull String packageName, int userId) { - BackgroundThread.getExecutor().execute(() -> { - List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId, - /*shortcutIds=*/ null); + public void onShortcutsAddedOrUpdated(@NonNull String packageName, + @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { + mInjector.getBackgroundExecutor().execute(() -> { for (ShortcutInfo shortcut : shortcuts) { if (isPersonShortcut(shortcut)) { addOrUpdateConversationInfo(shortcut); @@ -637,6 +641,30 @@ public class DataManager { } }); } + + @Override + public void onShortcutsRemoved(@NonNull String packageName, + @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { + mInjector.getBackgroundExecutor().execute(() -> { + int uid = Process.INVALID_UID; + try { + uid = mContext.getPackageManager().getPackageUidAsUser( + packageName, user.getIdentifier()); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Package not found: " + packageName, e); + } + PackageData packageData = getPackage(packageName, user.getIdentifier()); + for (ShortcutInfo shortcutInfo : shortcuts) { + if (packageData != null) { + packageData.deleteDataForConversation(shortcutInfo.getId()); + } + if (uid != Process.INVALID_UID) { + mNotificationManagerInternal.onConversationRemoved( + shortcutInfo.getPackage(), uid, shortcutInfo.getId()); + } + } + }); + } } /** Listener for the notifications and their settings changes. */ @@ -789,6 +817,10 @@ public class DataManager { return Executors.newSingleThreadScheduledExecutor(); } + Executor getBackgroundExecutor() { + return BackgroundThread.getExecutor(); + } + ContactsQueryHelper createContactsQueryHelper(Context context) { return new ContactsQueryHelper(context); } diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java index d47e2cc1ba90..35d245fc8d10 100644 --- a/services/people/java/com/android/server/people/data/PackageData.java +++ b/services/people/java/com/android/server/people/data/PackageData.java @@ -59,16 +59,14 @@ public class PackageData { @NonNull Predicate<String> isDefaultDialerPredicate, @NonNull Predicate<String> isDefaultSmsAppPredicate, @NonNull ScheduledExecutorService scheduledExecutorService, - @NonNull File perUserPeopleDataDir, - @NonNull ContactsQueryHelper helper) { + @NonNull File perUserPeopleDataDir) { mPackageName = packageName; mUserId = userId; mPackageDataDir = new File(perUserPeopleDataDir, mPackageName); mPackageDataDir.mkdirs(); - mConversationStore = new ConversationStore(mPackageDataDir, scheduledExecutorService, - helper); + mConversationStore = new ConversationStore(mPackageDataDir, scheduledExecutorService); mEventStore = new EventStore(mPackageDataDir, scheduledExecutorService); mIsDefaultDialerPredicate = isDefaultDialerPredicate; mIsDefaultSmsAppPredicate = isDefaultSmsAppPredicate; @@ -83,8 +81,7 @@ public class PackageData { @NonNull Predicate<String> isDefaultDialerPredicate, @NonNull Predicate<String> isDefaultSmsAppPredicate, @NonNull ScheduledExecutorService scheduledExecutorService, - @NonNull File perUserPeopleDataDir, - @NonNull ContactsQueryHelper helper) { + @NonNull File perUserPeopleDataDir) { Map<String, PackageData> results = new ArrayMap<>(); File[] packageDirs = perUserPeopleDataDir.listFiles(File::isDirectory); if (packageDirs == null) { @@ -93,7 +90,7 @@ public class PackageData { for (File packageDir : packageDirs) { PackageData packageData = new PackageData(packageDir.getName(), userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate, scheduledExecutorService, - perUserPeopleDataDir, helper); + perUserPeopleDataDir); packageData.loadFromDisk(); results.put(packageDir.getName(), packageData); } diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java index d3cecceed884..0f8b91bfa2b1 100644 --- a/services/people/java/com/android/server/people/data/UserData.java +++ b/services/people/java/com/android/server/people/data/UserData.java @@ -37,8 +37,6 @@ class UserData { private final ScheduledExecutorService mScheduledExecutorService; - private final ContactsQueryHelper mHelper; - private boolean mIsUnlocked; private Map<String, PackageData> mPackageDataMap = new ArrayMap<>(); @@ -49,12 +47,10 @@ class UserData { @Nullable private String mDefaultSmsApp; - UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService, - ContactsQueryHelper helper) { + UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService) { mUserId = userId; mPerUserPeopleDataDir = new File(Environment.getDataSystemCeDirectory(mUserId), "people"); mScheduledExecutorService = scheduledExecutorService; - mHelper = helper; } @UserIdInt int getUserId() { @@ -74,7 +70,7 @@ class UserData { // data from disk. mPerUserPeopleDataDir.mkdirs(); mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer, - this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir, mHelper)); + this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir)); } void setUserStopped() { @@ -131,7 +127,7 @@ class UserData { private PackageData createPackageData(String packageName) { return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp, - mScheduledExecutorService, mPerUserPeopleDataDir, mHelper); + mScheduledExecutorService, mPerUserPeopleDataDir); } private boolean isDefaultDialer(String packageName) { diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 62ff3a1c2126..ec56e1ebc8e0 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -96,7 +96,6 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import android.util.FeatureFlagUtils; import android.util.Pair; import com.android.internal.backup.IBackupTransport; @@ -259,9 +258,6 @@ public class KeyValueBackupTaskTest { public void tearDown() throws Exception { ShadowBackupDataInput.reset(); ShadowApplicationPackageManager.reset(); - // False by default. - FeatureFlagUtils.setEnabled( - mContext, FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS, false); } @Test @@ -2348,9 +2344,6 @@ public class KeyValueBackupTaskTest { @Test public void testRunTask_whenNoDataToBackupOnFirstBackup_doesNotTellTransportOfBackup() throws Exception { - FeatureFlagUtils.setEnabled( - mContext, FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS, true); - TransportMock transportMock = setUpInitializedTransport(mTransport); mBackupManagerService.setCurrentToken(0L); when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L); @@ -2368,9 +2361,6 @@ public class KeyValueBackupTaskTest { @Test public void testRunTask_whenBackupHasCompletedAndThenNoDataChanges_transportGetsNotified() throws Exception { - FeatureFlagUtils.setEnabled( - mContext, FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS, true); - TransportMock transportMock = setUpInitializedTransport(mTransport); when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L); when(transportMock.transport.isAppEligibleForBackup( diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index 1c8b00f303d9..30bb38a075be 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -111,6 +111,29 @@ public class AuthServiceTest { any()); } + @Test + public void testRegisterAuthenticator_callsInitConfiguredStrength() throws Exception { + + final String[] config = { + "0:2:15", // ID0:Fingerprint:Strong + "1:4:255", // ID1:Iris:Weak + "2:8:4095", // ID2:Face:Convenience + }; + + when(mInjector.getConfiguration(any())).thenReturn(config); + + mAuthService = new AuthService(mContext, mInjector); + mAuthService.onStart(); + + final int fingerprintStrength = 15; + final int irisStrength = 255; + final int faceStrength = 4095; + + verify(mFingerprintService).initConfiguredStrength(eq(fingerprintStrength)); + verify(mIrisService).initConfiguredStrength(eq(irisStrength)); + verify(mFaceService).initConfiguredStrength(eq(faceStrength)); + } + // TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index dbf2f14146fb..ac818ea8385f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3952,13 +3952,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } public void testIsOrganizationOwnedDevice() throws Exception { - setupProfileOwner(); // Set up the user manager to return correct user info - UserInfo managedProfileUserInfo = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, - "managed profile", - UserInfo.FLAG_MANAGED_PROFILE); - when(getServices().userManager.getUsers()) - .thenReturn(Arrays.asList(managedProfileUserInfo)); + addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1); // Any caller should be able to call this method. assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile()); @@ -5909,8 +5904,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void configureProfileOwnerOfOrgOwnedDevice(ComponentName who, int userId) { - when(getServices().userManager.getProfileParent(eq(UserHandle.of(userId)))) - .thenReturn(UserHandle.SYSTEM); final long ident = mServiceContext.binder.clearCallingIdentity(); mServiceContext.binder.callingUid = UserHandle.getUid(userId, DpmMockContext.SYSTEM_UID); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 37d40811571f..01f1a3e92f2c 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -265,6 +265,9 @@ public class MockSystemServices { .toArray(); } ); + when(userManagerInternal.getUserInfos()).thenReturn( + mUserInfos.toArray(new UserInfo[mUserInfos.size()])); + when(accountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]); // Create a data directory. diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java index 0f0521298641..8dcf21f9fe77 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java @@ -1,31 +1,43 @@ package com.android.server.devicepolicy; import static android.app.admin.SecurityLog.TAG_ADB_SHELL_CMD; +import static android.app.admin.SecurityLog.TAG_APP_PROCESS_START; +import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED; +import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED; +import static android.app.admin.SecurityLog.TAG_KEY_DESTRUCTION; +import static android.app.admin.SecurityLog.TAG_KEY_GENERATED; +import static android.app.admin.SecurityLog.TAG_KEY_IMPORT; +import static android.app.admin.SecurityLog.TAG_KEY_INTEGRITY_VIOLATION; +import static android.app.admin.SecurityLog.TAG_MEDIA_MOUNT; +import static android.app.admin.SecurityLog.TAG_MEDIA_UNMOUNT; import android.app.admin.SecurityLog.SecurityEvent; import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; +import android.os.UserHandle; +import android.text.TextUtils; import android.util.EventLog; +import android.util.EventLog.Event; + +import junit.framework.AssertionFailedError; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -@SmallTest public class SecurityEventTest extends DpmTestBase { - private static long ID = 549; - private static String DATA = "adb shell some_command"; - public void testSecurityEventId() { - SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0); - assertEquals(ID, event.getId()); + public void testSecurityEventId() throws Exception { + SecurityEvent event = createEvent(() -> { + EventLog.writeEvent(TAG_ADB_SHELL_CMD, 0); + }, TAG_ADB_SHELL_CMD); event.setId(20); assertEquals(20, event.getId()); } - public void testSecurityEventParceling() { + public void testSecurityEventParceling() throws Exception { // GIVEN an event. - SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0); + SecurityEvent event = createEvent(() -> { + EventLog.writeEvent(TAG_ADB_SHELL_CMD, "test"); + }, TAG_ADB_SHELL_CMD); // WHEN parceling the event. Parcel p = Parcel.obtain(); p.writeParcelable(event, 0); @@ -39,23 +51,104 @@ public class SecurityEventTest extends DpmTestBase { assertEquals(event.getId(), unparceledEvent.getId()); } - private List<SecurityEvent> buildSecurityEvents(int numEvents, long id) { - // Write an event to the EventLog. - for (int i = 0; i < numEvents; i++) { - EventLog.writeEvent(TAG_ADB_SHELL_CMD, DATA + "_" + i); - } - List<EventLog.Event> events = new ArrayList<>(); - try { - EventLog.readEvents(new int[]{TAG_ADB_SHELL_CMD}, events); - } catch (IOException e) { - fail("Reading a test event from storage failed: " + e); - } - assertTrue("Unexpected number of events read from the log.", events.size() >= numEvents); - // Read events generated by test, from the end of the log. - List<SecurityEvent> securityEvents = new ArrayList<>(); - for (int i = events.size() - numEvents; i < events.size(); i++) { - securityEvents.add(new SecurityEvent(id++, events.get(i).getBytes())); + public void testSecurityEventRedaction() throws Exception { + SecurityEvent event; + + // TAG_ADB_SHELL_CMD will has the command redacted + event = createEvent(() -> { + EventLog.writeEvent(TAG_ADB_SHELL_CMD, "command"); + }, TAG_ADB_SHELL_CMD); + assertFalse(TextUtils.isEmpty((String) event.getData())); + + // TAG_MEDIA_MOUNT will have the volume label redacted (second data) + event = createEvent(() -> { + EventLog.writeEvent(TAG_MEDIA_MOUNT, new Object[] {"path", "label"}); + }, TAG_MEDIA_MOUNT); + assertFalse(TextUtils.isEmpty(event.getStringData(1))); + assertTrue(TextUtils.isEmpty(event.redact(0).getStringData(1))); + + // TAG_MEDIA_UNMOUNT will have the volume label redacted (second data) + event = createEvent(() -> { + EventLog.writeEvent(TAG_MEDIA_UNMOUNT, new Object[] {"path", "label"}); + }, TAG_MEDIA_UNMOUNT); + assertFalse(TextUtils.isEmpty(event.getStringData(1))); + assertTrue(TextUtils.isEmpty(event.redact(0).getStringData(1))); + + // TAG_APP_PROCESS_START will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_APP_PROCESS_START, new Object[] {"process", 12345L, + UserHandle.getUid(10, 123), 456, "seinfo", "hash"}); + }, TAG_APP_PROCESS_START); + assertNotNull(event.redact(10)); + assertNull(event.redact(11)); + + // TAG_CERT_AUTHORITY_INSTALLED will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_CERT_AUTHORITY_INSTALLED, new Object[] {1, "subject", 10}); + }, TAG_CERT_AUTHORITY_INSTALLED); + assertNotNull(event.redact(10)); + assertNull(event.redact(11)); + + // TAG_CERT_AUTHORITY_REMOVED will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_CERT_AUTHORITY_REMOVED, new Object[] {1, "subject", 20}); + }, TAG_CERT_AUTHORITY_REMOVED); + assertNotNull(event.redact(20)); + assertNull(event.redact(0)); + + // TAG_KEY_GENERATED will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_KEY_GENERATED, + new Object[] {1, "alias", UserHandle.getUid(0, 123)}); + }, TAG_KEY_GENERATED); + assertNotNull(event.redact(0)); + assertNull(event.redact(10)); + + // TAG_KEY_IMPORT will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_KEY_IMPORT, + new Object[] {1, "alias", UserHandle.getUid(1, 123)}); + }, TAG_KEY_IMPORT); + assertNotNull(event.redact(1)); + assertNull(event.redact(10)); + + // TAG_KEY_DESTRUCTION will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_KEY_DESTRUCTION, + new Object[] {1, "alias", UserHandle.getUid(2, 123)}); + }, TAG_KEY_DESTRUCTION); + assertNotNull(event.redact(2)); + assertNull(event.redact(10)); + + // TAG_KEY_INTEGRITY_VIOLATION will be fully redacted if user does not match + event = createEvent(() -> { + EventLog.writeEvent(TAG_KEY_INTEGRITY_VIOLATION, + new Object[] {"alias", UserHandle.getUid(2, 123)}); + }, TAG_KEY_INTEGRITY_VIOLATION); + assertNotNull(event.redact(2)); + assertNull(event.redact(10)); + + } + + /** + * Creates an Event object. Only the native code has the serialization and deserialization logic + * so need to actually emit a real log in order to generate the object. + */ + private SecurityEvent createEvent(Runnable generator, int expectedTag) throws Exception { + Long markerData = System.currentTimeMillis(); + EventLog.writeEvent(expectedTag, markerData); + generator.run(); + + List<Event> events = new ArrayList<>(); + // Give the message some time to show up in the log + Thread.sleep(20); + EventLog.readEvents(new int[] {expectedTag}, events); + + for (int i = 0; i < events.size() - 1; i++) { + if (markerData.equals(events.get(i).getData())) { + return new SecurityEvent(0, events.get(i + 1).getBytes()); + } } - return securityEvents; + throw new AssertionFailedError("Unable to locate marker event"); } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 25d077823a3f..feae1e173f52 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -29,6 +29,12 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; +import com.android.server.display.DisplayModeDirector.RefreshRateRange; +import com.android.server.display.DisplayModeDirector.Vote; + +import com.google.common.truth.Truth; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,6 +43,9 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class DisplayModeDirectorTest { + // The tolerance within which we consider something approximately equals. + private static final float FLOAT_TOLERANCE = 0.01f; + private Context mContext; @Before @@ -56,30 +65,22 @@ public class DisplayModeDirectorTest { modes[i - minFps] = new Display.Mode( /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i); } - SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>(); + SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); supportedModesByDisplay.put(displayId, modes); director.injectSupportedModesByDisplay(supportedModesByDisplay); - SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>(); + SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>(); defaultModesByDisplay.put(displayId, modes[0]); director.injectDefaultModeByDisplay(defaultModesByDisplay); return director; } - private int[] intRange(int min, int max) { - int[] range = new int[max - min + 1]; - for (int i = min; i <= max; i++) { - range[i - min] = i; - } - return range; - } - @Test public void testDisplayModeVoting() { int displayId = 0; // With no votes present, DisplayModeDirector should allow any refresh rate. - assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/60, - new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)), + assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/60, + new RefreshRateRange(0f, Float.POSITIVE_INFINITY)), createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs( displayId)); @@ -93,20 +94,16 @@ public class DisplayModeDirectorTest { int maxFps = 90; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); assertTrue(2 * numPriorities < maxFps - minFps + 1); - SparseArray<DisplayModeDirector.Vote> votes = - new SparseArray<DisplayModeDirector.Vote>(); - SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay = - new SparseArray<SparseArray<DisplayModeDirector.Vote>>(); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); for (int i = 0; i < numPriorities; i++) { - int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i; - votes.put( - priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i)); + int priority = Vote.MIN_PRIORITY + i; + votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i)); director.injectVotesByDisplay(votesByDisplay); - assertEquals( - new DisplayModeDirector.DesiredDisplayModeSpecs( + assertEquals(new DesiredDisplayModeSpecs( /*baseModeId=*/minFps + i, - new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)), + new RefreshRateRange(minFps + i, maxFps - i)), director.getDesiredDisplayModeSpecs(displayId)); } } @@ -116,19 +113,35 @@ public class DisplayModeDirectorTest { { assertTrue(numPriorities >= 2); DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); - SparseArray<DisplayModeDirector.Vote> votes = - new SparseArray<DisplayModeDirector.Vote>(); - SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay = - new SparseArray<SparseArray<DisplayModeDirector.Vote>>(); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); - votes.put(DisplayModeDirector.Vote.MAX_PRIORITY, - DisplayModeDirector.Vote.forRefreshRates(65, 85)); - votes.put(DisplayModeDirector.Vote.MIN_PRIORITY, - DisplayModeDirector.Vote.forRefreshRates(70, 80)); + votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85)); + votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80)); director.injectVotesByDisplay(votesByDisplay); - assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/70, - new DisplayModeDirector.RefreshRateRange(70, 80)), + assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/70, + new RefreshRateRange(70, 80)), director.getDesiredDisplayModeSpecs(displayId)); } } + + @Test + public void testVotingWithFloatingPointErrors() { + int displayId = 0; + DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(displayId, votes); + float error = FLOAT_TOLERANCE / 4; + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error)); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, + Vote.forRefreshRates(60 - error, 60 - error)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + + Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + } } diff --git a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java index 6fafe113d90d..9b076e8edb52 100644 --- a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java @@ -118,16 +118,6 @@ public class MockableLocationProviderTest { } @Test - public void testRequestSetAllowed() { - mProvider.requestSetAllowed(true); - verify(mRealProvider, times(1)).onRequestSetAllowed(true); - - mProvider.setMockProvider(mMockProvider); - mProvider.requestSetAllowed(true); - verify(mMockProvider, times(1)).onRequestSetAllowed(true); - } - - @Test public void testSendExtraCommand() { mProvider.sendExtraCommand(0, 0, "command", null); verify(mRealProvider, times(1)).onExtraCommand(0, 0, "command", null); diff --git a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java index 762080fe5745..5943f67494f8 100644 --- a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java +++ b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java @@ -38,8 +38,5 @@ public class FakeProvider extends AbstractLocationProvider { protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} @Override - protected void onRequestSetAllowed(boolean allowed) {} - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {} } diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java index 03b5e38cadb7..d138700f1d2a 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import android.annotation.Nullable; import android.content.Context; import android.content.LocusId; import android.content.pm.ShortcutInfo; @@ -63,7 +62,6 @@ public final class ConversationStoreTest { private static final String PHONE_NUMBER_3 = "+9234567890"; private MockScheduledExecutorService mMockScheduledExecutorService; - private TestContactQueryHelper mTestContactQueryHelper; private ConversationStore mConversationStore; private File mFile; @@ -71,7 +69,6 @@ public final class ConversationStoreTest { public void setUp() { Context ctx = InstrumentationRegistry.getContext(); mFile = new File(ctx.getCacheDir(), "testdir"); - mTestContactQueryHelper = new TestContactQueryHelper(ctx); resetConversationStore(); } @@ -207,9 +204,6 @@ public final class ConversationStoreTest { mConversationStore.deleteConversation(SHORTCUT_ID_3); mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS); - mTestContactQueryHelper.setQueryResult(true, true); - mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2); - resetConversationStore(); ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID); ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2); @@ -240,9 +234,6 @@ public final class ConversationStoreTest { mConversationStore.addOrUpdate(in2); mMockScheduledExecutorService.fastForwardTime(DateUtils.MINUTE_IN_MILLIS); - mTestContactQueryHelper.setQueryResult(true); - mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER); - resetConversationStore(); ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID); ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2); @@ -256,10 +247,6 @@ public final class ConversationStoreTest { mConversationStore.addOrUpdate(in3); mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS); - mTestContactQueryHelper.reset(); - mTestContactQueryHelper.setQueryResult(true, true, true); - mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2, PHONE_NUMBER_3); - resetConversationStore(); out1 = mConversationStore.getConversation(SHORTCUT_ID); out2 = mConversationStore.getConversation(SHORTCUT_ID_2); @@ -290,9 +277,6 @@ public final class ConversationStoreTest { // loadConversationFromDisk gets called each time we call #resetConversationStore(). assertEquals(2, mMockScheduledExecutorService.getExecutes().size()); - mTestContactQueryHelper.setQueryResult(true, true); - mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2); - resetConversationStore(); ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID); ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2); @@ -303,8 +287,7 @@ public final class ConversationStoreTest { private void resetConversationStore() { mFile.mkdir(); mMockScheduledExecutorService = new MockScheduledExecutorService(); - mConversationStore = new ConversationStore(mFile, mMockScheduledExecutorService, - mTestContactQueryHelper); + mConversationStore = new ConversationStore(mFile, mMockScheduledExecutorService); mConversationStore.loadConversationsFromDisk(); } @@ -326,54 +309,4 @@ public final class ConversationStoreTest { .setBubbled(true) .build(); } - - private static class TestContactQueryHelper extends ContactsQueryHelper { - - private int mQueryCalls; - private boolean[] mQueryResult; - - private int mPhoneNumberCalls; - private String[] mPhoneNumberResult; - - TestContactQueryHelper(Context context) { - super(context); - - mQueryCalls = 0; - mPhoneNumberCalls = 0; - } - - private void setQueryResult(boolean... values) { - mQueryResult = values; - } - - private void setPhoneNumberResult(String... values) { - mPhoneNumberResult = values; - } - - private void reset() { - mQueryCalls = 0; - mQueryResult = null; - mPhoneNumberCalls = 0; - mPhoneNumberResult = null; - } - - @Override - boolean query(String contactUri) { - if (mQueryResult != null && mQueryCalls < mQueryResult.length) { - return mQueryResult[mQueryCalls++]; - } - mQueryCalls++; - return false; - } - - @Override - @Nullable - String getPhoneNumber() { - if (mPhoneNumberResult != null && mPhoneNumberCalls < mPhoneNumberResult.length) { - return mPhoneNumberResult[mPhoneNumberCalls++]; - } - mPhoneNumberCalls++; - return null; - } - } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index b54317b768c5..f0b7d206f2bf 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -52,6 +52,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.LauncherApps.ShortcutChangeCallback; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutServiceInternal; @@ -72,6 +74,7 @@ import android.util.Range; import com.android.internal.app.ChooserActivity; import com.android.internal.content.PackageMonitor; import com.android.server.LocalServices; +import com.android.server.notification.NotificationManagerInternal; import com.android.server.pm.parsing.pkg.AndroidPackage; import org.junit.After; @@ -79,6 +82,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -87,6 +92,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -102,6 +108,7 @@ public final class DataManagerTest { private static final String TEST_PKG_NAME = "pkg"; private static final String TEST_CLASS_NAME = "class"; private static final String TEST_SHORTCUT_ID = "sc"; + private static final int TEST_PKG_UID = 35; private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123"; private static final String PHONE_NUMBER = "+1234567890"; private static final String NOTIFICATION_CHANNEL_ID = "test : sc"; @@ -111,7 +118,9 @@ public final class DataManagerTest { @Mock private ShortcutServiceInternal mShortcutServiceInternal; @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; @Mock private PackageManagerInternal mPackageManagerInternal; + @Mock private NotificationManagerInternal mNotificationManagerInternal; @Mock private UserManager mUserManager; + @Mock private PackageManager mPackageManager; @Mock private TelephonyManager mTelephonyManager; @Mock private TelecomManager mTelecomManager; @Mock private ContentResolver mContentResolver; @@ -121,13 +130,16 @@ public final class DataManagerTest { @Mock private StatusBarNotification mStatusBarNotification; @Mock private Notification mNotification; + @Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor; + private NotificationChannel mNotificationChannel; private DataManager mDataManager; private CancellationSignal mCancellationSignal; + private ShortcutChangeCallback mShortcutChangeCallback; private TestInjector mInjector; @Before - public void setUp() { + public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); addLocalServiceMock(ShortcutServiceInternal.class, mShortcutServiceInternal); @@ -143,8 +155,12 @@ public final class DataManagerTest { return null; }).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt()); + addLocalServiceMock(NotificationManagerInternal.class, mNotificationManagerInternal); + + when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); when(mContext.getPackageName()).thenReturn("android"); + when(mContext.getPackageManager()).thenReturn(mPackageManager); Context originalContext = getInstrumentation().getTargetContext(); when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo()); @@ -175,7 +191,8 @@ public final class DataManagerTest { when(mUserManager.getEnabledProfiles(USER_ID_SECONDARY)) .thenReturn(Collections.singletonList(buildUserInfo(USER_ID_SECONDARY))); - when(mContext.getContentResolver()).thenReturn(mContentResolver); + when(mPackageManager.getPackageUidAsUser(TEST_PKG_NAME, USER_ID_PRIMARY)) + .thenReturn(TEST_PKG_UID); when(mStatusBarNotification.getNotification()).thenReturn(mNotification); when(mStatusBarNotification.getPackageName()).thenReturn(TEST_PKG_NAME); @@ -192,6 +209,10 @@ public final class DataManagerTest { mInjector = new TestInjector(); mDataManager = new DataManager(mContext, mInjector); mDataManager.initialize(); + + verify(mShortcutServiceInternal).addShortcutChangeCallback( + mShortcutChangeCallbackCaptor.capture()); + mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue(); } @After @@ -474,6 +495,43 @@ public final class DataManagerTest { } @Test + public void testShortcutAddedOrUpdated() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME, + Collections.singletonList(shortcut), UserHandle.of(USER_ID_PRIMARY)); + + List<ConversationInfo> conversations = getConversationsInPrimary(); + + assertEquals(1, conversations.size()); + assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId()); + } + + @Test + public void testShortcutDeleted() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc1", + buildPerson()); + ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc2", + buildPerson()); + mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME, + Arrays.asList(shortcut1, shortcut2), UserHandle.of(USER_ID_PRIMARY)); + mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME, + Collections.singletonList(shortcut1), UserHandle.of(USER_ID_PRIMARY)); + + List<ConversationInfo> conversations = getConversationsInPrimary(); + + assertEquals(1, conversations.size()); + assertEquals("sc2", conversations.get(0).getShortcutId()); + + verify(mNotificationManagerInternal) + .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, "sc1"); + } + + @Test public void testCallLogContentObserver() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); mDataManager.onUserUnlocked(USER_ID_SECONDARY); @@ -765,6 +823,11 @@ public final class DataManagerTest { } @Override + Executor getBackgroundExecutor() { + return Runnable::run; + } + + @Override ContactsQueryHelper createContactsQueryHelper(Context context) { return mContactsQueryHelper; } diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java index e52cdf59847c..8191d17aba97 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java @@ -61,7 +61,7 @@ public final class PackageDataTest { testDir.mkdir(); mPackageData = new PackageData( PACKAGE_NAME, USER_ID, pkg -> mIsDefaultDialer, pkg -> mIsDefaultSmsApp, - new MockScheduledExecutorService(), testDir, new ContactsQueryHelper(ctx)); + new MockScheduledExecutorService(), testDir); ConversationInfo conversationInfo = new ConversationInfo.Builder() .setShortcutId(SHORTCUT_ID) .setLocusId(LOCUS_ID) diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index 418067fd14f8..7934d33f907d 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -79,10 +79,9 @@ public final class UsageStatsQueryHelperTest { Context ctx = InstrumentationRegistry.getContext(); File testDir = new File(ctx.getCacheDir(), "testdir"); ScheduledExecutorService scheduledExecutorService = new MockScheduledExecutorService(); - ContactsQueryHelper helper = new ContactsQueryHelper(ctx); mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false, - scheduledExecutorService, testDir, helper); + scheduledExecutorService, testDir); mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder() .setShortcutId(SHORTCUT_ID) .setLocusId(LOCUS_ID_1) @@ -221,9 +220,8 @@ public final class UsageStatsQueryHelperTest { private ConversationInfo mConversationInfo; TestConversationStore(File packageDir, - ScheduledExecutorService scheduledExecutorService, - ContactsQueryHelper helper) { - super(packageDir, scheduledExecutorService, helper); + ScheduledExecutorService scheduledExecutorService) { + super(packageDir, scheduledExecutorService); } @Override @@ -241,12 +239,10 @@ public final class UsageStatsQueryHelperTest { TestPackageData(@NonNull String packageName, @UserIdInt int userId, @NonNull Predicate<String> isDefaultDialerPredicate, @NonNull Predicate<String> isDefaultSmsAppPredicate, - @NonNull ScheduledExecutorService scheduledExecutorService, @NonNull File rootDir, - @NonNull ContactsQueryHelper helper) { + @NonNull ScheduledExecutorService scheduledExecutorService, @NonNull File rootDir) { super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate, - scheduledExecutorService, rootDir, helper); - mConversationStore = new TestConversationStore(rootDir, scheduledExecutorService, - helper); + scheduledExecutorService, rootDir); + mConversationStore = new TestConversationStore(rootDir, scheduledExecutorService); mEventStore = new TestEventStore(rootDir, scheduledExecutorService); } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 118c540a4131..bec37e929a80 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -812,7 +812,7 @@ public final class UserManagerTest { "android", 0, mUserManager.getPrimaryUser().getUserHandle()) .getSystemService(Context.USER_SERVICE); - List<UserHandle> profiles = um.getUserProfiles(false); + List<UserHandle> profiles = um.getAllProfiles(); assertThat(profiles.size()).isEqualTo(2); assertThat(profiles.get(0).equals(userProfile.getUserHandle()) || profiles.get(1).equals(userProfile.getUserHandle())).isTrue(); diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 624cb83f9e19..9e067304fc7f 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -297,9 +297,11 @@ public class ThermalManagerServiceTest { @Test public void testGetCurrentTemperatures() throws RemoteException { assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(false, 0), - mService.mService.getCurrentTemperatures()); - assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN), - mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN)); + Arrays.asList(mService.mService.getCurrentTemperatures())); + assertListEqualsIgnoringOrder( + mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN), + Arrays.asList(mService.mService.getCurrentTemperaturesWithType( + Temperature.TYPE_SKIN))); } @Test @@ -331,21 +333,22 @@ public class ThermalManagerServiceTest { assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1)); assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1)); assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1)); - assertEquals(0, mService.mService.getCurrentTemperatures().size()); - assertEquals(0, - mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size()); + assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperatures()).size()); + assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperaturesWithType( + Temperature.TYPE_SKIN)).size()); assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus()); } @Test public void testGetCurrentCoolingDevices() throws RemoteException { assertListEqualsIgnoringOrder(mFakeHal.getCurrentCoolingDevices(false, 0), - mService.mService.getCurrentCoolingDevices()); + Arrays.asList(mService.mService.getCurrentCoolingDevices())); assertListEqualsIgnoringOrder( mFakeHal.getCurrentCoolingDevices(false, CoolingDevice.TYPE_BATTERY), - mService.mService.getCurrentCoolingDevices()); + Arrays.asList(mService.mService.getCurrentCoolingDevices())); assertListEqualsIgnoringOrder( mFakeHal.getCurrentCoolingDevices(true, CoolingDevice.TYPE_CPU), - mService.mService.getCurrentCoolingDevicesWithType(CoolingDevice.TYPE_CPU)); + Arrays.asList(mService.mService.getCurrentCoolingDevicesWithType( + CoolingDevice.TYPE_CPU))); } } diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 3f0cda3b8e5a..b7199f7cdd5a 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -29,6 +29,7 @@ android_test { libs: [ "android.test.runner", "android.test.base", + "android.test.mock", ], dxflags: ["--multi-dex"], 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 ccce0436ea59..d0283f78338d 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3246,7 +3246,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false, UserHandle.USER_ALL); - verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class)); + verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 7f9732bb350a..58299614efe8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -36,9 +36,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -67,6 +68,7 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.service.notification.ConversationChannelWrapper; +import android.test.mock.MockIContentProvider; import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; @@ -87,6 +89,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -123,7 +126,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Mock NotificationUsageStats mUsageStats; @Mock RankingHandler mHandler; @Mock PackageManager mPm; - @Mock IContentProvider mTestIContentProvider; + @Spy IContentProvider mTestIContentProvider = new MockIContentProvider(); @Mock Context mContext; @Mock ZenModeHelper mMockZenModeHelper; @@ -170,12 +173,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); contentResolver.addProvider(TEST_AUTHORITY, testContentProvider); - when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))) - .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(SOUND_URI); + doReturn(CANONICAL_SOUND_URI) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI)); + doReturn(CANONICAL_SOUND_URI) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(SOUND_URI) + .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); @@ -506,12 +509,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { .appendQueryParameter("title", "Test") .appendQueryParameter("canonical", "1") .build(); - when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(canonicalBasedOnLocal); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(localUri); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal))) - .thenReturn(localUri); + doReturn(canonicalBasedOnLocal) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(localUri) + .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(localUri) + .when(mTestIContentProvider).uncanonicalize(any(), any(), + eq(canonicalBasedOnLocal)); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); @@ -530,10 +534,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception { Thread.sleep(3000); - when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(null); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(null); + doReturn(null) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(null) + .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); @@ -557,7 +561,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception { // Not a local uncanonicalized uri, simulating that it fails to exist locally - when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null); + doReturn(null) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI)); String id = "id"; String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 1dd0b1a6f359..816e8e57acc6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; @@ -96,10 +97,33 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); - mSnoozeHelper.readXml(parser); - assertTrue("Should read the notification time from xml and it should be more than zero", - 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification( - 0, "pkg", "key").doubleValue()); + mSnoozeHelper.readXml(parser, 1); + assertEquals((long) Long.MAX_VALUE, (long) mSnoozeHelper + .getSnoozeTimeForUnpostedNotification(0, "pkg", "key")); + verify(mAm, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any()); + } + + @Test + public void testWriteXML_afterReading_noNPE() + throws XmlPullParserException, IOException { + final String max_time_str = Long.toString(Long.MAX_VALUE); + final String xml_string = "<snoozed-notifications>" + + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + + "pkg=\"pkg\" key=\"key\" time=\"" + max_time_str + "\"/>" + + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>" + + "</snoozed-notifications>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml_string.getBytes())), null); + mSnoozeHelper.readXml(parser, 1); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); } @Test @@ -115,7 +139,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, 1); assertEquals("Should read the notification context from xml and it should be `uri", "uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification( 0, "pkg", "key")); @@ -137,7 +161,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, 1); assertTrue("Should read the notification time from xml and it should be more than zero", 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification( 0, "pkg", r.getKey()).doubleValue()); @@ -161,7 +185,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, 2); int systemUser = UserHandle.SYSTEM.getIdentifier(); assertTrue("Should see a past time returned", System.currentTimeMillis() > mSnoozeHelper.getSnoozeTimeForUnpostedNotification( @@ -195,6 +219,30 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testScheduleRepostsForPersistedNotifications() throws Exception { + final String xml_string = "<snoozed-notifications>" + + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + + "pkg=\"pkg\" key=\"key\" time=\"" + 10 + "\"/>" + + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + + "pkg=\"pkg\" key=\"key2\" time=\"" + 15+ "\"/>" + + "</snoozed-notifications>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml_string.getBytes())), null); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq((long) 10), captor.capture()); + assertEquals("key", captor.getValue().getIntent().getStringExtra(EXTRA_KEY)); + + ArgumentCaptor<PendingIntent> captor2 = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq((long) 15), captor2.capture()); + assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); + } + + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); @@ -414,6 +462,23 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testGetSnoozedGroupNotifications_nonGrouped() throws Exception { + IntArray profileIds = new IntArray(); + profileIds.add(UserHandle.USER_CURRENT); + when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); + NotificationRecord r = getNotificationRecord("pkg", 1, "tag", + UserHandle.CURRENT, "group", true); + NotificationRecord r2 = getNotificationRecord("pkg", 2, "tag", + UserHandle.CURRENT, null, true); + mSnoozeHelper.snooze(r, 1000); + mSnoozeHelper.snooze(r2, 1000); + + assertEquals(1, + mSnoozeHelper.getNotifications("pkg", "group", UserHandle.USER_CURRENT).size()); + // and no NPE + } + + @Test public void testGetSnoozedNotificationByKey() throws Exception { IntArray profileIds = new IntArray(); profileIds.add(UserHandle.USER_CURRENT); diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp new file mode 100644 index 000000000000..f56c2cf76956 --- /dev/null +++ b/services/wifi/Android.bp @@ -0,0 +1,13 @@ +filegroup { + name: "services.wifi-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.wifi", + srcs: [ + ":services.wifi-sources", + ], +} diff --git a/services/wifi/java/com/android/server/wifi/SupplicantManager.java b/services/wifi/java/com/android/server/wifi/SupplicantManager.java new file mode 100644 index 000000000000..5ed1ab3198e2 --- /dev/null +++ b/services/wifi/java/com/android/server/wifi/SupplicantManager.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.annotation.SystemApi; +import android.os.SystemService; + +/** + * Wrapper to start/stop supplicant daemon using init system. + * @hide + */ +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public class SupplicantManager { + private static final String WPA_SUPPLICANT_DAEMON_NAME = "wpa_supplicant"; + + private SupplicantManager() {} + + /** + * Start the wpa_supplicant daemon. + * Note: This uses the init system to start the "wpa_supplicant" service. + */ + public static void start() { + SystemService.start(WPA_SUPPLICANT_DAEMON_NAME); + } + + /** + * Stop the wpa_supplicant daemon. + * Note: This uses the init system to stop the "wpa_supplicant" service. + */ + public static void stop() { + SystemService.stop(WPA_SUPPLICANT_DAEMON_NAME); + } +} diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java index f7ec11c79476..d1d64d39b688 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -156,6 +156,12 @@ public class MockContentProvider extends ContentProvider { } @Override + public void canonicalizeAsync(String callingPkg, String featureId, Uri uri, + RemoteCallback callback) { + MockContentProvider.this.canonicalizeAsync(uri, callback); + } + + @Override public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException { return MockContentProvider.this.uncanonicalize(uri); @@ -292,6 +298,18 @@ public class MockContentProvider extends ContentProvider { /** * @hide */ + @SuppressWarnings("deprecation") + public void canonicalizeAsync(Uri uri, RemoteCallback callback) { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, canonicalize(uri)); + callback.sendResult(bundle); + }); + } + + /** + * @hide + */ public boolean refresh(Uri url, Bundle args) { throw new UnsupportedOperationException("unimplemented mock method call"); } diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java index 1831bcdf9df7..223bcc59039d 100644 --- a/test-mock/src/android/test/mock/MockIContentProvider.java +++ b/test-mock/src/android/test/mock/MockIContentProvider.java @@ -145,12 +145,23 @@ public class MockIContentProvider implements IContentProvider { } @Override - public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) - throws RemoteException { + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override + @SuppressWarnings("deprecation") + public void canonicalizeAsync(String callingPkg, String featureId, Uri uri, + RemoteCallback remoteCallback) { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + canonicalize(callingPkg, featureId, uri)); + remoteCallback.sendResult(bundle); + }); + } + + @Override public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); diff --git a/tests/net/common/java/android/net/NetworkScoreTest.kt b/tests/net/common/java/android/net/NetworkScoreTest.kt index a7e33b3d63b6..a63d58d5a0f6 100644 --- a/tests/net/common/java/android/net/NetworkScoreTest.kt +++ b/tests/net/common/java/android/net/NetworkScoreTest.kt @@ -16,9 +16,7 @@ package android.net -import android.net.NetworkScore.POLICY_DEFAULT_SUBSCRIPTION -import android.net.NetworkScore.POLICY_IGNORE_ON_WIFI -import android.net.NetworkScore.RANGE_MEDIUM +import android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.testutils.assertParcelSane @@ -34,30 +32,26 @@ private const val TEST_SCORE = 80 @RunWith(AndroidJUnit4::class) @SmallTest class NetworkScoreTest { - private fun makeScoreBuilder() = NetworkScore.Builder() - .setLegacyScore(TEST_SCORE) - .addPolicy(POLICY_IGNORE_ON_WIFI) - .addPolicy(POLICY_DEFAULT_SUBSCRIPTION) - .setExiting(false) - .setEndToEndMetrics(NetworkScore.Metrics(145 /* latency */, - 2500 /* downlinkBandwidth */, 1430 /* uplinkBandwidth */)) - .setRange(RANGE_MEDIUM) - .setSignalStrength(400) - @Test fun testParcelNetworkScore() { val defaultCap = NetworkCapabilities() - val legacyBuilder = NetworkScore.Builder().setLegacyScore(TEST_SCORE) - assertEquals(TEST_SCORE, legacyBuilder.build().getLegacyScore()) - assertParcelSane(legacyBuilder.build(), 7) + val builder = NetworkScore.Builder().setLegacyScore(TEST_SCORE) + assertEquals(TEST_SCORE, builder.build().getLegacyScore()) + assertParcelSane(builder.build(), 7) - val builder = makeScoreBuilder() + builder.addPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI) + .addPolicy(NetworkScore.POLICY_DEFAULT_SUBSCRIPTION) + .setLinkLayerMetrics(NetworkScore.Metrics(44 /* latency */, + 380 /* downlinkBandwidth */, BANDWIDTH_UNKNOWN /* uplinkBandwidth */)) + .setEndToEndMetrics(NetworkScore.Metrics(11 /* latency */, + BANDWIDTH_UNKNOWN /* downlinkBandwidth */, 100_000 /* uplinkBandwidth */)) + .setRange(NetworkScore.RANGE_MEDIUM) assertParcelSane(builder.build(), 7) - builder.clearPolicy(POLICY_IGNORE_ON_WIFI) + builder.clearPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI) val ns = builder.build() assertParcelSane(ns, 7) - assertFalse(ns.hasPolicy(POLICY_IGNORE_ON_WIFI)) - assertTrue(ns.hasPolicy(POLICY_DEFAULT_SUBSCRIPTION)) + assertFalse(ns.hasPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI)) + assertTrue(ns.hasPolicy(NetworkScore.POLICY_DEFAULT_SUBSCRIPTION)) val exitingNs = ns.withExiting(true) assertNotEquals(ns.isExiting, exitingNs.isExiting) @@ -79,10 +73,4 @@ class NetworkScoreTest { assertTrue(builder1.build().equals(builder2.build())) assertEquals(builder1.build().hashCode(), builder2.build().hashCode()) } - - @Test - fun testBuilderEquals() { - val ns = makeScoreBuilder().build() - assertEquals(ns, NetworkScore.Builder(ns).build()) - } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 6d4a1b265171..4e75f2a273a9 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -2058,15 +2058,14 @@ public class ConnectivityServiceTest { assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - // Bring up validated wifi. + // Bring up wifi with a score of 70. // Cell is lingered because it would not satisfy any request, even if it validated. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); // Score: 60 + mWiFiNetworkAgent.adjustScore(50); + mWiFiNetworkAgent.connect(false); // Score: 70 callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - // TODO: Investigate sending validated before losing. callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -5850,7 +5849,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - trustedCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId)); reset(mNetworkManagementService); diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt index d2532c2ce3d3..86c91165f61b 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt +++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt @@ -16,18 +16,13 @@ package com.android.server.connectivity -import android.net.ConnectivityManager.TYPE_WIFI -import android.net.LinkProperties -import android.net.Network -import android.net.NetworkAgentConfig -import android.net.NetworkCapabilities -import android.net.NetworkInfo import android.net.NetworkRequest -import android.net.NetworkScore import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import kotlin.test.assertEquals import kotlin.test.assertNull @@ -37,24 +32,9 @@ import kotlin.test.assertNull class NetworkRankerTest { private val ranker = NetworkRanker() - private fun makeNai(satisfy: Boolean, score: Int) = object : NetworkAgentInfo( - null /* messenger */, - null /* asyncChannel*/, - Network(100), - NetworkInfo(TYPE_WIFI, 0 /* subtype */, "" /* typename */, "" /* subtypename */), - LinkProperties(), - NetworkCapabilities(), - NetworkScore.Builder().setLegacyScore(score).build(), - null /* context */, - null /* handler */, - NetworkAgentConfig(), - null /* connectivityService */, - null /* netd */, - null /* dnsResolver */, - null /* networkManagementService */, - 0 /* factorySerialNumber */) { - override fun satisfies(request: NetworkRequest?): Boolean = satisfy - override fun getCurrentScore(): Int = score + private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also { + doReturn(satisfy).`when`(it).satisfies(any()) + doReturn(score).`when`(it).currentScore } @Test diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py index 96619273af8d..6a5b0e1347b2 100755 --- a/tools/hiddenapi/merge_csv.py +++ b/tools/hiddenapi/merge_csv.py @@ -14,26 +14,56 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Merge mutliple CSV files, possibly with different columns, writing to stdout. +Merge multiple CSV files, possibly with different columns. """ +import argparse import csv -import sys +import io -csv_readers = [ - csv.DictReader(open(csv_file, 'r'), delimiter=',', quotechar='|') - for csv_file in sys.argv[1:] -] +from zipfile import ZipFile + +args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.') +args_parser.add_argument('--header', help='Comma separated field names; ' + 'if missing determines the header from input files.') +args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.') +args_parser.add_argument('--output', help='Output file for merged CSV.', + default='-', type=argparse.FileType('w')) +args_parser.add_argument('files', nargs=argparse.REMAINDER) +args = args_parser.parse_args() + + +def dict_reader(input): + return csv.DictReader(input, delimiter=',', quotechar='|') + + +if args.zip_input and len(args.files) > 0: + raise ValueError('Expecting either a single ZIP with CSV files' + ' or a list of CSV files as input; not both.') + +csv_readers = [] +if len(args.files) > 0: + for file in args.files: + csv_readers.append(dict_reader(open(file, 'r'))) +elif args.zip_input: + with ZipFile(args.zip_input) as zip: + for entry in zip.namelist(): + if entry.endswith('.uau'): + csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r')))) -# Build union of all columns from source files: headers = set() -for reader in csv_readers: - headers = headers.union(reader.fieldnames) +if args.header: + fieldnames = args.header.split(',') +else: + # Build union of all columns from source files: + for reader in csv_readers: + headers = headers.union(reader.fieldnames) + fieldnames = sorted(headers) # Concatenate all files to output: -out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, - dialect='unix', fieldnames=sorted(headers)) -out.writeheader() +writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, + dialect='unix', fieldnames=fieldnames) +writer.writeheader() for reader in csv_readers: for row in reader: - out.writerow(row) + writer.writerow(row) diff --git a/wifi/Android.bp b/wifi/Android.bp index 5c9fb4e86bc7..0c6cf1c170f5 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -89,6 +89,7 @@ java_library { ], srcs: [ ":framework-wifi-updatable-sources", + ":framework-wifi-util-lib-aidls", ], // java_api_finder must accompany `srcs` plugins: ["java_api_finder"], diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java index 2bbe7d2aa4ec..a831984f3968 100644 --- a/wifi/java/android/net/wifi/SoftApCapability.java +++ b/wifi/java/android/net/wifi/SoftApCapability.java @@ -16,7 +16,7 @@ package android.net.wifi; -import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -47,7 +47,7 @@ public final class SoftApCapability implements Parcelable { * {@link SoftApInfo#getFrequency} and {@link SoftApInfo#getBandwidth} to get * driver channel selection result. */ - public static final int SOFTAP_FEATURE_ACS_OFFLOAD = 1 << 0; + public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1 << 0; /** * Support for client force disconnect. @@ -59,7 +59,7 @@ public final class SoftApCapability implements Parcelable { * Check feature support before invoking * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} */ - public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1; + public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1; /** @@ -67,18 +67,18 @@ public final class SoftApCapability implements Parcelable { * * flag when {@link config_wifi_softap_sae_supported)} is true. */ - public static final int SOFTAP_FEATURE_WPA3_SAE = 1 << 2; + public static final long SOFTAP_FEATURE_WPA3_SAE = 1 << 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = { + @LongDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = { SOFTAP_FEATURE_ACS_OFFLOAD, SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT, SOFTAP_FEATURE_WPA3_SAE, }) public @interface HotspotFeatures {} - private @HotspotFeatures int mSupportedFeatures = 0; + private @HotspotFeatures long mSupportedFeatures = 0; private int mMaximumSupportedClientNumber; @@ -104,7 +104,7 @@ public final class SoftApCapability implements Parcelable { * * @param feature one of feature from {@link HotspotFeatures} */ - public boolean isFeatureSupported(@HotspotFeatures int feature) { + public boolean isFeatureSupported(@HotspotFeatures long feature) { return (mSupportedFeatures & feature) == feature; } @@ -125,7 +125,7 @@ public final class SoftApCapability implements Parcelable { * @param features One or combination of the feature from {@link @HotspotFeatures}. * @hide */ - public SoftApCapability(@HotspotFeatures int features) { + public SoftApCapability(@HotspotFeatures long features) { mSupportedFeatures = features; } @@ -138,7 +138,7 @@ public final class SoftApCapability implements Parcelable { @Override /** Implement the Parcelable interface */ public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mSupportedFeatures); + dest.writeLong(mSupportedFeatures); dest.writeInt(mMaximumSupportedClientNumber); } @@ -146,7 +146,7 @@ public final class SoftApCapability implements Parcelable { /** Implement the Parcelable interface */ public static final Creator<SoftApCapability> CREATOR = new Creator<SoftApCapability>() { public SoftApCapability createFromParcel(Parcel in) { - int supportedFeatures = in.readInt(); + long supportedFeatures = in.readLong(); SoftApCapability capability = new SoftApCapability(supportedFeatures); capability.mMaximumSupportedClientNumber = in.readInt(); return capability; diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index e1450cb794f6..1a12af341910 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -2167,6 +2167,7 @@ public class WifiConfiguration implements Parcelable { sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID). append(" PROVIDER-NAME: ").append(this.providerFriendlyName). append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN) + .append(" HOME-PROVIDER-NETWORK: ").append(this.isHomeProviderNetwork) .append(" PRIO: ").append(this.priority) .append(" HIDDEN: ").append(this.hiddenSSID) .append(" PMF: ").append(this.requirePmf) diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java index 3215246a9c1f..7d24142ba135 100644 --- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java +++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java @@ -25,7 +25,6 @@ import android.annotation.SystemService; import android.app.AlarmManager; import android.content.Context; import android.net.wifi.SoftApInfo; -import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiScanner; import android.os.Binder; import android.os.Handler; @@ -245,7 +244,7 @@ public class WifiNl80211Manager { * indication that the SoftAp is not enabled. * @param bandwidth The new bandwidth of the SoftAp. */ - void onSoftApChannelSwitched(int frequencyMhz, @WifiAnnotations.Bandwidth int bandwidth); + void onSoftApChannelSwitched(int frequencyMhz, int bandwidth); } /** @@ -383,7 +382,7 @@ public class WifiNl80211Manager { toFrameworkBandwidth(bandwidth))); } - private @WifiAnnotations.Bandwidth int toFrameworkBandwidth(int bandwidth) { + private int toFrameworkBandwidth(int bandwidth) { switch(bandwidth) { case IApInterfaceEventCallback.BANDWIDTH_INVALID: return SoftApInfo.CHANNEL_WIDTH_INVALID; @@ -856,7 +855,7 @@ public class WifiNl80211Manager { /** * Return scan type for the parcelable {@link SingleScanSettings} */ - private static int getScanType(@WifiAnnotations.ScanType int scanType) { + private static int getScanType(int scanType) { switch (scanType) { case WifiScanner.SCAN_TYPE_LOW_LATENCY: return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN; @@ -891,7 +890,7 @@ public class WifiNl80211Manager { * @return Returns true on success, false on failure (e.g. when called before the interface * has been set up). */ - public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, + public boolean startScan(@NonNull String ifaceName, int scanType, @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { @@ -1047,7 +1046,7 @@ public class WifiNl80211Manager { * @return frequencies vector of valid frequencies (MHz), or an empty array for error. * @throws IllegalArgumentException if band is not recognized. */ - public @NonNull int[] getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) { + public @NonNull int[] getChannelsMhzForBand(int band) { if (mWificond == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return new int[0]; @@ -1226,7 +1225,7 @@ public class WifiNl80211Manager { */ public static class OemSecurityType { /** The protocol defined in {@link android.net.wifi.WifiAnnotations.Protocol}. */ - public final @WifiAnnotations.Protocol int protocol; + public final int protocol; /** * Supported key management types defined * in {@link android.net.wifi.WifiAnnotations.KeyMgmt}. @@ -1238,7 +1237,7 @@ public class WifiNl80211Manager { */ @NonNull public final List<Integer> pairwiseCipher; /** The group cipher type defined in {@link android.net.wifi.WifiAnnotations.Cipher}. */ - public final @WifiAnnotations.Cipher int groupCipher; + public final int groupCipher; /** * Default constructor for OemSecurityType * @@ -1252,10 +1251,10 @@ public class WifiNl80211Manager { * in {@link android.net.wifi.WifiAnnotations.Cipher}. */ public OemSecurityType( - @WifiAnnotations.Protocol int protocol, + int protocol, @NonNull List<Integer> keyManagement, @NonNull List<Integer> pairwiseCipher, - @WifiAnnotations.Cipher int groupCipher) { + int groupCipher) { this.protocol = protocol; this.keyManagement = (keyManagement != null) ? keyManagement : new ArrayList<Integer>(); diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java index ef476ebc2667..73b501a4d8f2 100644 --- a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java @@ -35,7 +35,7 @@ public class SoftApCapabilityTest { */ @Test public void testCopyOperator() throws Exception { - int testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT + long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD; SoftApCapability capability = new SoftApCapability(testSoftApFeature); capability.setMaxSupportedClients(10); @@ -51,7 +51,7 @@ public class SoftApCapabilityTest { */ @Test public void testParcelOperation() throws Exception { - int testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT + long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD; SoftApCapability capability = new SoftApCapability(testSoftApFeature); capability.setMaxSupportedClients(10); |