diff options
349 files changed, 7451 insertions, 3281 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 2fd2e33bbc37..9b290c6a4760 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -12,8 +12,6 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp services/incremental/ [Hook Scripts] -checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} - strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT} hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} diff --git a/api/test-current.txt b/api/test-current.txt index 113d585164f6..ab0761374570 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -35,6 +35,7 @@ package android { public static final class R.bool { field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005 field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004 + field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006 } public static final class R.string { @@ -144,6 +145,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void stopSystemLockTaskMode(); method public static boolean supportsMultiWindow(android.content.Context); method public static boolean supportsSplitScreenMultiWindow(android.content.Context); + field public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; // 0x1b8 field public static final int INVALID_STACK_ID = -1; // 0xffffffff field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1 field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0 @@ -5002,7 +5004,6 @@ package android.util { field public static final String PERSIST_PREFIX = "persist.sys.fflag.override."; field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; - field public static final String SETTINGS_FUSE_FLAG = "settings_fuse"; field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 0617eb6c0e66..124f815f51f0 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -216,10 +216,6 @@ cc_binary { // address: true, //}, }, - debuggable: { - // Add a flag to enable stats log printing from statsd on debug builds. - cflags: ["-DVERY_VERBOSE_PRINTING"], - }, }, proto: { diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index e7b32c56551a..05e9ec3a1769 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -409,11 +409,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { onWatchdogRollbackOccurredLocked(event); } -#ifdef VERY_VERBOSE_PRINTING if (mPrintAllLogs) { ALOGI("%s", event->ToString().c_str()); } -#endif resetIfConfigTtlExpiredLocked(eventElapsedTimeNs); // Hard-coded logic to update the isolated uid's in the uid-map. diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 23f2584655b0..c0f54a0995ac 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -139,10 +139,8 @@ public: int64_t getLastReportTimeNs(const ConfigKey& key); inline void setPrintLogs(bool enabled) { -#ifdef VERY_VERBOSE_PRINTING std::lock_guard<std::mutex> lock(mMetricsMutex); mPrintAllLogs = enabled; -#endif } // Add a specific config key to the possible configs to dump ASAP. @@ -276,9 +274,7 @@ private: //Last time we wrote metadata to disk. int64_t mLastMetadataWriteNs = 0; -#ifdef VERY_VERBOSE_PRINTING bool mPrintAllLogs = false; -#endif FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 322648229d0e..d5e331495164 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -484,7 +484,8 @@ void StatsService::print_cmd_help(int out) { dprintf(out, " Clear cached puller data.\n"); dprintf(out, "\n"); dprintf(out, "usage: adb shell cmd stats print-logs\n"); - dprintf(out, " Only works on eng build\n"); + dprintf(out, " Requires root privileges.\n"); + dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n"); } status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { @@ -865,18 +866,19 @@ status_t StatsService::cmd_clear_puller_cache(int out) { } status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { - VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(), - AIBinder_getCallingUid()); - if (checkPermission(kPermissionDump)) { - bool enabled = true; - if (args.size() >= 2) { - enabled = atoi(args[1].c_str()) != 0; - } - mProcessor->setPrintLogs(enabled); - return NO_ERROR; - } else { + Status status = checkUid(AID_ROOT); + if (!status.isOk()) { return PERMISSION_DENIED; } + + VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(), + AIBinder_getCallingUid()); + bool enabled = true; + if (args.size() >= 2) { + enabled = atoi(args[1].c_str()) != 0; + } + mProcessor->setPrintLogs(enabled); + return NO_ERROR; } bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 8c54c242e04a..02c0d933b3cd 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -4590,7 +4590,7 @@ message GeneralExternalStorageAccessStats { // Includes file path and ContentResolver accesses optional uint32 secondary_storage_accesses = 4; // Comma-separated list of mime types that were accessed. - optional MimeTypes mime_types_accessed = 5; + optional MimeTypes mime_types_accessed = 5 [(log_mode) = MODE_BYTES]; } /** @@ -6174,7 +6174,7 @@ message ProcessStatsAvailablePagesProto { * Pulled from ProcessStatsService.java */ message ProcStats { - optional ProcessStatsSectionProto proc_stats_section = 1; + optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES]; // Data pulled from device into this is sometimes sharded across multiple atoms to work around // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer // with the number of this shard. @@ -6185,7 +6185,7 @@ message ProcStats { * Pulled from ProcessStatsService.java */ message ProcStatsPkgProc { - optional ProcessStatsSectionProto proc_stats_section = 1; + optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES]; } // Next Tag: 2 @@ -6203,7 +6203,7 @@ message NotificationRemoteViewsProto { * Pulled from NotificationManagerService.java */ message NotificationRemoteViews { - optional NotificationRemoteViewsProto notification_remote_views = 1; + optional NotificationRemoteViewsProto notification_remote_views = 1 [(log_mode) = MODE_BYTES]; } /** @@ -6271,7 +6271,7 @@ message DNDModeProto { // May also be "MANUAL_RULE" to indicate app-activation of the manual rule. optional string id = 5; optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other - optional DNDPolicyProto policy = 7; + optional DNDPolicyProto policy = 7 [(log_mode) = MODE_BYTES]; } /** @@ -6436,7 +6436,7 @@ message PowerProfileProto { * Pulled from PowerProfile.java */ message PowerProfile { - optional PowerProfileProto power_profile = 1; + optional PowerProfileProto power_profile = 1 [(log_mode) = MODE_BYTES]; } /** @@ -8131,7 +8131,7 @@ message DeviceIdentifierAccessDenied { message TrainInfo { optional int64 train_version_code = 1; - optional TrainExperimentIds train_experiment_id = 2; + optional TrainExperimentIds train_experiment_id = 2 [(log_mode) = MODE_BYTES]; optional string train_name = 3; @@ -11179,8 +11179,8 @@ message BlobInfo { optional int64 expiry_timestamp_millis = 3; // List of committers of this Blob - optional BlobCommitterListProto committers = 4; + optional BlobCommitterListProto committers = 4 [(log_mode) = MODE_BYTES]; // List of leasees of this Blob - optional BlobLeaseeListProto leasees = 5; + optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES]; } diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 0f31529451fb..4283d7ad2a62 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -31,6 +31,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.DisplayMetrics; import android.util.Singleton; import java.util.List; @@ -139,6 +140,8 @@ public class ActivityTaskManager { public static final String EXTRA_IGNORE_TARGET_SECURITY = "android.app.extra.EXTRA_IGNORE_TARGET_SECURITY"; + /** The minimal size of a display's long-edge needed to support split-screen multi-window. */ + public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; private static int sMaxRecentTasks = -1; @@ -282,8 +285,23 @@ public class ActivityTaskManager { com.android.internal.R.bool.config_supportsMultiWindow); } - /** Returns true if the system supports split screen multi-window. */ + /** + * Returns {@code true} if the display the context is associated with supports split screen + * multi-window. + * + * @throws UnsupportedOperationException if the supplied {@link Context} is not associated with + * a display. + */ public static boolean supportsSplitScreenMultiWindow(Context context) { + DisplayMetrics dm = new DisplayMetrics(); + context.getDisplay().getRealMetrics(dm); + + int widthDp = (int) (dm.widthPixels / dm.density); + int heightDp = (int) (dm.heightPixels / dm.density); + if (Math.max(widthDp, heightDp) < DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP) { + return false; + } + return supportsMultiWindow(context) && Resources.getSystem().getBoolean( com.android.internal.R.bool.config_supportsSplitScreenMultiWindow); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 89e1f5a28a35..9b13d256aea6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -86,6 +86,8 @@ import android.graphics.Canvas; import android.graphics.HardwareRenderer; import android.hardware.display.DisplayManagerGlobal; import android.inputmethodservice.InputMethodService; +import android.media.MediaFrameworkInitializer; +import android.media.MediaServiceManager; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.Proxy; @@ -269,9 +271,6 @@ public final class ActivityThread extends ClientTransactionHandler { /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */ public static final int SERVICE_DONE_EXECUTING_STOP = 2; - // Whether to invoke an activity callback after delivering new configuration. - private static final boolean REPORT_TO_ACTIVITY = true; - /** Use foreground GC policy (less pause time) and higher JIT weight. */ private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0; /** Use background GC policy and default JIT threshold. */ @@ -4971,7 +4970,8 @@ public final class ActivityThread extends ClientTransactionHandler { private void relaunchAllActivities(boolean preserveWindows) { for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { final ActivityClientRecord r = entry.getValue(); - if (!r.activity.mFinished) { + // Schedule relaunch the activity if it is not a local object or finishing. + if (!r.activity.mFinished && !(r.token instanceof Binder)) { if (preserveWindows && r.window != null) { r.mPreserveWindow = true; } @@ -5537,18 +5537,14 @@ public final class ActivityThread extends ClientTransactionHandler { } /** - * Updates the configuration for an Activity. The ActivityClientRecord's - * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for - * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering - * the updated Configuration. - * @param r ActivityClientRecord representing the Activity. - * @param newBaseConfig The new configuration to use. This may be augmented with - * {@link ActivityClientRecord#overrideConfig}. + * Updates the configuration for an Activity in its current display. + * + * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int, + * boolean) */ private void performConfigurationChangedForActivity(ActivityClientRecord r, Configuration newBaseConfig) { - performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId(), - false /* movedToDifferentDisplay */); + performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId()); } /** @@ -5560,17 +5556,16 @@ public final class ActivityThread extends ClientTransactionHandler { * @param newBaseConfig The new configuration to use. This may be augmented with * {@link ActivityClientRecord#overrideConfig}. * @param displayId The id of the display where the Activity currently resides. - * @param movedToDifferentDisplay Indicates if the activity was moved to different display. * @return {@link Configuration} instance sent to client, null if not sent. */ private Configuration performConfigurationChangedForActivity(ActivityClientRecord r, - Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) { + Configuration newBaseConfig, int displayId) { r.tmpConfig.setTo(newBaseConfig); if (r.overrideConfig != null) { r.tmpConfig.updateFrom(r.overrideConfig); } final Configuration reportedConfig = performActivityConfigurationChanged(r.activity, - r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay); + r.tmpConfig, r.overrideConfig, displayId); freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig)); return reportedConfig; } @@ -5597,10 +5592,6 @@ public final class ActivityThread extends ClientTransactionHandler { * @param newConfig The new configuration. */ private void performConfigurationChanged(ComponentCallbacks2 cb, Configuration newConfig) { - if (!REPORT_TO_ACTIVITY) { - return; - } - // ContextThemeWrappers may override the configuration for that context. We must check and // apply any overrides defined. Configuration contextThemeWrapperOverrideConfig = null; @@ -5625,12 +5616,10 @@ public final class ActivityThread extends ClientTransactionHandler { * from the base global configuration. This is supplied by * ActivityManager. * @param displayId Id of the display where activity currently resides. - * @param movedToDifferentDisplay Indicates if the activity was moved to different display. * @return Configuration sent to client, null if no changes and not moved to different display. */ private Configuration performActivityConfigurationChanged(Activity activity, - Configuration newConfig, Configuration amOverrideConfig, int displayId, - boolean movedToDifferentDisplay) { + Configuration newConfig, Configuration amOverrideConfig, int displayId) { if (activity == null) { throw new IllegalArgumentException("No activity provided."); } @@ -5643,6 +5632,7 @@ public final class ActivityThread extends ClientTransactionHandler { // callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition handleWindowingModeChangeIfNeeded(activity, newConfig); + final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId); boolean shouldReportChange = false; if (activity.mCurrentConfig == null) { shouldReportChange = true; @@ -5656,8 +5646,7 @@ public final class ActivityThread extends ClientTransactionHandler { amOverrideConfig)) { // Nothing significant, don't proceed with updating and reporting. return null; - } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0 - || !REPORT_TO_ACTIVITY) { + } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) { // If this activity doesn't handle any of the config changes, then don't bother // calling onConfigurationChanged. Otherwise, report to the activity for the // changes. @@ -5691,11 +5680,6 @@ public final class ActivityThread extends ClientTransactionHandler { final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig, contextThemeWrapperOverrideConfig); - if (!REPORT_TO_ACTIVITY) { - // Not configured to report to activity. - return configToReport; - } - if (movedToDifferentDisplay) { activity.dispatchMovedToDisplay(displayId, configToReport); } @@ -5988,8 +5972,6 @@ public final class ActivityThread extends ClientTransactionHandler { if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r); return; } - final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY - && displayId != r.activity.getDisplayId(); synchronized (r) { if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) { @@ -6003,6 +5985,7 @@ public final class ActivityThread extends ClientTransactionHandler { r.mPendingOverrideConfig = null; } + final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId); if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig) && !movedToDifferentDisplay) { if (DEBUG_CONFIGURATION) { @@ -6018,29 +6001,34 @@ public final class ActivityThread extends ClientTransactionHandler { final ViewRootImpl viewRoot = r.activity.mDecor != null ? r.activity.mDecor.getViewRootImpl() : null; - if (movedToDifferentDisplay) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:" - + r.activityInfo.name + ", displayId=" + displayId + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Handle activity config changed, activity:" + + r.activityInfo.name + ", displayId=" + r.activity.getDisplayId() + + (movedToDifferentDisplay ? (", newDisplayId=" + displayId) : "") + ", config=" + overrideConfig); - - final Configuration reportedConfig = performConfigurationChangedForActivity(r, - mCompatConfiguration, displayId, true /* movedToDifferentDisplay */); - if (viewRoot != null) { - viewRoot.onMovedToDisplay(displayId, reportedConfig); - } - } else { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: " - + r.activityInfo.name + ", config=" + overrideConfig); - performConfigurationChangedForActivity(r, mCompatConfiguration); } + final Configuration reportedConfig = performConfigurationChangedForActivity(r, + mCompatConfiguration, + movedToDifferentDisplay ? displayId : r.activity.getDisplayId()); // Notify the ViewRootImpl instance about configuration changes. It may have initiated this // update to make sure that resources are updated before updating itself. if (viewRoot != null) { + if (movedToDifferentDisplay) { + viewRoot.onMovedToDisplay(displayId, reportedConfig); + } viewRoot.updateConfiguration(displayId); } mSomeActivitiesChanged = true; } + /** + * Checks if the display id of activity is different from the given one. Note that + * {@link #INVALID_DISPLAY} means no difference. + */ + private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) { + return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId(); + } + final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { if (start) { try { @@ -7665,6 +7653,7 @@ public final class ActivityThread extends ClientTransactionHandler { public static void initializeMainlineModules() { TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager()); StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager()); + MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager()); } private void purgePendingResources() { diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index cfe0aff05d4a..e7b3e14bfda7 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -502,7 +502,7 @@ public final class ApplicationExitInfo implements Parcelable { * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and * {@link #getPackageUid}, if an external service has the * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set - * to <code>true<code> and was bound with the flag + * to <code>true</code> and was bound with the flag * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will * be the kernel user identifier of the external service provider. */ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9849e590c06b..9613e58fb943 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1900,26 +1900,31 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { - // We may override this API from outer context. - final boolean isUiContext = isUiContext() || getOuterContext().isUiContext(); - // Check incorrect Context usage. - if (isUiComponent(name) && !isUiContext && vmIncorrectContextUseEnabled()) { - final String errorMessage = "Tried to access visual service " - + SystemServiceRegistry.getSystemServiceClassName(name) - + " from a non-visual Context:" + getOuterContext(); - final String message = "Visual services, such as WindowManager, WallpaperService or " - + "LayoutInflater should be accessed from Activity or other visual Context. " - + "Use an Activity or a Context created with " - + "Context#createWindowContext(int, Bundle), which are adjusted to the " - + "configuration and visual bounds of an area on screen."; - final Exception exception = new IllegalAccessException(errorMessage); - StrictMode.onIncorrectContextUsed(message, exception); - Log.e(TAG, errorMessage + message, exception); + if (vmIncorrectContextUseEnabled()) { + // We may override this API from outer context. + final boolean isUiContext = isUiContext() || isOuterUiContext(); + // Check incorrect Context usage. + if (isUiComponent(name) && !isUiContext) { + final String errorMessage = "Tried to access visual service " + + SystemServiceRegistry.getSystemServiceClassName(name) + + " from a non-visual Context:" + getOuterContext(); + final String message = "Visual services, such as WindowManager, WallpaperService " + + "or LayoutInflater should be accessed from Activity or other visual " + + "Context. Use an Activity or a Context created with " + + "Context#createWindowContext(int, Bundle), which are adjusted to " + + "the configuration and visual bounds of an area on screen."; + final Exception exception = new IllegalAccessException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + " " + message, exception); + } } - return SystemServiceRegistry.getSystemService(this, name); } + private boolean isOuterUiContext() { + return getOuterContext() != null && getOuterContext().isUiContext(); + } + @Override public String getSystemServiceName(Class<?> serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); @@ -2372,7 +2377,7 @@ class ContextImpl extends Context { context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), mResources.getLoaders())); - context.mIsUiContext = isUiContext() || getOuterContext().isUiContext(); + context.mIsUiContext = isUiContext() || isOuterUiContext(); return context; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 3410c92a61c4..f82ab7bcfc08 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4801,7 +4801,6 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.time, View.GONE); contentView.setImageViewIcon(R.id.profile_badge, null); contentView.setViewVisibility(R.id.profile_badge, View.GONE); - contentView.setViewVisibility(R.id.alerted_icon, View.GONE); mN.mUsesStandardHeader = false; } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index c20c11fea1e4..59997ccab687 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -101,11 +101,11 @@ import android.location.ICountryDetector; import android.location.ILocationManager; import android.location.LocationManager; import android.media.AudioManager; +import android.media.MediaFrameworkInitializer; import android.media.MediaRouter; import android.media.midi.IMidiManager; import android.media.midi.MidiManager; import android.media.projection.MediaProjectionManager; -import android.media.session.MediaSessionManager; import android.media.soundtrigger.SoundTriggerManager; import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; @@ -855,13 +855,6 @@ public final class SystemServiceRegistry { return new ConsumerIrManager(ctx); }}); - registerService(Context.MEDIA_SESSION_SERVICE, MediaSessionManager.class, - new CachedServiceFetcher<MediaSessionManager>() { - @Override - public MediaSessionManager createService(ContextImpl ctx) { - return new MediaSessionManager(ctx); - }}); - registerService(Context.TRUST_SERVICE, TrustManager.class, new StaticServiceFetcher<TrustManager>() { @Override @@ -1335,6 +1328,7 @@ public final class SystemServiceRegistry { WifiFrameworkInitializer.registerServiceWrappers(); StatsFrameworkInitializer.registerServiceWrappers(); RollbackManagerFrameworkInitializer.initialize(); + MediaFrameworkInitializer.registerServiceWrappers(); } finally { // If any of the above code throws, we're in a pretty bad shape and the process // will likely crash, but we'll reset it just in case there's an exception handler... diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 786acba94ef4..621ef526aacf 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -29,7 +29,7 @@ import android.content.Context; @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE) public interface TimeZoneDetector { - /** + /** * Returns the current user's time zone capabilities. See {@link TimeZoneCapabilities}. */ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 8c3268c6fc14..d2a774bcb168 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1812,6 +1812,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { + if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); return mService.removeActiveDevice(profiles); } } catch (RemoteException e) { @@ -1856,6 +1857,9 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { + if (DBG) { + Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); + } return mService.setActiveDevice(device, profiles); } } catch (RemoteException e) { diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 565f34a149bd..5fe094d082ae 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1153,6 +1153,9 @@ public class ContextWrapper extends Context { */ @Override public boolean isUiContext() { + if (mBase == null) { + return false; + } return mBase.isUiContext(); } } diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 79da1f6ab282..ee9bd3d259fb 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1168,7 +1168,12 @@ public class IntentFilter implements Parcelable { public int match(Uri data, boolean wildcardSupported) { String host = data.getHost(); if (host == null) { - return NO_MATCH_DATA; + if (wildcardSupported && mWild) { + // special case, if no host is provided, but the Authority is wildcard, match + return MATCH_CATEGORY_HOST; + } else { + return NO_MATCH_DATA; + } } if (false) Log.v("IntentFilter", "Match host " + host + ": " + mHost); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 940bf26621e5..f533760de84d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4673,8 +4673,7 @@ public abstract class PackageManager { * Marks an application exempt from having its permissions be automatically revoked when * the app is unused for an extended period of time. * - * Only the installer on record that installed the given package, or a holder of - * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this. + * Only the installer on record that installed the given package is allowed to call this. * * Packages start in whitelisted state, and it is the installer's responsibility to * un-whitelist the packages it installs, unless auto-revoking permissions from that package diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 431c5d7310f1..3688f1bda979 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1510,7 +1510,7 @@ public class ParsingPackageUtils { Uri data = null; String dataType = null; - String host = IntentFilter.WILDCARD; + String host = null; final int numActions = intentInfo.countActions(); final int numSchemes = intentInfo.countDataSchemes(); final int numTypes = intentInfo.countDataTypes(); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 58cb1e135350..c7fc2ad9b29a 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -610,7 +610,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan serverCallback.sendResult(null /* data */); } } - }); + }, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index f4122fe7c625..2f8c97f8022d 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -90,7 +90,7 @@ interface IFaceService { void resetLockout(int userId, in byte [] hardwareAuthToken); // Add a callback which gets notified when the face lockout period expired. - void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback); + void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName); void setFeature(IBinder token, int userId, int feature, boolean enabled, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName); diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index abe63d65102c..9dacca769229 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -768,7 +768,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing serverCallback.sendResult(null /* data */); } } - }); + }, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 5adba7595ef9..ad58fea4281c 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -94,7 +94,7 @@ interface IFingerprintService { void resetLockout(int userId, in byte [] hardwareAuthToken); // Add a callback which gets notified when the fingerprint lockout period expired. - void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback); + void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName); // Check if a client request is currently being handled boolean isClientActive(); diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java index 509cbe09df69..90d0ff0a5026 100644 --- a/core/java/android/inputmethodservice/InlineSuggestionSession.java +++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java @@ -149,6 +149,11 @@ class InlineSuggestionSession { */ @MainThread void invalidate() { + try { + mCallback.onInlineSuggestionsSessionInvalidated(); + } catch (RemoteException e) { + Log.w(TAG, "onInlineSuggestionsSessionInvalidated() remote exception:" + e); + } if (mResponseCallback != null) { consumeInlineSuggestionsResponse(EMPTY_RESPONSE); mResponseCallback.invalidate(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 5647bf90d2fb..c5a11abe1136 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -605,9 +605,6 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding + " ic=" + mInputConnection); // Unbind input is per process per display. - // TODO(b/150902448): free-up IME surface when target is changing. - // e.g. DisplayContent#setInputMethodTarget() - removeImeSurface(); onUnbindInput(); mInputBinding = null; mInputConnection = null; diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 0e10c42e61db..0eb3c1e8ad01 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -38,7 +38,9 @@ import java.util.Arrays; * Representation of a MAC address. * * This class only supports 48 bits long addresses and does not support 64 bits long addresses. - * Instances of this class are immutable. + * Instances of this class are immutable. This class provides implementations of hashCode() + * and equals() that make it suitable for use as keys in standard implementations of + * {@link java.util.Map}. */ public final class MacAddress implements Parcelable { @@ -122,12 +124,22 @@ public final class MacAddress implements Parcelable { } /** + * Convert this MacAddress to a byte array. + * + * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6, + * the returned array is [1, 2, 3, 4, 5, 6]. + * * @return a byte array representation of this MacAddress. */ public @NonNull byte[] toByteArray() { return byteAddrFromLongAddr(mAddr); } + /** + * Returns a human-readable representation of this MacAddress. + * The exact format is implementation-dependent and should not be assumed to have any + * particular format. + */ @Override public @NonNull String toString() { return stringAddrFromLongAddr(mAddr); diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index ea7462bd9113..a0207c8497c8 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -122,6 +122,14 @@ public class Binder implements IBinder { private static native long getNativeFinalizer(); + /** + * Returns the TID (task ID) for the current thread. Same as {@link Thread#getNativeTid()} + * + * @hide + */ + @CriticalNative + public static native int getNativeTid(); + // Use a Holder to allow static initialization of Binder in the boot image, and // possibly to avoid some initialization ordering issues. private static class NoImagePreloadHolder { diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index 7c42c36e7747..64ab1d711765 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -96,6 +96,15 @@ public abstract class HwBinder implements IHwBinder { throws RemoteException, NoSuchElementException; /** + * This allows getService to bypass the VINTF manifest for testing only. + * + * Disabled on user builds. + * @hide + */ + public static native final void setTrebleTestingOverride( + boolean testingOverride); + + /** * Configures how many threads the process-wide hwbinder threadpool * has to process incoming requests. * diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java index d35ce5b2c3f8..32c80e7069da 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -159,7 +159,7 @@ public class V4Signature { * * @param fileSize - size of the signed file (APK) */ - public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo, + public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo, SigningInfo signingInfo) { final int size = 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 0abf8ae352af..6b5eb16d7bff 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -86,7 +86,6 @@ import android.system.Os; import android.system.OsConstants; import android.text.TextUtils; import android.util.DataUnit; -import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -161,11 +160,6 @@ public class StorageManager { /** {@hide} */ public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; /** {@hide} */ - public static final String PROP_FUSE = "persist.sys.fuse"; - /** {@hide} */ - public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX - + FeatureFlagUtils.SETTINGS_FUSE_FLAG; - /** {@hide} */ public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST = "forced_scoped_storage_whitelist"; diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 4c9afcf5cd05..fae26cf77c16 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3946,8 +3946,7 @@ public final class Telephony { /** * The APN set id. When the user manually selects an APN or the framework sets an APN as - * preferred, all APNs with the same set id as the selected APN should be prioritized over - * APNs in other sets. + * preferred, the device can only use APNs with the same set id as the selected APN. * <p>Type: INTEGER</p> * @hide */ diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 2677087a4c39..5d34c476a4a6 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1938,6 +1938,17 @@ public abstract class NotificationListenerService extends Service { /** * @hide */ + public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) { + if (previous != null && previous.mLastAudiblyAlertedMs > 0 + && this.mLastAudiblyAlertedMs <= 0) { + this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs; + } + return this; + } + + /** + * @hide + */ public void populate(Ranking other) { populate(other.mKey, other.mRank, diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 537498c44d5e..e338fd977f27 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -41,7 +41,6 @@ public class FeatureFlagUtils { public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; - public static final String SETTINGS_FUSE_FLAG = "settings_fuse"; /** @hide */ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED = "settings_do_not_restore_preserved"; @@ -52,7 +51,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS = new HashMap<>(); DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); - DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true"); DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 346fe293d7ae..6e34666aea84 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -24,7 +24,6 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; -import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.util.ArrayMap; @@ -213,11 +212,9 @@ public class ApkSignatureSchemeV2Verifier { verityDigest, apk.length(), signatureInfo); } - byte[] digest = pickBestDigestForV4(contentDigests); - return new VerifiedSigner( signerCerts.toArray(new X509Certificate[signerCerts.size()][]), - verityRootHash, digest); + verityRootHash, contentDigests); } private static X509Certificate[] verifySigner( @@ -339,8 +336,7 @@ public class ApkSignatureSchemeV2Verifier { } catch (CertificateException e) { throw new SecurityException("Failed to decode certificate #" + certificateCount, e); } - certificate = new VerbatimX509Certificate( - certificate, encodedCert); + certificate = new VerbatimX509Certificate(certificate, encodedCert); certs.add(certificate); } @@ -434,12 +430,15 @@ public class ApkSignatureSchemeV2Verifier { public final X509Certificate[][] certs; public final byte[] verityRootHash; - public final byte[] digest; + // Algorithm -> digest map of signed digests in the signature. + // All these are verified if requested. + public final Map<Integer, byte[]> contentDigests; - public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) { + public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, + Map<Integer, byte[]> contentDigests) { this.certs = certs; this.verityRootHash = verityRootHash; - this.digest = digest; + this.contentDigests = contentDigests; } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 4ab541b616ed..93572857796c 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -24,7 +24,6 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; -import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.os.Build; @@ -161,7 +160,7 @@ public class ApkSignatureSchemeV3Verifier { boolean doVerifyIntegrity) throws SecurityException, IOException { int signerCount = 0; Map<Integer, byte[]> contentDigests = new ArrayMap<>(); - VerifiedSigner result = null; + Pair<X509Certificate[], VerifiedProofOfRotation> result = null; CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); @@ -206,18 +205,17 @@ public class ApkSignatureSchemeV3Verifier { ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } + byte[] verityRootHash = null; if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); - result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength( + verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength( verityDigest, apk.length(), signatureInfo); } - result.digest = pickBestDigestForV4(contentDigests); - - return result; + return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests); } - private static VerifiedSigner verifySigner( + private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner( ByteBuffer signerBlock, Map<Integer, byte[]> contentDigests, CertificateFactory certFactory) @@ -349,8 +347,7 @@ public class ApkSignatureSchemeV3Verifier { } catch (CertificateException e) { throw new SecurityException("Failed to decode certificate #" + certificateCount, e); } - certificate = new VerbatimX509Certificate( - certificate, encodedCert); + certificate = new VerbatimX509Certificate(certificate, encodedCert); certs.add(certificate); } @@ -382,8 +379,9 @@ public class ApkSignatureSchemeV3Verifier { private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; - private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs, - List<X509Certificate> certs, CertificateFactory certFactory) throws IOException { + private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes( + ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory) + throws IOException { X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]); VerifiedProofOfRotation por = null; @@ -421,7 +419,7 @@ public class ApkSignatureSchemeV3Verifier { break; } } - return new VerifiedSigner(certChain, por); + return Pair.create(certChain, por); } private static VerifiedProofOfRotation verifyProofOfRotationStruct( @@ -570,12 +568,17 @@ public class ApkSignatureSchemeV3Verifier { public final X509Certificate[] certs; public final VerifiedProofOfRotation por; - public byte[] verityRootHash; - public byte[] digest; + public final byte[] verityRootHash; + // Algorithm -> digest map of signed digests in the signature. + // All these are verified if requested. + public final Map<Integer, byte[]> contentDigests; - public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) { + public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por, + byte[] verityRootHash, Map<Integer, byte[]> contentDigests) { this.certs = certs; this.por = por; + this.verityRootHash = verityRootHash; + this.contentDigests = contentDigests; } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java index d40efce0b3b3..844816c0d903 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java @@ -16,12 +16,14 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import android.os.incremental.IncrementalManager; import android.os.incremental.V4Signature; +import android.util.ArrayMap; import android.util.Pair; import java.io.ByteArrayInputStream; @@ -42,6 +44,7 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; +import java.util.Map; /** * APK Signature Scheme v4 verifier. @@ -79,13 +82,20 @@ public class ApkSignatureSchemeV4Verifier { throw new SignatureNotFoundException("Failed to read V4 signature.", e); } - final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo, + // Verify signed data and extract certificates and apk digest. + final byte[] signedData = V4Signature.getSignedData(apk.length(), hashingInfo, signingInfo); + final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData); - return verifySigner(signingInfo, signedData); + // Populate digests enforced by IncFS driver. + Map<Integer, byte[]> contentDigests = new ArrayMap<>(); + contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm), + hashingInfo.rawRootHash); + + return new VerifiedSigner(new Certificate[]{result.first}, result.second, contentDigests); } - private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo, + private static Pair<Certificate, byte[]> verifySigner(V4Signature.SigningInfo signingInfo, final byte[] signedData) throws SecurityException { if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) { throw new SecurityException("No supported signatures found"); @@ -145,21 +155,34 @@ public class ApkSignatureSchemeV4Verifier { "Public key mismatch between certificate and signature record"); } - return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest); + return Pair.create(certificate, signingInfo.apkDigest); + } + + private static int convertToContentDigestType(int hashAlgorithm) throws SecurityException { + if (hashAlgorithm == V4Signature.HASHING_ALGORITHM_SHA256) { + return CONTENT_DIGEST_VERITY_CHUNKED_SHA256; + } + throw new SecurityException("Unsupported hashAlgorithm: " + hashAlgorithm); } /** - * Verified APK Signature Scheme v4 signer, including V3 digest. + * Verified APK Signature Scheme v4 signer, including V2/V3 digest. * * @hide for internal use only. */ public static class VerifiedSigner { public final Certificate[] certs; - public byte[] apkDigest; + public final byte[] apkDigest; + + // Algorithm -> digest map of signed digests in the signature. + // These are continuously enforced by the IncFS driver. + public final Map<Integer, byte[]> contentDigests; - public VerifiedSigner(Certificate[] certs, byte[] apkDigest) { + public VerifiedSigner(Certificate[] certs, byte[] apkDigest, + Map<Integer, byte[]> contentDigests) { this.certs = certs; this.apkDigest = apkDigest; + this.contentDigests = contentDigests; } } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index ab8f80d3d1a5..e0258f7657b2 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -45,6 +45,7 @@ import java.security.cert.CertificateEncodingException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; @@ -184,21 +185,21 @@ public class ApkSignatureVerifier { Signature[] signerSigs = convertToSignatures(signerCerts); if (verifyFull) { - byte[] nonstreamingDigest = null; - Certificate[][] nonstreamingCerts = null; + Map<Integer, byte[]> nonstreamingDigests; + Certificate[][] nonstreamingCerts; try { // v4 is an add-on and requires v2 or v3 signature to validate against its // certificate and digest ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer = ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath); - nonstreamingDigest = v3Signer.digest; + nonstreamingDigests = v3Signer.contentDigests; nonstreamingCerts = new Certificate[][]{v3Signer.certs}; } catch (SignatureNotFoundException e) { try { ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer = ApkSignatureSchemeV2Verifier.verify(apkPath, false); - nonstreamingDigest = v2Signer.digest; + nonstreamingDigests = v2Signer.contentDigests; nonstreamingCerts = v2Signer.certs; } catch (SignatureNotFoundException ee) { throw new SecurityException( @@ -220,8 +221,15 @@ public class ApkSignatureVerifier { } } - if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest, - vSigner.apkDigest.length)) { + boolean found = false; + for (byte[] nonstreamingDigest : nonstreamingDigests.values()) { + if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest, + vSigner.apkDigest.length)) { + found = true; + break; + } + } + if (!found) { throw new SecurityException("APK digest in V4 signature does not match V2/V3"); } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 6efe95cb9e92..990092caa833 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -577,21 +577,6 @@ final class ApkSigningBlockUtils { } /** - * Returns the best digest from the map of available digests. - * similarly to compareContentDigestAlgorithm. - * - * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils. - */ - static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) { - for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) { - if (contentDigests.containsKey(algo)) { - return contentDigests.get(algo); - } - } - return null; - } - - /** * Returns new byte buffer whose content is a shared subsequence of this buffer's content * between the specified start (inclusive) and end (exclusive) positions. As opposed to * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl index 429c3aeba9b3..a0d4a6587bcf 100644 --- a/core/java/android/view/IDisplayWindowInsetsController.aidl +++ b/core/java/android/view/IDisplayWindowInsetsController.aidl @@ -27,6 +27,13 @@ import android.view.InsetsState; oneway interface IDisplayWindowInsetsController { /** + * Called when top focused window changes to determine whether or not to take over insets + * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false. + * @param packageName: Passes the top package name + */ + void topFocusedWindowChanged(String packageName); + + /** * @see IWindow#insetsChanged */ void insetsChanged(in InsetsState insetsState); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 58597cf3fb6d..00fc67214f75 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -388,16 +388,6 @@ interface IWindowManager oneway void hideTransientBars(int displayId); /** - * When set to {@code true} the system bars will always be shown. This is true even if an app - * requests to be fullscreen by setting the system ui visibility flags. The - * functionality was added for the automotive case as a way to guarantee required content stays - * on screen at all times. - * - * @hide - */ - oneway void setForceShowSystemBars(boolean show); - - /** * Called by System UI to notify of changes to the visibility of Recents. */ oneway void setRecentsVisibility(boolean visible); diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index ef9d990168d2..c1998c6009cf 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -119,11 +119,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching // this code here means that we now got control, so we can start the animation immediately. // If client window is trying to control IME and IME is already visible, it is immediate. - if (fromIme || mState.getSource(getType()).isVisible()) { + if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) { return ShowResult.SHOW_IMMEDIATELY; } - return getImm().requestImeShow(null /* resultReceiver */) + return getImm().requestImeShow(mController.getHost().getWindowToken()) ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED; } @@ -132,12 +132,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { */ @Override void notifyHidden() { - getImm().notifyImeHidden(); + getImm().notifyImeHidden(mController.getHost().getWindowToken()); } @Override public void removeSurface() { - getImm().removeImeSurface(); + final IBinder window = mController.getHost().getWindowToken(); + if (window != null) { + getImm().removeImeSurface(window); + } } @Override @@ -146,6 +149,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { super.setControl(control, showTypes, hideTypes); if (control == null && !mIsRequestedVisibleAwaitingControl) { hide(); + removeSurface(); } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 40e6f4b2fce8..700dc66fab55 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -153,6 +153,9 @@ public class InsetsSourceConsumer { if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) { applyHiddenToControl(); } + if (!requestedVisible && !mIsAnimationPending) { + removeSurface(); + } } } if (lastControl != null) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 13f11dce6e4b..2168fe331972 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13800,6 +13800,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + private void notifySubtreeAccessibilityStateChangedByParentIfNeeded() { + if (!AccessibilityManager.getInstance(mContext).isEnabled()) { + return; + } + + final View sendA11yEventView = (View) getParentForAccessibility(); + if (sendA11yEventView != null && sendA11yEventView.isShown()) { + sendA11yEventView.notifySubtreeAccessibilityStateChangedIfNeeded(); + } + } + /** * Changes the visibility of this View without triggering any other changes. This should only * be used by animation frameworks, such as {@link android.transition.Transition}, where @@ -16229,7 +16240,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, ((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) { dispatchVisibilityAggregated(newVisibility == VISIBLE); } - notifySubtreeAccessibilityStateChangedIfNeeded(); + // If this view is invisible from visible, then sending the A11y event by its + // parent which is shown and has the accessibility important. + if ((old & VISIBILITY_MASK) == VISIBLE) { + notifySubtreeAccessibilityStateChangedByParentIfNeeded(); + } else { + notifySubtreeAccessibilityStateChangedIfNeeded(); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 81a78dc177ec..b022d2af7772 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2717,7 +2717,6 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.isEnabled()) { mAttachInfo.mThreadedRenderer.destroy(); } - notifySurfaceDestroyed(); } else if ((surfaceReplaced || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) && mSurfaceHolder == null @@ -2948,6 +2947,10 @@ public final class ViewRootImpl implements ViewParent, } } + if (surfaceDestroyed) { + notifySurfaceDestroyed(); + } + if (triggerGlobalLayoutListener) { mAttachInfo.mRecomputeGlobalAttributes = false; mAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); @@ -9382,6 +9385,11 @@ public final class ViewRootImpl implements ViewParent, return mInputEventReceiver.getToken(); } + @NonNull + public IBinder getWindowToken() { + return mAttachInfo.mWindowToken; + } + /** * Class for managing the accessibility interaction connection * based on the global accessibility state. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 864c40f8a740..fa92e29cbf58 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2111,28 +2111,36 @@ public final class InputMethodManager { /** * Call showSoftInput with currently focused view. - * @return {@code true} if IME can be shown. + * + * @param windowToken the window from which this request originates. If this doesn't match the + * currently served view, the request is ignored and returns {@code false}. + * + * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise. * @hide */ - public boolean requestImeShow(ResultReceiver resultReceiver) { + public boolean requestImeShow(IBinder windowToken) { synchronized (mH) { final View servedView = getServedViewLocked(); - if (servedView == null) { + if (servedView == null || servedView.getWindowToken() != windowToken) { return false; } - showSoftInput(servedView, 0 /* flags */, resultReceiver); + showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */); return true; } } /** * Notify IME directly that it is no longer visible. + * + * @param windowToken the window from which this request originates. If this doesn't match the + * currently served view, the request is ignored. * @hide */ - public void notifyImeHidden() { + public void notifyImeHidden(IBinder windowToken) { synchronized (mH) { try { - if (mCurMethod != null) { + if (mCurMethod != null && mCurRootView != null + && mCurRootView.getWindowToken() == windowToken) { mCurMethod.notifyImeHidden(); } } catch (RemoteException re) { @@ -2142,15 +2150,15 @@ public final class InputMethodManager { /** * Notify IME directly to remove surface as it is no longer visible. + * @param windowToken The client window token that requests the IME to remove its surface. * @hide */ - public void removeImeSurface() { + public void removeImeSurface(IBinder windowToken) { synchronized (mH) { try { - if (mCurMethod != null) { - mCurMethod.removeImeSurface(); - } - } catch (RemoteException re) { + mService.removeImeSurfaceFromWindow(windowToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } } diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index ff3ac0732aa2..9eb63087a66e 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -174,12 +174,9 @@ public class EditorTouchState { int touchSlop = config.getScaledTouchSlop(); mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop; if (mMovedEnoughForDrag) { - // If the direction of the swipe motion is within 30 degrees of vertical, it is - // considered a vertical drag. We don't actually have to compute the angle to - // implement the check though. When the angle is exactly 30 degrees from - // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from - // vertical, 2*deltaX < distance. - mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared; + // If the direction of the swipe motion is within 45 degrees of vertical, it is + // considered a vertical drag. + mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY); } } } else if (action == MotionEvent.ACTION_CANCEL) { diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 843700cef55e..32c68bdd0491 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -108,7 +108,7 @@ public final class SelectionActionModeHelper { * * @return the swap result, index 0 is the start index and index 1 is the end index. */ - private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) { + private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) { if (selectionStart < selectionEnd) { return new int[]{selectionStart, selectionEnd}; } @@ -122,11 +122,11 @@ public final class SelectionActionModeHelper { * @param textView the selected TextView. * @return the swap result, index 0 is the start index and index 1 is the end index. */ - private static int[] sortSelctionIndicesFromTextView(TextView textView) { + private static int[] sortSelectionIndicesFromTextView(TextView textView) { int selectionStart = textView.getSelectionStart(); int selectionEnd = textView.getSelectionEnd(); - return sortSelctionIndices(selectionStart, selectionEnd); + return sortSelectionIndices(selectionStart, selectionEnd); } /** @@ -135,7 +135,7 @@ public final class SelectionActionModeHelper { public void startSelectionActionModeAsync(boolean adjustSelection) { // Check if the smart selection should run for editable text. adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled(); - int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onOriginalSelection( getText(mTextView), @@ -165,7 +165,7 @@ public final class SelectionActionModeHelper { * Starts Link ActionMode. */ public void startLinkActionModeAsync(int start, int end) { - int[] indexResult = sortSelctionIndices(start, end); + int[] indexResult = sortSelectionIndices(start, end); mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1], true /*isLink*/); cancelAsyncTask(); @@ -201,21 +201,21 @@ public final class SelectionActionModeHelper { /** Reports a selection action event. */ public void onSelectionAction(int menuItemId, @Nullable String actionLabel) { - int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onSelectionAction( sortedSelectionIndices[0], sortedSelectionIndices[1], getActionType(menuItemId), actionLabel, mTextClassification); } public void onSelectionDrag() { - int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onSelectionAction( sortedSelectionIndices[0], sortedSelectionIndices[1], SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification); } public void onTextChanged(int start, int end) { - int[] sortedSelectionIndices = sortSelctionIndices(start, end); + int[] sortedSelectionIndices = sortSelectionIndices(start, end); mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification); } @@ -334,7 +334,7 @@ public final class SelectionActionModeHelper { startSelectionActionMode(startSelectionResult); }; // TODO do not trigger the animation if the change included only non-printable characters - int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); final boolean didSelectionChange = result != null && (sortedSelectionIndices[0] != result.mStart || sortedSelectionIndices[1] != result.mEnd); @@ -486,7 +486,7 @@ public final class SelectionActionModeHelper { if (actionMode != null) { actionMode.invalidate(); } - final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView); + final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onSelectionUpdated( sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification); mTextClassificationAsyncTask = null; @@ -495,7 +495,7 @@ public final class SelectionActionModeHelper { private void resetTextClassificationHelper(int selectionStart, int selectionEnd) { if (selectionStart < 0 || selectionEnd < 0) { // Use selection indices - int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); selectionStart = sortedSelectionIndices[0]; selectionEnd = sortedSelectionIndices[1]; } @@ -637,7 +637,7 @@ public final class SelectionActionModeHelper { mAllowReset = false; boolean selected = editor.selectCurrentWord(); if (selected) { - final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView); + final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView); mSelectionStart = sortedSelectionIndices[0]; mSelectionEnd = sortedSelectionIndices[1]; mLogger.logSelectionAction( @@ -1215,7 +1215,7 @@ public final class SelectionActionModeHelper { SelectionResult(int start, int end, @Nullable TextClassification classification, @Nullable TextSelection selection) { - int[] sortedIndices = sortSelctionIndices(start, end); + int[] sortedIndices = sortSelectionIndices(start, end); mStart = sortedIndices[0]; mEnd = sortedIndices[1]; mClassification = classification; diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 476198b5311a..bbfd07b64ccf 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -358,6 +358,8 @@ public class NativeLibraryHelper { createNativeLibrarySubdir(subDir); } + // Even if extractNativeLibs is false, we still need to check if the native libs in the APK + // are valid. This is done in the native code. int copyRet = copyNativeBinaries(handle, subDir, supportedAbi); if (copyRet != PackageManager.INSTALL_SUCCEEDED) { return copyRet; diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index cc55cff262a3..e09ef49acd10 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -27,6 +27,7 @@ import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -126,6 +127,11 @@ public class BinderCallsStats implements BinderInternal.Observer { } }; + private final Object mNativeTidsLock = new Object(); + // @GuardedBy("mNativeTidsLock") // Cannot mark it as "GuardedBy" because it's read + // directly, as a volatile field. + private volatile IntArray mNativeTids = new IntArray(0); + /** Injector for {@link BinderCallsStats}. */ public static class Injector { public Random getRandomGenerator() { @@ -175,6 +181,8 @@ public class BinderCallsStats implements BinderInternal.Observer { return null; } + noteNativeThreadId(); + final CallSession s = obtainCallSession(); s.binderClass = binder.getClass(); s.transactionCode = code; @@ -312,6 +320,27 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + private void noteNativeThreadId() { + final int tid = getNativeTid(); + int index = mNativeTids.binarySearch(tid); + if (index >= 0) { + return; + } + + // Use the copy-on-write approach. The changes occur exceedingly infrequently, so + // this code path is exercised just a few times per boot + synchronized (mNativeTidsLock) { + IntArray nativeTids = mNativeTids; + index = nativeTids.binarySearch(tid); + if (index < 0) { + IntArray copyOnWriteArray = new IntArray(nativeTids.size() + 1); + copyOnWriteArray.addAll(nativeTids); + copyOnWriteArray.add(-index - 1, tid); + mNativeTids = copyOnWriteArray; + } + } + } + /** * This method is expensive to call. */ @@ -505,6 +534,17 @@ public class BinderCallsStats implements BinderInternal.Observer { return Binder.getCallingUid(); } + protected int getNativeTid() { + return Binder.getNativeTid(); + } + + /** + * Returns known Linux TIDs for threads taking incoming binder calls. + */ + public int[] getNativeTids() { + return mNativeTids.toArray(); + } + protected long getElapsedRealtimeMicro() { return SystemClock.elapsedRealtimeNanos() / 1000; } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 0b46658d5a06..0a3fe096279d 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static android.view.WindowInsets.Type.ime; -import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -34,8 +32,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.NonNull; import android.annotation.Nullable; @@ -146,17 +142,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) { return new Pair<>(Insets.NONE, insets); } - - boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode - & SOFT_INPUT_MASK_ADJUST) - == SOFT_INPUT_ADJUST_RESIZE; - Insets insetsToApply; - if (ViewRootImpl.sNewInsetsMode == 0) { - insetsToApply = insets.getSystemWindowInsets(); - } else { - insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0)); - } - insets = insets.inset(insetsToApply); + Insets insetsToApply = insets.getSystemWindowInsets(); return new Pair<>(insetsToApply, insets.inset(insetsToApply).consumeSystemWindowInsets()); }; diff --git a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl index cf1220c08467..03948a92bcab 100644 --- a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl +++ b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl @@ -58,4 +58,7 @@ oneway interface IInlineSuggestionsRequestCallback { // #onFinishInput()} is called on the field specified by the earlier // {@link #onInputMethodStartInput(AutofillId)}. void onInputMethodFinishInput(); + + // Indicates that the current IME changes inline suggestion session. + void onInlineSuggestionsSessionInvalidated(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 8ec51b89d240..a1cbd3fcae79 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -73,5 +73,8 @@ interface IInputMethodManager { in float[] matrixValues); oneway void reportPerceptible(in IBinder windowToken, boolean perceptible); + /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */ void removeImeSurface(); + /** Remove the IME surface. Requires passing the currently focused window. */ + void removeImeSurfaceFromWindow(in IBinder windowToken); } diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp index 71edfd553e7e..350f35828418 100644 --- a/core/jni/android_hardware_input_InputApplicationHandle.cpp +++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp @@ -61,8 +61,8 @@ bool NativeInputApplicationHandle::updateInfo() { mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>"); - mInfo.dispatchingTimeout = env->GetLongField(obj, - gInputApplicationHandleClassInfo.dispatchingTimeoutNanos); + mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)( + env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos)); jobject tokenObj = env->GetObjectField(obj, gInputApplicationHandleClassInfo.token); diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 81569e0f7b7a..792c00572318 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -117,8 +117,8 @@ bool NativeInputWindowHandle::updateInfo() { gInputWindowHandleClassInfo.layoutParamsFlags); mInfo.layoutParamsType = env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType); - mInfo.dispatchingTimeout = env->GetLongField(obj, - gInputWindowHandleClassInfo.dispatchingTimeoutNanos); + mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)( + env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos)); mInfo.frameLeft = env->GetIntField(obj, gInputWindowHandleClassInfo.frameLeft); mInfo.frameTop = env->GetIntField(obj, diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index b6427c9aa01c..48f33a6a3d77 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -339,6 +339,10 @@ static jobject JHwBinder_native_getService( return JHwRemoteBinder::NewObject(env, service); } +void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) { + hardware::details::setTrebleTestingOverride(testingOverride); +} + void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass, jlong maxThreads, jboolean callerWillJoin) { CHECK(maxThreads > 0); @@ -368,6 +372,9 @@ static JNINativeMethod gMethods[] = { { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, + { "setTrebleTestingOverride", "(Z)V", + (void *)JHwBinder_native_setTrebleTestingOverride }, + { "configureRpcThreadpool", "(JZ)V", (void *)JHwBinder_native_configureRpcThreadpool }, diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index a88f8919ed08..ff336ee64b54 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -122,10 +122,18 @@ void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteExcep std::stringstream ss; ss << "HwBinder Error: (" << err << ")"; - jniThrowException( - env, - canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException", - ss.str().c_str()); + const char* exception = nullptr; + if (canThrowRemoteException) { + if (err == DEAD_OBJECT) { + exception = "android/os/DeadObjectException"; + } else { + exception = "android/os/RemoteException"; + } + } else { + exception = "java/lang/RuntimeException"; + } + + jniThrowException(env, exception, ss.str().c_str()); break; } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 4c9d7abeb5a1..885b0a33e43c 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -30,6 +30,7 @@ #include <unistd.h> #include <android-base/stringprintf.h> +#include <android-base/threads.h> #include <binder/BpBinder.h> #include <binder/IInterface.h> #include <binder/IPCThreadState.h> @@ -1047,6 +1048,10 @@ static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject ext jbh->setExtension(extension); } +static jint android_os_Binder_getNativeTid() { + return (jint)android::base::GetThreadId(); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gBinderMethods[] = { @@ -1078,6 +1083,8 @@ static const JNINativeMethod gBinderMethods[] = { { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }, { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension }, { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension }, + // @CriticalNative + { "getNativeTid", "()I", (void*)android_os_Binder_getNativeTid }, }; const char* const kBinderPathName = "android/os/Binder"; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 6e49c0dda1ad..b7c5289043d6 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -124,7 +124,6 @@ typedef const std::function<void(std::string)>& fail_fn_t; static pid_t gSystemServerPid = 0; static constexpr const char* kVoldAppDataIsolation = "persist.sys.vold_app_data_isolation_enabled"; -static constexpr const char* kPropFuse = "persist.sys.fuse"; static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkSystemServerHooks; @@ -836,29 +835,20 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL, multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn); - bool isFuse = GetBoolProperty(kPropFuse, false); bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false); - if (isFuse) { - if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { + if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id); PrepareDir(pass_through_source, 0710, AID_ROOT, AID_MEDIA_RW, fail_fn); BindMount(pass_through_source, "/storage", fail_fn); - } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { + } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id); BindMount(installer_source, "/storage", fail_fn); - } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) { + } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) { const std::string writable_source = StringPrintf("/mnt/androidwritable/%d", user_id); BindMount(writable_source, "/storage", fail_fn); - } else { - BindMount(user_source, "/storage", fail_fn); - } } else { - const std::string& storage_source = ExternalStorageViews[mount_mode]; - BindMount(storage_source, "/storage", fail_fn); - - // Mount user-specific symlink helper into place - BindMount(user_source, "/storage/self", fail_fn); + BindMount(user_source, "/storage", fail_fn); } } diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 00505caa18c3..f5d7246b96a3 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1172,7 +1172,7 @@ <string name="Midnight" msgid="8176019203622191377">"منتصف الليل"</string> <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> - <string name="selectAll" msgid="1532369154488982046">"اختيار الكل"</string> + <string name="selectAll" msgid="1532369154488982046">"تحديد الكل"</string> <string name="cut" msgid="2561199725874745819">"قص"</string> <string name="copy" msgid="5472512047143665218">"نسخ"</string> <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"تعذّر النسخ في الحافظة"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index ce08e43cb1af..d312d859a58c 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1152,7 +1152,7 @@ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Capturar imagen con %1$s"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagen"</string> <string name="alwaysUse" msgid="3153558199076112903">"Usar siempre para esta acción"</string> - <string name="use_a_different_app" msgid="4987790276170972776">"Utiliza otra aplicación"</string> + <string name="use_a_different_app" msgid="4987790276170972776">"Usar otra aplicación"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Para borrar los valores predeterminados, accede a Ajustes del sistema > Aplicaciones > Descargadas."</string> <string name="chooseActivity" msgid="8563390197659779956">"Selecciona una acción"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"Elegir una aplicación para el dispositivo USB"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index eb675ddb06a5..485f5bf08224 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1926,7 +1926,7 @@ <string name="time_picker_header_text" msgid="9073802285051516688">"Ezarri ordua"</string> <string name="time_picker_input_error" msgid="8386271930742451034">"Idatzi balio duen ordu bat"</string> <string name="time_picker_prompt_label" msgid="303588544656363889">"Idatzi ordua"</string> - <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Aldatu testu modura ordua zehazteko."</string> + <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Ordua idazteko, aldatu testua idazteko metodora."</string> <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Aldatu erloju modura ordua zehazteko."</string> <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Betetze automatikoaren aukerak"</string> <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Gorde betetze automatikoarekin erabiltzeko"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 8d9df1f2b09b..bd328b542966 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -857,7 +857,7 @@ <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string> <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"A tarxeta SIM está bloqueada con código PUK."</string> - <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string> + <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía para usuarios ou ponte en contacto co servizo de asistencia ao cliente."</string> <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"A tarxeta SIM está bloqueada."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Desbloqueando tarxeta SIM…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 99665c43bf20..4f3d89009323 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1548,7 +1548,7 @@ <string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउज़र लॉन्च करें?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकार करें?"</string> <string name="activity_resolver_use_always" msgid="5575222334666843269">"हमेशा"</string> - <string name="activity_resolver_use_once" msgid="948462794469672658">"केवल एक बार"</string> + <string name="activity_resolver_use_once" msgid="948462794469672658">"सिर्फ़ एक बार"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s वर्क प्रोफ़ाइल का समर्थन नहीं करता"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"टैबलेट"</string> <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीवी"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index e74157f2e02b..eabf37c50878 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1152,7 +1152,7 @@ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"「%1$s」を使用して画像をキャプチャ"</string> <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"画像をキャプチャ"</string> <string name="alwaysUse" msgid="3153558199076112903">"常にこの操作で使用する"</string> - <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリの使用"</string> + <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリを使用"</string> <string name="clearDefaultHintMsg" msgid="1325866337702524936">"[システム設定]>[アプリ]>[ダウンロード済み]でデフォルト設定をクリアします。"</string> <string name="chooseActivity" msgid="8563390197659779956">"操作の選択"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"USBデバイス用アプリを選択"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index affbc838048b..351790d44b7b 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"Жіберілуде..."</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"Браузер қосылсын ба?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Қоңырауды қабылдау?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"Үнемі"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"Әрқашан"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Бір рет қана"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жұмыс профилін қолдамайды"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index df8e33d5c149..f1bea56a62fd 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -538,7 +538,7 @@ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа жыйнагыңыз сакталган жерлерди окуу"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string> - <string name="biometric_dialog_default_title" msgid="55026799173208210">"Сиз экениңизди ырастаңыз"</string> + <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string> @@ -1111,7 +1111,7 @@ <string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string> <string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string> - <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Системанын кээ бир функциялары иштебеши мүмкүн"</string> + <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string> <string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> иштөөдө"</string> <string name="app_running_notification_text" msgid="5120815883400228566">"Көбүрөөк маалымат үчүн же колдонмону токтотуш үчүн таптап коюңуз."</string> @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"Жөнөтүлүүдө…"</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"Серепчи иштетилсинби?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Чалуу кабыл алынсынбы?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"Дайыма"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"Ар дайым"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Бир жолу гана"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жумуш профилин колдоого албайт"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index a24db6617c2e..b4982ebdeeca 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1547,7 +1547,7 @@ <string name="sending" msgid="206925243621664438">"അയയ്ക്കുന്നു…"</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"ബ്രൗസർ സമാരംഭിക്കണോ?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"കോൾ സ്വീകരിക്കണോ?"</string> - <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string> + <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"ഒരിക്കൽ മാത്രം"</string> <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്ക്കുന്നില്ല"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"ടാബ്ലെറ്റ്"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 8eb8cef9f3c2..be21f7b8df15 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1093,7 +1093,7 @@ <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll" msgid="1532369154488982046">"Hammasini belgilash"</string> - <string name="cut" msgid="2561199725874745819">"Kesish"</string> + <string name="cut" msgid="2561199725874745819">"Kesib olish"</string> <string name="copy" msgid="5472512047143665218">"Nusxa olish"</string> <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Vaqtinchalik xotiraga nusxalab bo‘lmadi"</string> <string name="paste" msgid="461843306215520225">"Joylash"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 8061ed8c6dd2..33675da1fd38 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -538,7 +538,7 @@ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"đọc vị trí từ bộ sưu tập phương tiện"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string> - <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh đó là bạn"</string> + <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 51737ba80550..6ccfc09a46a0 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1792,8 +1792,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string> - <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string> - <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string> + <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string> + <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> @@ -2000,9 +2000,9 @@ <string name="notification_feedback_indicator" msgid="663476517711323016">"提供意見"</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電池電力可能會在你平常的充電時間前耗盡"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用節約耗電量模式以延長電池續航力"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"節約耗電量"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"節約耗電量模式已關閉"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用省電模式以延長電池續航力"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"省電模式"</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"省電模式已關閉"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"手機電力充足,各項功能不再受到限制。"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"平板電腦電力充足,各項功能不再受到限制。"</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"裝置電力充足,各項功能不再受到限制。"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f60766adc7f7..28a164f139e1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -712,10 +712,17 @@ case, this can be disabled (set to false). --> <bool name="config_enableCarDockHomeLaunch">true</bool> - <!-- Control whether to force the display of System UI Bars at all times regardless of - System Ui Flags. This can be useful in the Automotive case if there's a requirement for - a UI element to be on screen at all times. --> - <bool name="config_forceShowSystemBars">false</bool> + <!-- Control whether to force apps to give up control over the display of system bars at all + times regardless of System Ui Flags. + In the Automotive case, this is helpful if there's a requirement for an UI element to be on + screen at all times. Setting this to true also gives System UI the ability to override the + visibility controls for the system through the usage of the + "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting. + Ex: Only setting the config to true will force show system bars for the entire system. + Ex: Setting the config to true and the "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting to + "immersive.status=apps" will force show navigation bar for all apps and force hide status + bar for all apps. --> + <bool name="config_remoteInsetsControllerControlsSystemBars">false</bool> <!-- HDMI behavior --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 37d1eccefd4a..303fde6705c2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3024,6 +3024,8 @@ <!-- @hide @TestApi --> <public type="bool" name="config_assistantOnTopOfDream" id="0x01110005" /> + <!-- @hide @TestApi --> + <public type="bool" name="config_remoteInsetsControllerControlsSystemBars" id="0x01110006" /> <!-- =============================================================== Resources added in version S of the platform diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5bb784a1edc7..16427cd50577 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1669,7 +1669,7 @@ <java-symbol type="bool" name="config_enableCarDockHomeLaunch" /> <java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" /> <java-symbol type="bool" name="config_enableLockScreenRotation" /> - <java-symbol type="bool" name="config_forceShowSystemBars" /> + <java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" /> <java-symbol type="bool" name="config_lidControlsScreenLock" /> <java-symbol type="bool" name="config_lidControlsSleep" /> <java-symbol type="bool" name="config_lockDayNightMode" /> diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index 17d1389a6602..777f4a3e03a8 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.app.ActivityThread; @@ -180,4 +181,26 @@ public class ContextTest { VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); return virtualDisplay.getDisplay(); } + + @Test + public void testIsUiContext_ContextWrapper() { + ContextWrapper wrapper = new ContextWrapper(null /* base */); + + assertFalse(wrapper.isUiContext()); + + wrapper = new ContextWrapper(new TestUiContext()); + + assertTrue(wrapper.isUiContext()); + } + + private static class TestUiContext extends ContextWrapper { + TestUiContext() { + super(null /* base */); + } + + @Override + public boolean isUiContext() { + return true; + } + } } diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 1b3272572db0..7efd616c5607 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -71,6 +71,9 @@ public class InsetsSourceConsumerTest { private SurfaceControl mLeash; @Mock Transaction mMockTransaction; private InsetsSource mSpyInsetsSource; + private boolean mRemoveSurfaceCalled = false; + private InsetsController mController; + private InsetsState mState; @Before public void setup() { @@ -89,13 +92,19 @@ public class InsetsSourceConsumerTest { } catch (BadTokenException e) { // activity isn't running, lets ignore BadTokenException. } - InsetsState state = new InsetsState(); + mState = new InsetsState(); mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR)); - state.addSource(mSpyInsetsSource); - - mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state, - () -> mMockTransaction, - new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl))); + mState.addSource(mSpyInsetsSource); + + mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)); + mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState, + () -> mMockTransaction, mController) { + @Override + public void removeSurface() { + super.removeSurface(); + mRemoveSurfaceCalled = true; + } + }; }); instrumentation.waitForIdleSync(); @@ -171,6 +180,25 @@ public class InsetsSourceConsumerTest { mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), new int[1], hideTypes); assertEquals(statusBars(), hideTypes[0]); + assertFalse(mRemoveSurfaceCalled); + }); + } + + @Test + public void testRestore_noAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mConsumer.hide(); + mController.onStateChanged(mState); + mConsumer.setControl(null, new int[1], new int[1]); + reset(mMockTransaction); + verifyZeroInteractions(mMockTransaction); + mRemoveSurfaceCalled = false; + int[] hideTypes = new int[1]; + mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), + new int[1], hideTypes); + assertTrue(mRemoveSurfaceCalled); + assertEquals(0, hideTypes[0]); }); + } } diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index 89cc6e743752..df2946c97d20 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -37,7 +37,6 @@ import static org.junit.Assert.assertTrue; import android.app.Activity; import android.app.Instrumentation; import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; @@ -96,7 +95,6 @@ public class EditorCursorDragTest { mMotionEvents.clear(); } - @Presubmit @Test public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable { String text = "Hello world!"; @@ -145,7 +143,7 @@ public class EditorCursorDragTest { // Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to // the handle as the touch moves downwards (and because we have some slop to avoid jumping // across lines), the cursor position will end up higher than the finger position. - onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3"))); + onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2"))); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1"))); // Swipe right-down along a very steep diagonal path. This should not drag the cursor. @@ -181,7 +179,7 @@ public class EditorCursorDragTest { // Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to // the handle as the touch moves downwards (and because we have some slop to avoid jumping // across lines), the cursor position will end up higher than the finger position. - onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3"))); + onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2"))); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1"))); // Swipe right-down along a very steep diagonal path. This should not drag the cursor. diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index ec75e40f1334..35fd4bd7dc14 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -326,9 +326,9 @@ public class EditorTouchStateTest { mTouchState.update(event1, mConfig); assertSingleTap(mTouchState, 0f, 0f, 0, 0); - // Simulate an ACTION_MOVE event that is > 30 deg from vertical. + // Simulate an ACTION_MOVE event that is > 45 deg from vertical. long event2Time = 1002; - MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f); + MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f); mTouchState.update(event2, mConfig); assertDrag(mTouchState, 0f, 0f, 0, 0, false); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index b7d1c9b993d9..a5117a3e7cc3 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -807,6 +807,38 @@ public class BinderCallsStatsTest { } } + @Test + public void testNativeTids() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + Binder binder = new Binder(); + + bcs.nativeTid = 3; + + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + bcs.nativeTid = 1; + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + bcs.nativeTid = 1; + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + bcs.nativeTid = 2; + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + int[] tids = bcs.getNativeTids(); + assertEquals(3, tids.length); + assertEquals(1, tids[0]); + assertEquals(2, tids[1]); + assertEquals(3, tids[2]); + } + private static class TestHandler extends Handler { ArrayList<Runnable> mRunnables = new ArrayList<>(); @@ -825,6 +857,7 @@ public class BinderCallsStatsTest { public int callingUid = CALLING_UID; public long time = 1234; public long elapsedTime = 0; + public int nativeTid; TestBinderCallsStats() { this(mDeviceState); @@ -874,6 +907,10 @@ public class BinderCallsStatsTest { protected void setCallingUid(int uid) { callingUid = uid; } - } + @Override + protected int getNativeTid() { + return nativeTid; + } + } } diff --git a/data/keyboards/Vendor_1532_Product_1007.kl b/data/keyboards/Vendor_1532_Product_1007.kl new file mode 100644 index 000000000000..6f6c286b34a5 --- /dev/null +++ b/data/keyboards/Vendor_1532_Product_1007.kl @@ -0,0 +1,65 @@ +# 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. + +# +# Razer Raiju Tournament Edition Controller with wired USB interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +key 0x13d BUTTON_1 diff --git a/data/keyboards/Vendor_1532_Product_100a.kl b/data/keyboards/Vendor_1532_Product_100a.kl new file mode 100644 index 000000000000..b0e966d519b3 --- /dev/null +++ b/data/keyboards/Vendor_1532_Product_100a.kl @@ -0,0 +1,65 @@ +# 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. + +# +# Razer Raiju Tournament Edition Controller with wireless Bluetooth interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +key 0x13d BUTTON_1 diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 3b494e9129db..5e480a66c355 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -24,30 +24,9 @@ #include <log/log.h> -namespace android { - -// --- WeakLooperCallback --- - -class WeakLooperCallback: public LooperCallback { -protected: - virtual ~WeakLooperCallback() { } - -public: - explicit WeakLooperCallback(const wp<LooperCallback>& callback) : - mCallback(callback) { - } +#include <memory> - virtual int handleEvent(int fd, int events, void* data) { - sp<LooperCallback> callback = mCallback.promote(); - if (callback != NULL) { - return callback->handleEvent(fd, events, data); - } - return 0; // the client is gone, remove the callback - } - -private: - wp<LooperCallback> mCallback; -}; +namespace android { // --- PointerController --- @@ -64,29 +43,50 @@ static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms // The number of events to be read at once for DisplayEventReceiver. static const int EVENT_BUFFER_SIZE = 100; -// --- PointerController --- - -PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController) : - mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { - mHandler = new WeakMessageHandler(this); - mCallback = new WeakLooperCallback(this); - - if (mDisplayEventReceiver.initCheck() == NO_ERROR) { - mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, mCallback, nullptr); +std::shared_ptr<PointerController> PointerController::create( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController) { + std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( + new PointerController(policy, looper, spriteController)); + + /* + * Now we need to hook up the constructed PointerController object to its callbacks. + * + * This must be executed after the constructor but before any other methods on PointerController + * in order to ensure that the fully constructed object is visible on the Looper thread, since + * that may be a different thread than where the PointerController is initially constructed. + * + * Unfortunately, this cannot be done as part of the constructor since we need to hand out + * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr. + */ + + controller->mHandler->pointerController = controller; + controller->mCallback->pointerController = controller; + if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) { + controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, controller->mCallback, nullptr); } else { ALOGE("Failed to initialize DisplayEventReceiver."); } + return controller; +} +PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, + const sp<SpriteController>& spriteController) + : mPolicy(policy), + mLooper(looper), + mSpriteController(spriteController), + mHandler(new MessageHandler()), + mCallback(new LooperCallback()) { AutoMutex _l(mLock); mLocked.animationPending = false; - mLocked.presentation = PRESENTATION_POINTER; + mLocked.presentation = Presentation::POINTER; mLocked.presentationChanged = false; - mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL; + mLocked.inactivityTimeout = InactivityTimeout::NORMAL; mLocked.pointerFadeDirection = 0; mLocked.pointerX = 0; @@ -221,7 +221,7 @@ void PointerController::fade(Transition transition) { removeInactivityTimeoutLocked(); // Start fading. - if (transition == TRANSITION_IMMEDIATE) { + if (transition == Transition::IMMEDIATE) { mLocked.pointerFadeDirection = 0; mLocked.pointerAlpha = 0.0f; updatePointerLocked(); @@ -238,7 +238,7 @@ void PointerController::unfade(Transition transition) { resetInactivityTimeoutLocked(); // Start unfading. - if (transition == TRANSITION_IMMEDIATE) { + if (transition == Transition::IMMEDIATE) { mLocked.pointerFadeDirection = 0; mLocked.pointerAlpha = 1.0f; updatePointerLocked(); @@ -262,7 +262,7 @@ void PointerController::setPresentation(Presentation presentation) { return; } - if (presentation == PRESENTATION_POINTER) { + if (presentation == Presentation::POINTER) { if (mLocked.additionalMouseResources.empty()) { mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, &mLocked.animationResources, @@ -480,24 +480,35 @@ void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { updatePointerLocked(); } -void PointerController::handleMessage(const Message& message) { +void PointerController::MessageHandler::handleMessage(const Message& message) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + + if (controller == nullptr) { + ALOGE("PointerController instance was released before processing message: what=%d", + message.what); + return; + } switch (message.what) { case MSG_INACTIVITY_TIMEOUT: - doInactivityTimeout(); + controller->doInactivityTimeout(); break; } } -int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { +int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + if (controller == nullptr) { + ALOGW("PointerController instance was released with pending callbacks. events=0x%x", + events); + return 0; // Remove the callback, the PointerController is gone anyways + } if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); return 0; // remove the callback } if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); return 1; // keep the callback } @@ -505,7 +516,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { ssize_t n; nsecs_t timestamp; DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { for (size_t i = 0; i < static_cast<size_t>(n); ++i) { if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { timestamp = buf[i].header.timestamp; @@ -514,7 +525,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { } } if (gotVsync) { - doAnimate(timestamp); + controller->doAnimate(timestamp); } return 1; // keep the callback } @@ -613,7 +624,7 @@ bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) { } void PointerController::doInactivityTimeout() { - fade(TRANSITION_GRADUAL); + fade(Transition::GRADUAL); } void PointerController::startAnimationLocked() { @@ -627,8 +638,9 @@ void PointerController::startAnimationLocked() { void PointerController::resetInactivityTimeoutLocked() { mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); - nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT - ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; + nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT + ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT + : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); } @@ -655,7 +667,7 @@ void PointerController::updatePointerLocked() REQUIRES(mLock) { } if (mLocked.pointerIconChanged || mLocked.presentationChanged) { - if (mLocked.presentation == PRESENTATION_POINTER) { + if (mLocked.presentation == Presentation::POINTER) { if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { mLocked.pointerSprite->setIcon(mLocked.pointerIcon); } else { @@ -731,7 +743,7 @@ PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vec return spot; } } - return NULL; + return nullptr; } void PointerController::releaseSpotLocked(Spot* spot) { @@ -772,7 +784,7 @@ void PointerController::loadResourcesLocked() REQUIRES(mLock) { mLocked.additionalMouseResources.clear(); mLocked.animationResources.clear(); - if (mLocked.presentation == PRESENTATION_POINTER) { + if (mLocked.presentation == Presentation::POINTER) { mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, &mLocked.animationResources, mLocked.viewport.displayId); } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index ebc622bae302..14c0679654c6 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -17,19 +17,20 @@ #ifndef _UI_POINTER_CONTROLLER_H #define _UI_POINTER_CONTROLLER_H -#include "SpriteController.h" - -#include <map> -#include <vector> - -#include <ui/DisplayInfo.h> +#include <PointerControllerInterface.h> +#include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <PointerControllerInterface.h> +#include <ui/DisplayInfo.h> #include <utils/BitSet.h> -#include <utils/RefBase.h> #include <utils/Looper.h> -#include <gui/DisplayEventReceiver.h> +#include <utils/RefBase.h> + +#include <map> +#include <memory> +#include <vector> + +#include "SpriteController.h" namespace android { @@ -70,25 +71,22 @@ public: virtual int32_t getCustomPointerIconId() = 0; }; - /* * Tracks pointer movements and draws the pointer sprite to a surface. * * Handles pointer acceleration and animation. */ -class PointerController : public PointerControllerInterface, public MessageHandler, - public LooperCallback { -protected: - virtual ~PointerController(); - +class PointerController : public PointerControllerInterface { public: - enum InactivityTimeout { - INACTIVITY_TIMEOUT_NORMAL = 0, - INACTIVITY_TIMEOUT_SHORT = 1, + static std::shared_ptr<PointerController> create( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController); + enum class InactivityTimeout { + NORMAL = 0, + SHORT = 1, }; - PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController); + virtual ~PointerController(); virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -113,8 +111,8 @@ public: void reloadPointerResources(); private: - static const size_t MAX_RECYCLED_SPRITES = 12; - static const size_t MAX_SPOTS = 12; + static constexpr size_t MAX_RECYCLED_SPRITES = 12; + static constexpr size_t MAX_SPOTS = 12; enum { MSG_INACTIVITY_TIMEOUT, @@ -130,8 +128,13 @@ private: float x, y; inline Spot(uint32_t id, const sp<Sprite>& sprite) - : id(id), sprite(sprite), alpha(1.0f), scale(1.0f), - x(0.0f), y(0.0f), lastIcon(NULL) { } + : id(id), + sprite(sprite), + alpha(1.0f), + scale(1.0f), + x(0.0f), + y(0.0f), + lastIcon(nullptr) {} void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); @@ -139,12 +142,24 @@ private: const SpriteIcon* lastIcon; }; + class MessageHandler : public virtual android::MessageHandler { + public: + void handleMessage(const Message& message) override; + std::weak_ptr<PointerController> pointerController; + }; + + class LooperCallback : public virtual android::LooperCallback { + public: + int handleEvent(int fd, int events, void* data) override; + std::weak_ptr<PointerController> pointerController; + }; + mutable Mutex mLock; sp<PointerControllerPolicyInterface> mPolicy; sp<Looper> mLooper; sp<SpriteController> mSpriteController; - sp<WeakMessageHandler> mHandler; + sp<MessageHandler> mHandler; sp<LooperCallback> mCallback; DisplayEventReceiver mDisplayEventReceiver; @@ -181,14 +196,15 @@ private: int32_t buttonState; std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; - std::vector<sp<Sprite> > recycledSprites; + std::vector<sp<Sprite>> recycledSprites; } mLocked GUARDED_BY(mLock); + PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController); + bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); - void handleMessage(const Message& message); - int handleEvent(int fd, int events, void* data); void doAnimate(nsecs_t timestamp); bool doFadingAnimationLocked(nsecs_t timestamp); bool doBitmapAnimationLocked(nsecs_t timestamp); diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index a15742671dc7..6e129a064385 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -136,7 +136,7 @@ protected: sp<MockSprite> mPointerSprite; sp<MockPointerControllerPolicyInterface> mPolicy; sp<MockSpriteController> mSpriteController; - sp<PointerController> mPointerController; + std::shared_ptr<PointerController> mPointerController; private: void loopThread(); @@ -160,7 +160,7 @@ PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<Moc EXPECT_CALL(*mSpriteController, createSprite()) .WillOnce(Return(mPointerSprite)); - mPointerController = new PointerController(mPolicy, mLooper, mSpriteController); + mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController); } PointerControllerTest::~PointerControllerTest() { @@ -193,7 +193,7 @@ void PointerControllerTest::loopThread() { TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT); EXPECT_CALL(*mPointerSprite, setVisible(true)); @@ -208,7 +208,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { TEST_F(PointerControllerTest, updatePointerIcon) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t type = CURSOR_TYPE_ADDITIONAL; std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type); @@ -224,7 +224,7 @@ TEST_F(PointerControllerTest, updatePointerIcon) { TEST_F(PointerControllerTest, setCustomPointerIcon) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t style = CURSOR_TYPE_CUSTOM; float hotSpotX = 15; @@ -246,13 +246,13 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) { } TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { - mPointerController->setPresentation(PointerController::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); mPointerController->clearSpots(); mPointerController->setPosition(1.0f, 1.0f); mPointerController->move(1.0f, 1.0f); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); - mPointerController->fade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); + mPointerController->fade(PointerController::Transition::IMMEDIATE); EXPECT_TRUE(mPolicy->noResourcesAreLoaded()); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index fed65c971f7e..c0b8e1bf3bbe 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -260,7 +260,9 @@ public class LocationManager { * {@code OP_MONITOR_HIGH_POWER_LOCATION}. * * @hide + * @deprecated This action is unnecessary from Android S forward. */ + @Deprecated public static final String HIGH_POWER_REQUEST_CHANGE_ACTION = "android.location.HIGH_POWER_REQUEST_CHANGE"; @@ -1855,7 +1857,8 @@ public class LocationManager { "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } - getGnssStatusTransportMultiplexer().addListener(listener, DIRECT_EXECUTOR); + getGnssStatusTransportMultiplexer().addListener(listener, + new HandlerExecutor(new Handler())); return true; } @@ -1974,7 +1977,7 @@ public class LocationManager { @Deprecated @RequiresPermission(ACCESS_FINE_LOCATION) public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) { - return addNmeaListener(DIRECT_EXECUTOR, listener); + return addNmeaListener(listener, null); } /** diff --git a/media/java/android/media/MediaFrameworkInitializer.java b/media/java/android/media/MediaFrameworkInitializer.java new file mode 100644 index 000000000000..577442ec42e4 --- /dev/null +++ b/media/java/android/media/MediaFrameworkInitializer.java @@ -0,0 +1,74 @@ +/* + * 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.media; + +import android.annotation.NonNull; +import android.app.SystemServiceRegistry; +import android.content.Context; +import android.media.session.MediaSessionManager; + +import com.android.internal.util.Preconditions; + +import java.util.Objects; + +/** + * Class for performing registration for all media services + * + * TODO (b/160513103): Move this class when moving media service code to APEX + * @hide + */ +public class MediaFrameworkInitializer { + private MediaFrameworkInitializer() { + } + + private static volatile MediaServiceManager sMediaServiceManager; + + /** + * Sets an instance of {@link MediaServiceManager} that allows + * the media mainline module to register/obtain media binder services. This is called + * by the platform during the system initialization. + * + * @param mediaServiceManager instance of {@link MediaServiceManager} that allows + * the media mainline module to register/obtain media binder services. + */ + public static void setMediaServiceManager( + @NonNull MediaServiceManager mediaServiceManager) { + Preconditions.checkState(sMediaServiceManager == null, + "setMediaServiceManager called twice!"); + sMediaServiceManager = Objects.requireNonNull(mediaServiceManager); + } + + /** @hide */ + public static MediaServiceManager getMediaServiceManager() { + return sMediaServiceManager; + } + + /** + * Called by {@link SystemServiceRegistry}'s static initializer and registers all media + * services to {@link Context}, so that {@link Context#getSystemService} can return them. + * + * @throws IllegalStateException if this is called from anywhere besides + * {@link SystemServiceRegistry} + */ + public static void registerServiceWrappers() { + SystemServiceRegistry.registerContextAwareService( + Context.MEDIA_SESSION_SERVICE, + MediaSessionManager.class, + context -> new MediaSessionManager(context) + ); + } +} diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java new file mode 100644 index 000000000000..21e2d84b0200 --- /dev/null +++ b/media/java/android/media/MediaServiceManager.java @@ -0,0 +1,68 @@ +/* + * 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.media; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * Provides a way to register and obtain the system service binder objects managed by the media + * service. + * + * <p> Only the media mainline module will be able to access an instance of this class. + * @hide + */ +public class MediaServiceManager { + /** + * @hide + */ + public MediaServiceManager() {} + + /** + * A class that exposes the methods to register and obtain each system service. + */ + public static final class ServiceRegisterer { + private final String mServiceName; + + /** + * @hide + */ + public ServiceRegisterer(String serviceName) { + mServiceName = serviceName; + } + + /** + * Get the system server binding object for MediaServiceManager. + * + * <p> This blocks until the service instance is ready. + * or a timeout happens, in which case it returns null. + */ + @Nullable + public IBinder get() { + return ServiceManager.getService(mServiceName); + } + } + + /** + * Returns {@link ServiceRegisterer} for the "media_session" service. + */ + @NonNull + public ServiceRegisterer getMediaSessionServiceRegisterer() { + return new ServiceRegisterer("media_session"); + } +} diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 2fd721e6870b..6976a3569655 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -28,14 +28,13 @@ import android.content.Context; import android.content.pm.ParceledListSlice; import android.media.AudioManager; import android.media.IRemoteVolumeController; +import android.media.MediaFrameworkInitializer; import android.media.MediaSession2; import android.media.Session2Token; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.ServiceManager; import android.os.UserHandle; import android.service.media.MediaBrowserService; import android.service.notification.NotificationListenerService; @@ -115,8 +114,10 @@ public final class MediaSessionManager { // Consider rewriting like DisplayManagerGlobal // Decide if we need context mContext = context; - IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE); - mService = ISessionManager.Stub.asInterface(b); + mService = ISessionManager.Stub.asInterface(MediaFrameworkInitializer + .getMediaServiceManager() + .getMediaSessionServiceRegisterer() + .get()); } /** diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 515d610109ab..df022d562768 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -447,7 +447,7 @@ jobjectArray FilterCallback::getMediaEvent( if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) { sp<MediaEvent> mediaEventSp = new MediaEvent(mIFilter, mediaEvent.avMemory, - mediaEvent.avDataId, dataLength, obj); + mediaEvent.avDataId, dataLength + offset, obj); mediaEventSp->mAvHandleRefCnt++; env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get()); mediaEventSp->incStrong(obj); diff --git a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh index b709c5f97b77..43db3530a16b 100644 --- a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh +++ b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh @@ -13,9 +13,6 @@ fi mm -# Push the files onto the device. -. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/push_assets.sh - echo "[==========] waiting for device" adb root && adb wait-for-device remount @@ -27,6 +24,13 @@ echo "[==========] build test apk" mmm -j16 . adb install -r -g ${OUT}/testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk +# Push the files into app's cache directory/ +FILES=$ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/* +for file in $FILES +do +adb push --sync $file /data/user/0/com.android.mediatranscodingtest/cache/ +done + echo "[==========] running real transcoding tests" adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java index e5952fb44ac1..3a5e69293a02 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java @@ -67,7 +67,7 @@ public class MediaTranscodeManagerTest // Setting for transcoding to H.264. private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC; - private static final int BIT_RATE = 2000000; // 2Mbps + private static final int BIT_RATE = 20000000; // 20Mbps private static final int WIDTH = 1920; private static final int HEIGHT = 1080; @@ -177,7 +177,7 @@ public class MediaTranscodeManagerTest assertNotNull(job); if (job != null) { - Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete."); + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel."); boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue("Transcode failed to complete in time.", finishedOnTime); @@ -192,5 +192,43 @@ public class MediaTranscodeManagerTest stats.mAveragePSNR >= PSNR_THRESHOLD); } + @Test + public void testCancelTranscoding() throws Exception { + Log.d(TAG, "Starting: testMediaTranscodeManager"); + Semaphore transcodeCompleteSemaphore = new Semaphore(0); + + // Transcode a 15 seconds video, so that the transcoding is not finished when we cancel. + Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4"); + Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4"); + + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(srcUri) + .setDestinationUri(destinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + Executor listenerExecutor = Executors.newSingleThreadExecutor(); + + TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, + transcodingJob -> { + Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult()); + assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_CANCELED); + transcodeCompleteSemaphore.release(); + }); + assertNotNull(job); + + // TODO(hkuang): Wait for progress update before calling cancel to make sure transcoding is + // started. + + job.cancel(); + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel."); + boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( + 30, TimeUnit.MILLISECONDS); + assertTrue("Fails to cancel transcoding", finishedOnTime); + } } diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java new file mode 100644 index 000000000000..ee06720327bc --- /dev/null +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java @@ -0,0 +1,182 @@ +/* + * 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.mediatranscodingtest; + +import android.content.ContentResolver; +import android.content.Context; +import android.media.MediaFormat; +import android.media.MediaTranscodeManager; +import android.media.MediaTranscodeManager.TranscodingJob; +import android.media.MediaTranscodeManager.TranscodingRequest; +import android.net.Uri; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/* + * Benchmarking for MediaTranscodeManager in the media framework. + * + * Note: This benchmarking requires to push all the files from http://go/transcodingbenchmark + * to /data/user/0/com.android.mediatranscodingtest/cache/ directory after installing the apk. + * + * TODO(hkuang): Change it to download from server automatically instead of manually. + * + * To run this test suite: + make frameworks/base/media/tests/MediaTranscodingTest + make mediatranscodingtest + + adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk + // Push the files to /data/user/0/com.android.mediatranscodingtest/cache/ + adb push $DOWNLOADPATH/*.mp4 /data/user/0/com.android.mediatranscodingtest/cache/ + + adb shell am instrument -e class \ + com.android.mediatranscodingtest.MediaTranscodingBenchmark \ + -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner + * + */ +public class MediaTranscodingBenchmark + extends ActivityInstrumentationTestCase2<MediaTranscodingTest> { + private static final String TAG = "MediaTranscodingBenchmark"; + // TODO(hkuang): Change this to query from MediaCodecInfo.CodecCapabilities for different + // resolution. + private static final int MINIMUM_TRANSCODING_FPS = 80; + private static final int LOOP_COUNT = 10; + // Default Setting for transcoding to H.264. + private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC; + private static final int BIT_RATE = 20000000; // 20Mbps + private static final int WIDTH = 1920; + private static final int HEIGHT = 1080; + private Context mContext; + private MediaTranscodeManager mMediaTranscodeManager = null; + + public MediaTranscodingBenchmark() { + super("com.android.MediaTranscodingBenchmark", MediaTranscodingTest.class); + } + + /** + * Creates a MediaFormat with the basic set of values. + */ + private static MediaFormat createMediaFormat() { + MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT); + format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); + return format; + } + + @Override + public void setUp() throws Exception { + Log.d(TAG, "setUp"); + super.setUp(); + mContext = getInstrumentation().getContext(); + mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + /* + * Transcode the sourceFileName to destinationFileName with LOOP_COUNT. + */ + private void transcode(final String sourceFileName, final String destinationFileName) + throws IOException, InterruptedException { + AtomicLong totalTimeMs = new AtomicLong(); + AtomicLong transcodingTime = new AtomicLong(); + Uri srcUri = getUri(sourceFileName); + Uri dstUri = getUri(destinationFileName); + + MediaTranscodingTestUtil.VideoFileInfo info = + MediaTranscodingTestUtil.extractVideoFileInfo(mContext, getUri(sourceFileName)); + int timeoutSeconds = calMaxTranscodingWaitTimeSeconds(info.mNumVideoFrames, + MINIMUM_TRANSCODING_FPS); + Log.d(TAG, "Start Transcoding " + info.toString() + " " + timeoutSeconds); + + for (int loop = 0; loop < LOOP_COUNT; ++loop) { + Semaphore transcodeCompleteSemaphore = new Semaphore(0); + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(srcUri) + .setDestinationUri(dstUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + Executor listenerExecutor = Executors.newSingleThreadExecutor(); + + long startTimeMs = System.currentTimeMillis(); + TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, + transcodingJob -> { + Log.d(TAG, + "Transcoding completed with result: " + transcodingJob.getResult()); + assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS); + transcodeCompleteSemaphore.release(); + transcodingTime.set(System.currentTimeMillis() - startTimeMs); + totalTimeMs.addAndGet(transcodingTime.get()); + }); + + if (job != null) { + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete."); + boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( + timeoutSeconds, TimeUnit.SECONDS); + assertTrue("Transcode failed to complete in time.", finishedOnTime); + } + Log.i(TAG, "Loop: " + loop + " take " + transcodingTime.get() + " ms "); + } + + float fps = info.mNumVideoFrames * 1000 * LOOP_COUNT / totalTimeMs.get(); + Log.i(TAG, "Transcoding " + info.toString() + " Transcoding fps: " + fps); + } + + // Calculate the maximum wait time based on minimum transcoding throughput and frame number. + private int calMaxTranscodingWaitTimeSeconds(int numberFrames, int minTranscodingFps) { + int waitTimeSeconds = numberFrames / minTranscodingFps; + // If waitTimeSeconds is 0, wait for 1 seconds at least. + return waitTimeSeconds == 0 ? 1 : waitTimeSeconds; + } + + private Uri getUri(final String fileName) { + String path = mContext.getCacheDir().getAbsolutePath(); + return new Uri.Builder().scheme(ContentResolver.SCHEME_FILE).appendPath(path).appendPath( + fileName).build(); + } + + @Test + public void testBenchmarkingAVCToAVCWith66FramesWithoutAudio() throws Exception { + String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps"; + String testVideoName = videoNameWithoutExtension + ".mp4"; + String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4"; + + transcode(testVideoName, transcodedVideoName); + } + + @Test + public void testBenchmarkingAVCToAVCWith66FramesWithAudio() throws Exception { + String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps_aac"; + String testVideoName = videoNameWithoutExtension + ".mp4"; + String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4"; + + transcode(testVideoName, transcodedVideoName); + } +} diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java index 3b044c7e3707..53b23927fc64 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java @@ -38,6 +38,7 @@ public class MediaTranscodingTestRunner extends InstrumentationTestRunner { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(MediaTranscodeManagerTest.class); suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class); + suite.addTestSuite(MediaTranscodingBenchmark.class); return suite; } diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java index a1c32512f94a..69f124f42abd 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java @@ -27,6 +27,7 @@ import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaExtractor; import android.media.MediaFormat; +import android.media.MediaMetadataRetriever; import android.net.Uri; import android.util.Log; import android.util.Size; @@ -42,6 +43,75 @@ import java.util.Locale; /* package */ class MediaTranscodingTestUtil { private static final String TAG = "MediaTranscodingTestUtil"; + // Helper class to extract the information from source file and transcoded file. + static class VideoFileInfo { + String mUri; + int mNumVideoFrames = 0; + int mWidth = 0; + int mHeight = 0; + float mVideoFrameRate = 0.0f; + boolean mHasAudio = false; + + public String toString() { + String str = mUri; + str += " Width:" + mWidth; + str += " Height:" + mHeight; + str += " FrameRate:" + mWidth; + str += " FrameCount:" + mNumVideoFrames; + str += " HasAudio:" + (mHasAudio ? "Yes" : "No"); + return str; + } + } + + static VideoFileInfo extractVideoFileInfo(Context ctx, Uri videoUri) throws IOException { + VideoFileInfo info = new VideoFileInfo(); + AssetFileDescriptor afd = null; + MediaMetadataRetriever retriever = null; + + try { + afd = ctx.getContentResolver().openAssetFileDescriptor(videoUri, "r"); + retriever = new MediaMetadataRetriever(); + retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + + info.mUri = videoUri.getLastPathSegment(); + Log.i(TAG, "Trying to transcode to " + info.mUri); + String width = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); + String height = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + if (width != null && height != null) { + info.mWidth = Integer.parseInt(width); + info.mHeight = Integer.parseInt(height); + } + + String frameRate = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE); + if (frameRate != null) { + info.mVideoFrameRate = Float.parseFloat(frameRate); + } + + String frameCount = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT); + if (frameCount != null) { + info.mNumVideoFrames = Integer.parseInt(frameCount); + } + + String hasAudio = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO); + if (hasAudio != null) { + info.mHasAudio = hasAudio.equals("yes"); + } + } finally { + if (retriever != null) { + retriever.close(); + } + if (afd != null) { + afd.close(); + } + } + return info; + } + static VideoTranscodingStatistics computeStats(final Context ctx, final Uri sourceMp4, final Uri transcodedMp4) throws Exception { diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 32b33a758535..8598f74e1441 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -49,7 +49,7 @@ android_library { "androidx.lifecycle_lifecycle-extensions", "SystemUI-tags", "SystemUI-proto", - "dagger2-2.19", + "dagger2", "//external/kotlinc:kotlin-annotations", ], @@ -59,7 +59,7 @@ android_library { manifest: "AndroidManifest.xml", - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } @@ -104,7 +104,7 @@ android_library { "mockito-target-inline-minus-junit4", "testables", "truth-prebuilt", - "dagger2-2.19", + "dagger2", "//external/kotlinc:kotlin-annotations", ], libs: [ @@ -118,7 +118,7 @@ android_library { "com.android.systemui", ], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } android_app { @@ -157,7 +157,7 @@ android_app { kotlincflags: ["-Xjvm-default=enable"], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], required: ["privapp_whitelist_com.android.systemui"], } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 5bf989a971b9..496742680893 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -67,6 +67,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.volume.VolumeDialogComponent; +import com.android.systemui.wm.DisplayImeController; +import com.android.systemui.wm.DisplaySystemBarsController; import javax.inject.Named; import javax.inject.Singleton; @@ -97,6 +99,10 @@ public abstract class CarSystemUIModule { groupManager, configurationController); } + @Binds + abstract DisplayImeController bindDisplayImeController( + DisplaySystemBarsController displaySystemBarsController); + @Singleton @Provides @Named(LEAK_REPORT_EMAIL_NAME) diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java new file mode 100644 index 000000000000..5f9665ff7632 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java @@ -0,0 +1,250 @@ +/* + * 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.wm; + +import android.car.settings.CarSettings; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.Slog; +import android.view.WindowInsets; + +import androidx.annotation.VisibleForTesting; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Util class to load PolicyControl and allow for querying if a package matches immersive filters. + * Similar to {@link com.android.server.wm.PolicyControl}, but separate due to CarSystemUI needing + * to set its own policies for system bar visibilities. + * + * This forces immersive mode behavior for one or both system bars (based on a package + * list). + * + * Control by setting {@link Settings.Global#POLICY_CONTROL_AUTO} to one or more name-value pairs. + * e.g. + * to force immersive mode everywhere: + * "immersive.full=*" + * to force hide status bars for com.package1 but not com.package2: + * "immersive.status=com.package1,-com.package2" + * + * Separate multiple name-value pairs with ':' + * e.g. "immersive.status=com.package:immersive.navigation=*" + */ +public class BarControlPolicy { + + private static final String TAG = "BarControlPolicy"; + private static final boolean DEBUG = false; + + private static final String NAME_IMMERSIVE_FULL = "immersive.full"; + private static final String NAME_IMMERSIVE_STATUS = "immersive.status"; + private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation"; + + @VisibleForTesting + static String sSettingValue; + @VisibleForTesting + static Filter sImmersiveStatusFilter; + private static Filter sImmersiveNavigationFilter; + + /** Loads values from the POLICY_CONTROL setting to set filters. */ + static boolean reloadFromSetting(Context context) { + if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); + String value = null; + try { + value = Settings.Global.getStringForUser(context.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + UserHandle.USER_CURRENT); + if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) { + return false; + } + setFilters(value); + sSettingValue = value; + } catch (Throwable t) { + Slog.w(TAG, "Error loading policy control, value=" + value, t); + return false; + } + return true; + } + + /** Used in testing to reset BarControlPolicy. */ + @VisibleForTesting + static void reset() { + sSettingValue = null; + sImmersiveStatusFilter = null; + sImmersiveNavigationFilter = null; + } + + /** + * Registers a content observer to listen to updates to the SYSTEM_BAR_VISIBILITY_OVERRIDE flag. + */ + static void registerContentObserver(Context context, Handler handler, FilterListener listener) { + context.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE), false, + new ContentObserver(handler) { + @Override + public void onChange(boolean selfChange) { + if (reloadFromSetting(context)) { + listener.onFilterUpdated(); + } + } + }, UserHandle.USER_ALL); + } + + /** + * Returns bar visibilities based on POLICY_CONTROL_AUTO filters and window policies. + * @return int[], where the first value is the inset types that should be shown, and the second + * is the inset types that should be hidden. + */ + @WindowInsets.Type.InsetsType + static int[] getBarVisibilities(String packageName) { + int hideTypes = 0; + int showTypes = 0; + if (matchesStatusFilter(packageName)) { + hideTypes |= WindowInsets.Type.statusBars(); + } else { + showTypes |= WindowInsets.Type.statusBars(); + } + if (matchesNavigationFilter(packageName)) { + hideTypes |= WindowInsets.Type.navigationBars(); + } else { + showTypes |= WindowInsets.Type.navigationBars(); + } + + return new int[] {showTypes, hideTypes}; + } + + private static boolean matchesStatusFilter(String packageName) { + return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName); + } + + private static boolean matchesNavigationFilter(String packageName) { + return sImmersiveNavigationFilter != null + && sImmersiveNavigationFilter.matches(packageName); + } + + private static void setFilters(String value) { + if (DEBUG) Slog.d(TAG, "setFilters: " + value); + sImmersiveStatusFilter = null; + sImmersiveNavigationFilter = null; + if (value != null) { + String[] nvps = value.split(":"); + for (String nvp : nvps) { + int i = nvp.indexOf('='); + if (i == -1) continue; + String n = nvp.substring(0, i); + String v = nvp.substring(i + 1); + if (n.equals(NAME_IMMERSIVE_FULL)) { + Filter f = Filter.parse(v); + sImmersiveStatusFilter = sImmersiveNavigationFilter = f; + } else if (n.equals(NAME_IMMERSIVE_STATUS)) { + Filter f = Filter.parse(v); + sImmersiveStatusFilter = f; + } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) { + Filter f = Filter.parse(v); + sImmersiveNavigationFilter = f; + } + } + } + if (DEBUG) { + Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter); + Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter); + } + } + + private static class Filter { + private static final String ALL = "*"; + + private final ArraySet<String> mWhitelist; + private final ArraySet<String> mBlacklist; + + private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { + mWhitelist = whitelist; + mBlacklist = blacklist; + } + + boolean matches(String packageName) { + if (packageName == null) return false; + if (onBlacklist(packageName)) return false; + return onWhitelist(packageName); + } + + private boolean onBlacklist(String packageName) { + return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + } + + private boolean onWhitelist(String packageName) { + return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + } + + void dump(PrintWriter pw) { + pw.print("Filter["); + dump("whitelist", mWhitelist, pw); pw.print(','); + dump("blacklist", mBlacklist, pw); pw.print(']'); + } + + private void dump(String name, ArraySet<String> set, PrintWriter pw) { + pw.print(name); pw.print("=("); + int n = set.size(); + for (int i = 0; i < n; i++) { + if (i > 0) pw.print(','); + pw.print(set.valueAt(i)); + } + pw.print(')'); + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + dump(new PrintWriter(sw, true)); + return sw.toString(); + } + + // value = comma-delimited list of tokens, where token = (package name|*) + // e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*" + static Filter parse(String value) { + if (value == null) return null; + ArraySet<String> whitelist = new ArraySet<String>(); + ArraySet<String> blacklist = new ArraySet<String>(); + for (String token : value.split(",")) { + token = token.trim(); + if (token.startsWith("-") && token.length() > 1) { + token = token.substring(1); + blacklist.add(token); + } else { + whitelist.add(token); + } + } + return new Filter(whitelist, blacklist); + } + } + + /** + * Interface to listen for updates to the filter triggered by the content observer listening to + * the SYSTEM_BAR_VISIBILITY_OVERRIDE flag. + */ + interface FilterListener { + + /** Callback triggered when the content observer updates the filter. */ + void onFilterUpdated(); + } + + private BarControlPolicy() {} +} diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java new file mode 100644 index 000000000000..a831464e7987 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java @@ -0,0 +1,168 @@ +/* + * 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.wm; + +import android.os.Handler; +import android.os.RemoteException; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; +import android.view.IDisplayWindowInsetsController; +import android.view.InsetsController; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.WindowInsets; + +import androidx.annotation.VisibleForTesting; + +import com.android.systemui.TransactionPool; +import com.android.systemui.dagger.qualifiers.Main; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to + * give system bar control to SystemUI. + * {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller + * takes control or not. + */ +@Singleton +public class DisplaySystemBarsController extends DisplayImeController { + + private static final String TAG = "DisplaySystemBarsController"; + + private SparseArray<PerDisplay> mPerDisplaySparseArray; + + @Inject + public DisplaySystemBarsController( + SystemWindows syswin, + DisplayController displayController, + @Main Handler mainHandler, + TransactionPool transactionPool) { + super(syswin, displayController, mainHandler, transactionPool); + } + + @Override + public void onDisplayAdded(int displayId) { + PerDisplay pd = new PerDisplay(displayId); + try { + mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to set insets controller on display " + displayId); + } + // Lazy loading policy control filters instead of during boot. + if (mPerDisplaySparseArray == null) { + mPerDisplaySparseArray = new SparseArray<>(); + BarControlPolicy.reloadFromSetting(mSystemWindows.mContext); + BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> { + int size = mPerDisplaySparseArray.size(); + for (int i = 0; i < size; i++) { + mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets(); + } + }); + } + mPerDisplaySparseArray.put(displayId, pd); + } + + @Override + public void onDisplayRemoved(int displayId) { + try { + mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to remove insets controller on display " + displayId); + } + mPerDisplaySparseArray.remove(displayId); + } + + @VisibleForTesting + class PerDisplay extends IDisplayWindowInsetsController.Stub { + + int mDisplayId; + InsetsController mInsetsController; + InsetsState mInsetsState = new InsetsState(); + String mPackageName; + + PerDisplay(int displayId) { + mDisplayId = displayId; + mInsetsController = new InsetsController( + new DisplaySystemBarsInsetsControllerHost(mHandler, this)); + } + + @Override + public void insetsChanged(InsetsState insetsState) { + if (mInsetsState.equals(insetsState)) { + return; + } + mInsetsState.set(insetsState, true /* copySources */); + mInsetsController.onStateChanged(insetsState); + if (mPackageName != null) { + modifyDisplayWindowInsets(); + } + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] activeControls) { + mInsetsController.onControlsChanged(activeControls); + } + + @Override + public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { + mInsetsController.hide(types); + } + + @Override + public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { + mInsetsController.show(types); + } + + @Override + public void topFocusedWindowChanged(String packageName) { + // If both package names are null or both package names are equal, return. + if (mPackageName == packageName + || (mPackageName != null && mPackageName.equals(packageName))) { + return; + } + mPackageName = packageName; + modifyDisplayWindowInsets(); + } + + private void modifyDisplayWindowInsets() { + if (mPackageName == null) { + return; + } + int[] barVisibilities = BarControlPolicy.getBarVisibilities(mPackageName); + updateInsetsState(barVisibilities[0], /* visible= */ true); + updateInsetsState(barVisibilities[1], /* visible= */ false); + showInsets(barVisibilities[0], /* fromIme= */ false); + hideInsets(barVisibilities[1], /* fromIme= */ false); + try { + mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to update window manager service."); + } + } + + private void updateInsetsState(@WindowInsets.Type.InsetsType int types, boolean visible) { + ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); + for (int i = internalTypes.size() - 1; i >= 0; i--) { + mInsetsState.getSource(internalTypes.valueAt(i)).setVisible(visible); + } + } + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java new file mode 100644 index 000000000000..2f8da44ba851 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java @@ -0,0 +1,173 @@ +/* + * 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.wm; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.IDisplayWindowInsetsController; +import android.view.InsetsController; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; +import android.view.WindowInsetsController; +import android.view.inputmethod.InputMethodManager; + +import java.util.List; + +/** + * Implements {@link InsetsController.Host} for usage by + * {@link DisplaySystemBarsController.PerDisplay} instances in {@link DisplaySystemBarsController}. + * @hide + */ +public class DisplaySystemBarsInsetsControllerHost implements InsetsController.Host { + + private static final String TAG = DisplaySystemBarsInsetsControllerHost.class.getSimpleName(); + + private final Handler mHandler; + private final IDisplayWindowInsetsController mController; + private final float[] mTmpFloat9 = new float[9]; + + public DisplaySystemBarsInsetsControllerHost( + Handler handler, IDisplayWindowInsetsController controller) { + mHandler = handler; + mController = controller; + } + + @Override + public Handler getHandler() { + return mHandler; + } + + @Override + public void notifyInsetsChanged() { + // no-op + } + + @Override + public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) { + // no-op + } + + @Override + public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart( + @NonNull WindowInsetsAnimation animation, + @NonNull WindowInsetsAnimation.Bounds bounds) { + return null; + } + + @Override + public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, + @NonNull List<WindowInsetsAnimation> runningAnimations) { + return null; + } + + @Override + public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) { + // no-op + } + + @Override + public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { + for (int i = params.length - 1; i >= 0; i--) { + SyncRtSurfaceTransactionApplier.applyParams( + new SurfaceControl.Transaction(), params[i], mTmpFloat9); + } + + } + + @Override + public void updateCompatSysUiVisibility( + @InsetsState.InternalInsetsType int type, boolean visible, boolean hasControl) { + // no-op + } + + @Override + public void onInsetsModified(InsetsState insetsState) { + try { + mController.insetsChanged(insetsState); + } catch (RemoteException e) { + Log.e(TAG, "Failed to send insets to controller"); + } + } + + @Override + public boolean hasAnimationCallbacks() { + return false; + } + + @Override + public void setSystemBarsAppearance( + @WindowInsetsController.Appearance int appearance, + @WindowInsetsController.Appearance int mask) { + // no-op + } + + @Override + public @WindowInsetsController.Appearance int getSystemBarsAppearance() { + return 0; + } + + @Override + public void setSystemBarsBehavior(@WindowInsetsController.Behavior int behavior) { + // no-op + } + + @Override + public @WindowInsetsController.Behavior int getSystemBarsBehavior() { + return 0; + } + + @Override + public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) { + surfaceControl.release(); + } + + @Override + public void addOnPreDrawRunnable(Runnable r) { + mHandler.post(r); + } + + @Override + public void postInsetsAnimationCallback(Runnable r) { + mHandler.post(r); + } + + @Override + public InputMethodManager getInputMethodManager() { + return null; + } + + @Override + public String getRootViewTitle() { + return null; + } + + @Override + public int dipToPx(int dips) { + return 0; + } + + @Override + public IBinder getWindowToken() { + return null; + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index eca51e34995c..232df2eced39 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -24,6 +24,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadsetClient; import android.content.Intent; import android.os.Handler; @@ -44,14 +46,19 @@ import org.junit.runner.RunWith; public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private static final String BLUETOOTH_REMOTE_ADDRESS = "00:11:22:33:44:55"; private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier; + private TestableLooper mTestableLooper; private Handler mTestHandler; + private BluetoothDevice mBluetoothDevice; @Before public void setUp() throws Exception { - TestableLooper testableLooper = TestableLooper.get(this); - mTestHandler = spy(new Handler(testableLooper.getLooper())); + mTestableLooper = TestableLooper.get(this); + mTestHandler = spy(new Handler(mTestableLooper.getLooper())); + mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + BLUETOOTH_REMOTE_ADDRESS); mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier( mContext, mTestHandler); mVoiceRecognitionNotifier.onBootCompleted(); @@ -61,8 +68,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { public void testReceiveIntent_started_showToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, VOICE_RECOGNITION_STARTED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler).post(any()); } @@ -71,8 +80,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { public void testReceiveIntent_invalidExtra_noToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, INVALID_VALUE); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler, never()).post(any()); } @@ -80,8 +91,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { @Test public void testReceiveIntent_noExtra_noToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler, never()).post(any()); } @@ -89,8 +102,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { @Test public void testReceiveIntent_invalidIntent_noToast() { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - waitForIdleSync(); + mTestableLooper.processAllMessages(); verify(mTestHandler, never()).post(any()); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java new file mode 100644 index 000000000000..da7cb8e4f6ac --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java @@ -0,0 +1,195 @@ +/* + * 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.wm; + +import static android.view.WindowInsets.Type.navigationBars; +import static android.view.WindowInsets.Type.statusBars; + +import static com.google.common.truth.Truth.assertThat; + +import android.car.settings.CarSettings; +import android.provider.Settings; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class BarControlPolicyTest extends SysuiTestCase { + + private static final String PACKAGE_NAME = "sample.app"; + + @Before + public void setUp() { + BarControlPolicy.reset(); + } + + @After + public void tearDown() { + Settings.Global.clearProviderForTest(); + } + + @Test + public void reloadFromSetting_notSet_doesNotSetFilters() { + BarControlPolicy.reloadFromSetting(mContext); + + assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull(); + } + + @Test + public void reloadFromSetting_invalidPolicyControlString_doesNotSetFilters() { + String text = "sample text"; + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + text + ); + + BarControlPolicy.reloadFromSetting(mContext); + + assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull(); + } + + @Test + public void reloadFromSetting_validPolicyControlString_setsFilters() { + String text = "immersive.status=" + PACKAGE_NAME; + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + text + ); + + BarControlPolicy.reloadFromSetting(mContext); + + assertThat(BarControlPolicy.sImmersiveStatusFilter).isNotNull(); + } + + @Test + public void reloadFromSetting_filtersSet_doesNotSetFiltersAgain() { + String text = "immersive.status=" + PACKAGE_NAME; + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + text + ); + + BarControlPolicy.reloadFromSetting(mContext); + + assertThat(BarControlPolicy.reloadFromSetting(mContext)).isFalse(); + } + + @Test + public void getBarVisibilities_policyControlNotSet_showsSystemBars() { + int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME); + + assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars()); + assertThat(visibilities[1]).isEqualTo(0); + } + + @Test + public void getBarVisibilities_immersiveStatusForAppAndMatchingApp_hidesStatusBar() { + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + "immersive.status=" + PACKAGE_NAME); + BarControlPolicy.reloadFromSetting(mContext); + + int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME); + + assertThat(visibilities[0]).isEqualTo(navigationBars()); + assertThat(visibilities[1]).isEqualTo(statusBars()); + } + + @Test + public void getBarVisibilities_immersiveStatusForAppAndNonMatchingApp_showsSystemBars() { + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + "immersive.status=" + PACKAGE_NAME); + BarControlPolicy.reloadFromSetting(mContext); + + int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app"); + + assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars()); + assertThat(visibilities[1]).isEqualTo(0); + } + + @Test + public void getBarVisibilities_immersiveStatusForAppsAndNonApp_showsSystemBars() { + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + "immersive.status=apps"); + BarControlPolicy.reloadFromSetting(mContext); + + int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME); + + assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars()); + assertThat(visibilities[1]).isEqualTo(0); + } + + @Test + public void getBarVisibilities_immersiveFullForAppAndMatchingApp_hidesSystemBars() { + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + "immersive.full=" + PACKAGE_NAME); + BarControlPolicy.reloadFromSetting(mContext); + + int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME); + + assertThat(visibilities[0]).isEqualTo(0); + assertThat(visibilities[1]).isEqualTo(statusBars() | navigationBars()); + } + + @Test + public void getBarVisibilities_immersiveFullForAppAndNonMatchingApp_showsSystemBars() { + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + "immersive.full=" + PACKAGE_NAME); + BarControlPolicy.reloadFromSetting(mContext); + + int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app"); + + assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars()); + assertThat(visibilities[1]).isEqualTo(0); + } + + @Test + public void getBarVisibilities_immersiveFullForAppsAndNonApp_showsSystemBars() { + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + "immersive.full=apps"); + BarControlPolicy.reloadFromSetting(mContext); + + int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME); + + assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars()); + assertThat(visibilities[1]).isEqualTo(0); + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java new file mode 100644 index 000000000000..29cc8eec4bc3 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java @@ -0,0 +1,111 @@ +/* + * 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.wm; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; + +import android.car.settings.CarSettings; +import android.os.Handler; +import android.os.RemoteException; +import android.provider.Settings; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.IWindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.TransactionPool; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class DisplaySystemBarsControllerTest extends SysuiTestCase { + + private DisplaySystemBarsController mController; + + private static final int DISPLAY_ID = 1; + + @Mock + private SystemWindows mSystemWindows; + @Mock + private IWindowManager mIWindowManager; + @Mock + private DisplayController mDisplayController; + @Mock + private Handler mHandler; + @Mock + private TransactionPool mTransactionPool; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mSystemWindows.mContext = mContext; + mSystemWindows.mWmService = mIWindowManager; + + mController = new DisplaySystemBarsController( + mSystemWindows, + mDisplayController, + mHandler, + mTransactionPool + ); + } + + @Test + public void onDisplayAdded_setsDisplayWindowInsetsControllerOnWMService() + throws RemoteException { + mController.onDisplayAdded(DISPLAY_ID); + + verify(mIWindowManager).setDisplayWindowInsetsController( + eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class)); + } + + @Test + public void onDisplayAdded_loadsBarControlPolicyFilters() { + String text = "sample text"; + Settings.Global.putString( + mContext.getContentResolver(), + CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, + text + ); + + mController.onDisplayAdded(DISPLAY_ID); + + assertThat(BarControlPolicy.sSettingValue).isEqualTo(text); + } + + @Test + public void onDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService() + throws RemoteException { + mController.onDisplayAdded(DISPLAY_ID); + + mController.onDisplayRemoved(DISPLAY_ID); + + verify(mIWindowManager).setDisplayWindowInsetsController( + DISPLAY_ID, /* displayWindowInsetsController= */ null); + } +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java index 5a51ac22b88f..973ab8956304 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java @@ -239,8 +239,8 @@ public class UninstallFinish extends BroadcastReceiver { builder.addAction((new Notification.Action.Builder( Icon.createWithResource(context, R.drawable.ic_settings_multiuser), context.getString(R.string.manage_users), - PendingIntent.getActivity(context, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT))).build()); + PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_IMMUTABLE))).build()); } /** @@ -259,7 +259,7 @@ public class UninstallFinish extends BroadcastReceiver { builder.addAction((new Notification.Action.Builder( Icon.createWithResource(context, R.drawable.ic_lock), context.getString(R.string.manage_device_administrators), - PendingIntent.getActivity(context, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT))).build()); + PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_IMMUTABLE))).build()); } } diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java index 9ae31989eeb2..5a756fe50209 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java @@ -46,7 +46,7 @@ public class RecommendationServiceImpl extends RecommendationService private static final String LOG_TAG = "PrintServiceRecService"; /** All registered plugins */ - private ArrayList<RemotePrintServicePlugin> mPlugins; + private final ArrayList<RemotePrintServicePlugin> mPlugins = new ArrayList<>(); /** Lock to keep multi-cast enabled */ private WifiManager.MulticastLock mMultiCastLock; @@ -62,8 +62,6 @@ public class RecommendationServiceImpl extends RecommendationService mMultiCastLock.acquire(); } - mPlugins = new ArrayList<>(); - try { for (VendorConfig config : VendorConfig.getAllConfigs(this)) { try { @@ -138,6 +136,7 @@ public class RecommendationServiceImpl extends RecommendationService Log.e(LOG_TAG, "Could not stop plugin", e); } } + mPlugins.clear(); if (mMultiCastLock != null) { mMultiCastLock.release(); diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java index 3565b0e3a9ae..e82997431290 100644 --- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java +++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java @@ -16,9 +16,6 @@ package com.android.settingslib.widget; -import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED; -import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_DEFAULT; - import android.annotation.ColorInt; import android.content.res.Resources; import android.graphics.Bitmap; @@ -48,11 +45,12 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper { private static final float ADVANCED_ICON_CENTER = 50f; private static final float ADVANCED_ICON_RADIUS = 48f; + public static final int ICON_TYPE_DEFAULT = 0; + public static final int ICON_TYPE_ADVANCED = 1; + @Retention(RetentionPolicy.SOURCE) - @IntDef({TYPE_DEFAULT, TYPE_ADVANCED}) + @IntDef({ICON_TYPE_DEFAULT, ICON_TYPE_ADVANCED}) public @interface AdaptiveOutlineIconType { - int TYPE_DEFAULT = 0; - int TYPE_ADVANCED = 1; } @VisibleForTesting @@ -66,7 +64,7 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper { public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) { super(new AdaptiveIconShapeDrawable(resources)); - init(resources, bitmap, TYPE_DEFAULT); + init(resources, bitmap, ICON_TYPE_DEFAULT); } public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap, @@ -96,10 +94,10 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper { private @ColorInt int getColor(Resources resources, @AdaptiveOutlineIconType int type) { int resId; switch (type) { - case TYPE_ADVANCED: + case ICON_TYPE_ADVANCED: resId = R.color.advanced_outline_color; break; - case TYPE_DEFAULT: + case ICON_TYPE_DEFAULT: default: resId = R.color.bt_outline_color; break; @@ -110,10 +108,10 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper { private int getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type) { int resId; switch (type) { - case TYPE_ADVANCED: + case ICON_TYPE_ADVANCED: resId = R.dimen.advanced_dashboard_tile_foreground_image_inset; break; - case TYPE_DEFAULT: + case ICON_TYPE_DEFAULT: default: resId = R.dimen.dashboard_tile_foreground_image_inset; break; @@ -133,7 +131,7 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper { final int count = canvas.save(); canvas.scale(scaleX, scaleY); // Draw outline - if (mType == TYPE_DEFAULT) { + if (mType == ICON_TYPE_DEFAULT) { canvas.drawPath(mPath, mOutlinePaint); } else { canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS, diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml index fd89676ab6d7..96d9e518663f 100644 --- a/packages/SettingsLib/Utils/AndroidManifest.xml +++ b/packages/SettingsLib/Utils/AndroidManifest.xml @@ -16,7 +16,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.settingslib.utils"> + package="com.android.settingslib.widget"> <uses-sdk android:minSdkVersion="21" /> diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java index 6125b8509fcf..e7c0d9659d11 100644 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java @@ -22,7 +22,7 @@ import android.content.pm.PackageManager; import android.os.UserManager; import android.util.Log; -import com.android.settingslib.utils.R; +import com.android.settingslib.widget.R; public class AppUtils { diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index c27973f8bc86..287a1aca2fbd 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -22,7 +22,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se pueden buscar las redes."</string> <string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string> - <string name="wifi_remembered" msgid="3266709779723179188">"Guardada"</string> + <string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string> <string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string> <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración IP"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 3e8b1c1b9cde..f9d57c453f69 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -37,7 +37,7 @@ <string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string> - <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de rede"</string> + <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> <string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 08cf5f9fd5a9..8407db6d3a08 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -211,8 +211,8 @@ <string name="adb_wireless_error" msgid="721958772149779856">"Алдаа"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Wireless debugging"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Боломжтой төхөөрөмжүүдийг харах болох ашиглахын тулд wireless debugging-г асаана уу"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Хурдан хариу үйлдлийн кодоор төхөөрөмжийг хослуул"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Хурдан хариу үйлдлийн кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR кодоор төхөөрөмжийг хослуул"</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Хослуулах кодоор төхөөрөмжийг хослуулна уу"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Зургаан оронтой кодыг ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Хослуулсан төхөөрөмжүүд"</string> @@ -226,12 +226,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi хослуулах код"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Хослуулалт амжилтгүй боллоо"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Төхөөрөмжийг ижил сүлжээнд холбосон эсэхийг шалгана уу."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Төхөөрөмжийг хослуулж байна…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. Хурдан хариу үйлдлийн код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. QR код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP хаяг ба порт"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Хурдан хариу үйлдлийн кодыг скан хийх"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодыг скан хийх"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi сүлжээнд холбогдоно уу"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, дебаг хийх, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Алдаа мэдээлэх товчлол"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index e33df4ad7d1c..32cc39ec5b4f 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -37,7 +37,7 @@ <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string> <string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string> - <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via netwerkbeoordelaar"</string> + <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string> <string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 95e916b9871a..68f72896c251 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -1,6 +1,6 @@ package com.android.settingslib.bluetooth; -import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED; +import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -238,7 +238,7 @@ public class BluetoothUtils { final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, false); bitmap.recycle(); - return new AdaptiveOutlineDrawable(resources, resizedBitmap, TYPE_ADVANCED); + return new AdaptiveOutlineDrawable(resources, resizedBitmap, ICON_TYPE_ADVANCED); } return drawable; diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java index 278b57da0c28..e5ea4467517b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java @@ -41,7 +41,7 @@ public class CircleFramedDrawable extends Drawable { private final Bitmap mBitmap; private final int mSize; - private final Paint mPaint; + private Paint mIconPaint; private float mScale; private Rect mSrcRect; @@ -75,18 +75,18 @@ public class CircleFramedDrawable extends Drawable { canvas.drawColor(0, PorterDuff.Mode.CLEAR); // opaque circle matte - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mPaint.setColor(Color.BLACK); - mPaint.setStyle(Paint.Style.FILL); - canvas.drawPath(fillPath, mPaint); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.BLACK); + paint.setStyle(Paint.Style.FILL); + canvas.drawPath(fillPath, paint); // mask in the icon where the bitmap is opaque - mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(icon, cropRect, circleRect, mPaint); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(icon, cropRect, circleRect, paint); // prepare paint for frame drawing - mPaint.setXfermode(null); + paint.setXfermode(null); mScale = 1f; @@ -100,7 +100,7 @@ public class CircleFramedDrawable extends Drawable { final float pad = (mSize - inside) / 2f; mDstRect.set(pad, pad, mSize - pad, mSize - pad); - canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null); + canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, mIconPaint); } public void setScale(float scale) { @@ -122,8 +122,12 @@ public class CircleFramedDrawable extends Drawable { @Override public void setColorFilter(ColorFilter cf) { + if (mIconPaint == null) { + mIconPaint = new Paint(); + } + mIconPaint.setColorFilter(cf); } - + @Override public int getIntrinsicWidth() { return mSize; diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 7a27676237a1..6ecf303c6dc8 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -68,14 +68,14 @@ android_library { "iconloader_base", "SystemUI-tags", "SystemUI-proto", - "dagger2-2.19", + "dagger2", "jsr330" ], manifest: "AndroidManifest.xml", kotlincflags: ["-Xjvm-default=enable"], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } filegroup { @@ -139,7 +139,7 @@ android_library { "mockito-target-extended-minus-junit4", "testables", "truth-prebuilt", - "dagger2-2.19", + "dagger2", "jsr330" ], libs: [ @@ -151,7 +151,7 @@ android_library { "--extra-packages", "com.android.systemui", ], - plugins: ["dagger2-compiler-2.19"], + plugins: ["dagger2-compiler"], } android_app { diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md index c440fba10135..bb68647ceb00 100644 --- a/packages/SystemUI/docs/dagger.md +++ b/packages/SystemUI/docs/dagger.md @@ -206,11 +206,31 @@ public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet a ## Updating Dagger2 +We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates +when that repository is updated. + +*Deprecated:* + Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded into [/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/) +The following commands should work, substituting in the version that you are looking for: + +```` +cd prebuilts/tools/common/m2/repository/com/google/dagger/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/ + +wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/ +```` +Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars. + ## TODO List - Eliminate usages of Dependency#get diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index 597644bf3295..4527c6c793d5 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -53,6 +53,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:elevation="4dp" + android:importantForAccessibility="no" android:layout_weight="1"> <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index f474a36b99b7..732758a2ded2 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<com.google.android.systemui.udfps.UdfpsView +<com.android.systemui.biometrics.UdfpsView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/udfps_view" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index ee2d001866ce..9d3620f34186 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -25,6 +25,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_ import android.app.ActivityManager.TaskSnapshot; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.util.Log; @@ -62,10 +63,12 @@ public class ThumbnailData { public ThumbnailData(TaskSnapshot snapshot) { final HardwareBuffer buffer = snapshot.getHardwareBuffer(); - if (buffer != null && (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) { + if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) { // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state - Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE"); - thumbnail = Bitmap.createBitmap(buffer.getWidth(), buffer.getHeight(), ARGB_8888); + Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " + + buffer); + Point taskSize = snapshot.getTaskSize(); + thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888); thumbnail.eraseColor(Color.BLACK); } else { thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace()); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index 10dcbd6f9182..a84664ceee3d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -448,8 +448,8 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " + " UpdateSim.onSimCheckResponse: " + " attemptsRemaining=" + result.getAttemptsRemaining()); - mStateMachine.reset(); } + mStateMachine.reset(); mCheckSimPukThread = null; } }); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 6c06553a84a6..ad11d71eb132 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -62,6 +62,7 @@ import android.os.UserHandle; import android.provider.Settings.Secure; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayCutout.BoundsPosition; import android.view.DisplayInfo; @@ -820,6 +821,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f; + private Display.Mode mDisplayMode = null; private final DisplayInfo mInfo = new DisplayInfo(); private final Paint mPaint = new Paint(); private final List<Rect> mBounds = new ArrayList(); @@ -904,11 +906,33 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override public void onDisplayChanged(int displayId) { + Display.Mode oldMode = mDisplayMode; + mDisplayMode = getDisplay().getMode(); + + // Display mode hasn't meaningfully changed, we can ignore it + if (!modeChanged(oldMode, mDisplayMode)) { + return; + } + if (displayId == getDisplay().getDisplayId()) { update(); } } + private boolean modeChanged(Display.Mode oldMode, Display.Mode newMode) { + if (oldMode == null) { + return true; + } + + boolean changed = false; + changed |= oldMode.getPhysicalHeight() != newMode.getPhysicalHeight(); + changed |= oldMode.getPhysicalWidth() != newMode.getPhysicalWidth(); + // We purposely ignore refresh rate and id changes here, because we don't need to + // invalidate for those, and they can trigger the refresh rate to increase + + return changed; + } + public void setRotation(int rotation) { mRotation = rotation; update(); diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 941de2dc63ec..5fd7b53435cf 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -21,9 +21,9 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -57,12 +57,11 @@ public class AppOpsControllerImpl implements AppOpsController, private static final long NOTED_OP_TIME_DELAY_MS = 5000; private static final String TAG = "AppOpsControllerImpl"; private static final boolean DEBUG = false; - private final Context mContext; private final AppOpsManager mAppOps; private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); - private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); + private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>(); private boolean mListening; @GuardedBy("mActiveItems") @@ -71,6 +70,7 @@ public class AppOpsControllerImpl implements AppOpsController, private final List<AppOpItem> mNotedItems = new ArrayList<>(); protected static final int[] OPS = new int[] { + AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, AppOpsManager.OP_CAMERA, AppOpsManager.OP_SYSTEM_ALERT_WINDOW, AppOpsManager.OP_RECORD_AUDIO, @@ -83,7 +83,6 @@ public class AppOpsControllerImpl implements AppOpsController, Context context, @Background Looper bgLooper, DumpManager dumpManager) { - mContext = context; mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mBGHandler = new H(bgLooper); final int numOps = OPS.length; @@ -131,7 +130,7 @@ public class AppOpsControllerImpl implements AppOpsController, boolean added = false; final int numCodes = opsCodes.length; for (int i = 0; i < numCodes; i++) { - if (mCallbacksByCode.containsKey(opsCodes[i])) { + if (mCallbacksByCode.contains(opsCodes[i])) { mCallbacksByCode.get(opsCodes[i]).add(callback); added = true; } else { @@ -155,7 +154,7 @@ public class AppOpsControllerImpl implements AppOpsController, public void removeCallback(int[] opsCodes, AppOpsController.Callback callback) { final int numCodes = opsCodes.length; for (int i = 0; i < numCodes; i++) { - if (mCallbacksByCode.containsKey(opsCodes[i])) { + if (mCallbacksByCode.contains(opsCodes[i])) { mCallbacksByCode.get(opsCodes[i]).remove(callback); } } @@ -312,7 +311,7 @@ public class AppOpsControllerImpl implements AppOpsController, } private void notifySuscribers(int code, int uid, String packageName, boolean active) { - if (mCallbacksByCode.containsKey(code)) { + if (mCallbacksByCode.contains(code)) { if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { cb.onActiveStateChanged(code, uid, packageName, active); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index c82e1debf185..97e97ffaae0c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -22,6 +22,8 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.LayoutInflater; @@ -44,6 +46,7 @@ class UdfpsController { private final Context mContext; private final IFingerprintService mFingerprintService; private final WindowManager mWindowManager; + private final Handler mHandler; private UdfpsView mView; private WindowManager.LayoutParams mLayoutParams; @@ -96,6 +99,7 @@ class UdfpsController { mContext = context; mFingerprintService = fingerprintService; mWindowManager = windowManager; + mHandler = new Handler(Looper.getMainLooper()); start(); } @@ -138,23 +142,27 @@ class UdfpsController { } private void showUdfpsOverlay() { - Log.v(TAG, "showUdfpsOverlay | adding window"); - if (!mIsOverlayShowing) { - try { - mWindowManager.addView(mView, mLayoutParams); - mIsOverlayShowing = true; - } catch (RuntimeException e) { - Log.e(TAG, "showUdfpsOverlay | failed to add window", e); + mHandler.post(() -> { + Log.v(TAG, "showUdfpsOverlay | adding window"); + if (!mIsOverlayShowing) { + try { + mWindowManager.addView(mView, mLayoutParams); + mIsOverlayShowing = true; + } catch (RuntimeException e) { + Log.e(TAG, "showUdfpsOverlay | failed to add window", e); + } } - } + }); } private void hideUdfpsOverlay() { - Log.v(TAG, "hideUdfpsOverlay | removing window"); - if (mIsOverlayShowing) { - mWindowManager.removeView(mView); - mIsOverlayShowing = false; - } + mHandler.post(() -> { + Log.v(TAG, "hideUdfpsOverlay | removing window"); + if (mIsOverlayShowing) { + mWindowManager.removeView(mView); + mIsOverlayShowing = false; + } + }); } private void onFingerDown(int x, int y, float minor, float major) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java index 40a93e1cdc47..57e2362bd511 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java @@ -24,7 +24,6 @@ import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -95,9 +94,16 @@ public class BubbleIconFactory extends BaseIconFactory { Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(), userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig()); Canvas c = new Canvas(badgeAndRing); - Rect dest = new Rect((int) ringStrokeWidth, (int) ringStrokeWidth, - c.getHeight() - (int) ringStrokeWidth, c.getWidth() - (int) ringStrokeWidth); - c.drawBitmap(userBadgedBitmap, null, dest, null); + + final int bitmapTop = (int) ringStrokeWidth; + final int bitmapLeft = (int) ringStrokeWidth; + final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth; + final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth; + + Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth, + bitmapHeight, /* filter */ true); + c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null); + Paint ringPaint = new Paint(); ringPaint.setStyle(Paint.Style.STROKE); ringPaint.setColor(importantConversationColor); @@ -105,6 +111,7 @@ public class BubbleIconFactory extends BaseIconFactory { ringPaint.setStrokeWidth(ringStrokeWidth); c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2 - ringStrokeWidth, ringPaint); + shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c); return createIconBitmap(badgeAndRing); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 0873328c2382..4bd046e23dab 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.assist.AssistModule; +import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.dump.DumpManager; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; @@ -68,6 +69,7 @@ import dagger.Provides; }, subcomponents = {StatusBarComponent.class, NotificationRowComponent.class, + DozeComponent.class, ExpandableNotificationRowComponent.class}) public abstract class SystemUIModule { diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java index 4fea45c39d5d..60ee806d0a9f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java @@ -51,6 +51,14 @@ public class AlwaysOnDisplayPolicy { static final String KEY_WALLPAPER_VISIBILITY_MS = "wallpaper_visibility_timeout"; static final String KEY_WALLPAPER_FADE_OUT_MS = "wallpaper_fade_out_duration"; + + /** + * Integer used to dim the screen while dozing. + * + * @see R.integer.config_screenBrightnessDoze + */ + public int defaultDozeBrightness; + /** * Integer array to map ambient brightness type to real screen brightness. * @@ -165,6 +173,8 @@ public class AlwaysOnDisplayPolicy { DEFAULT_WALLPAPER_FADE_OUT_MS); wallpaperVisibilityDuration = mParser.getLong(KEY_WALLPAPER_VISIBILITY_MS, DEFAULT_WALLPAPER_VISIBILITY_MS); + defaultDozeBrightness = resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessDoze); screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY, resources.getIntArray( R.array.config_doze_brightness_sensor_to_brightness)); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java index abd41d4318bd..5eb9808ebd7c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java @@ -16,20 +16,22 @@ package com.android.systemui.doze; -import android.content.Context; - import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; +import com.android.systemui.doze.dagger.DozeScope; + +import javax.inject.Inject; /** * Controls removing Keyguard authorization when the phone goes to sleep. */ +@DozeScope public class DozeAuthRemover implements DozeMachine.Part { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - public DozeAuthRemover(Context context) { - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); + @Inject + public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor) { + mKeyguardUpdateMonitor = keyguardUpdateMonitor; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java index 554457b3564a..2a3d67fd7a8d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java @@ -22,33 +22,41 @@ import android.util.Log; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeMachine.State; +import com.android.systemui.doze.dagger.DozeScope; import java.io.PrintWriter; +import javax.inject.Inject; + /** * Handles dock events for ambient state changes. */ +@DozeScope public class DozeDockHandler implements DozeMachine.Part { private static final String TAG = "DozeDockHandler"; private static final boolean DEBUG = DozeService.DEBUG; private final AmbientDisplayConfiguration mConfig; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final DockManager mDockManager; private final DockEventListener mDockEventListener; private int mDockState = DockManager.STATE_NONE; - DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine, - DockManager dockManager) { - mMachine = machine; + @Inject + DozeDockHandler(AmbientDisplayConfiguration config, DockManager dockManager) { mConfig = config; mDockManager = dockManager; mDockEventListener = new DockEventListener(); } @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + + @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case INITIALIZED: diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java deleted file mode 100644 index 3bac196ca59f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2016 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.doze; - -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.app.IWallpaperManager; -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dock.DockManager; -import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.BiometricUnlockController; -import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.util.sensors.AsyncSensorManager; -import com.android.systemui.util.sensors.ProximitySensor; -import com.android.systemui.util.wakelock.DelayedWakeLock; -import com.android.systemui.util.wakelock.WakeLock; - -import javax.inject.Inject; - -public class DozeFactory { - - private final FalsingManager mFalsingManager; - private final DozeLog mDozeLog; - private final DozeParameters mDozeParameters; - private final BatteryController mBatteryController; - private final AsyncSensorManager mAsyncSensorManager; - private final AlarmManager mAlarmManager; - private final WakefulnessLifecycle mWakefulnessLifecycle; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final DockManager mDockManager; - private final IWallpaperManager mWallpaperManager; - private final ProximitySensor mProximitySensor; - private final ProximitySensor.ProximityCheck mProximityCheck; - private final DelayedWakeLock.Builder mDelayedWakeLockBuilder; - private final Handler mHandler; - private final BiometricUnlockController mBiometricUnlockController; - private final BroadcastDispatcher mBroadcastDispatcher; - private final DozeHost mDozeHost; - - @Inject - public DozeFactory(FalsingManager falsingManager, DozeLog dozeLog, - DozeParameters dozeParameters, BatteryController batteryController, - AsyncSensorManager asyncSensorManager, AlarmManager alarmManager, - WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor, - DockManager dockManager, @Nullable IWallpaperManager wallpaperManager, - ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proximityCheck, - DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler, - BiometricUnlockController biometricUnlockController, - BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) { - mFalsingManager = falsingManager; - mDozeLog = dozeLog; - mDozeParameters = dozeParameters; - mBatteryController = batteryController; - mAsyncSensorManager = asyncSensorManager; - mAlarmManager = alarmManager; - mWakefulnessLifecycle = wakefulnessLifecycle; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mDockManager = dockManager; - mWallpaperManager = wallpaperManager; - mProximitySensor = proximitySensor; - mProximityCheck = proximityCheck; - mDelayedWakeLockBuilder = delayedWakeLockBuilder; - mHandler = handler; - mBiometricUnlockController = biometricUnlockController; - mBroadcastDispatcher = broadcastDispatcher; - mDozeHost = dozeHost; - } - - /** Creates a DozeMachine with its parts for {@code dozeService}. */ - DozeMachine assembleMachine(DozeService dozeService) { - AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(dozeService); - WakeLock wakeLock = mDelayedWakeLockBuilder.setHandler(mHandler).setTag("Doze").build(); - - DozeMachine.Service wrappedService = dozeService; - wrappedService = new DozeBrightnessHostForwarder(wrappedService, mDozeHost); - wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( - wrappedService, mDozeParameters); - wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( - wrappedService, mDozeParameters); - - DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock, - mWakefulnessLifecycle, mBatteryController, mDozeLog, mDockManager, - mDozeHost); - machine.setParts(new DozeMachine.Part[]{ - new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()), - new DozeFalsingManagerAdapter(mFalsingManager), - createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost, - mAlarmManager, config, mDozeParameters, wakeLock, - machine, mDockManager, mDozeLog, mProximityCheck), - createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler, - mAlarmManager, mDozeParameters, mDozeLog), - new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters, - wakeLock), - createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager, - mDozeHost, mDozeParameters, mHandler), - new DozeWallpaperState(mWallpaperManager, mBiometricUnlockController, - mDozeParameters), - new DozeDockHandler(config, machine, mDockManager), - new DozeAuthRemover(dozeService) - }); - - return machine; - } - - private DozeMachine.Part createDozeScreenBrightness(Context context, - DozeMachine.Service service, SensorManager sensorManager, DozeHost host, - DozeParameters params, Handler handler) { - Sensor sensor = DozeSensors.findSensorWithType(sensorManager, - context.getString(R.string.doze_brightness_sensor_type)); - return new DozeScreenBrightness(context, service, sensorManager, sensor, - mBroadcastDispatcher, host, handler, params.getPolicy()); - } - - private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager, - DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, - DozeParameters params, WakeLock wakeLock, - DozeMachine machine, DockManager dockManager, DozeLog dozeLog, - ProximitySensor.ProximityCheck proximityCheck) { - boolean allowPulseTriggers = true; - return new DozeTriggers(context, machine, host, alarmManager, config, params, - sensorManager, wakeLock, allowPulseTriggers, dockManager, - mProximitySensor, proximityCheck, dozeLog, mBroadcastDispatcher); - - } - - private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock, - DozeMachine machine, Handler handler, AlarmManager alarmManager, - DozeParameters params, DozeLog dozeLog) { - return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params, - mKeyguardUpdateMonitor, dozeLog); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java index 250308d4c3a6..94b8ba305d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java @@ -16,15 +16,20 @@ package com.android.systemui.doze; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.plugins.FalsingManager; +import javax.inject.Inject; + /** * Notifies FalsingManager of whether or not AOD is showing. */ +@DozeScope public class DozeFalsingManagerAdapter implements DozeMachine.Part { private final FalsingManager mFalsingManager; + @Inject public DozeFalsingManagerAdapter(FalsingManager falsingManager) { mFalsingManager = falsingManager; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index ae7d82ac4a5e..b9d23ade2ee1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -28,6 +28,8 @@ import android.view.Display; import com.android.internal.util.Preconditions; import com.android.systemui.dock.DockManager; +import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.doze.dagger.WrappedService; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness; import com.android.systemui.statusbar.phone.DozeParameters; @@ -38,6 +40,8 @@ import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; + /** * Orchestrates all things doze. * @@ -46,6 +50,7 @@ import java.util.ArrayList; * * During state transitions and in certain states, DozeMachine holds a wake lock. */ +@DozeScope public class DozeMachine { static final String TAG = "DozeMachine"; @@ -146,9 +151,11 @@ public class DozeMachine { private boolean mWakeLockHeldForCurrentState = false; private DockManager mDockManager; - public DozeMachine(Service service, AmbientDisplayConfiguration config, WakeLock wakeLock, - WakefulnessLifecycle wakefulnessLifecycle, BatteryController batteryController, - DozeLog dozeLog, DockManager dockManager, DozeHost dozeHost) { + @Inject + public DozeMachine(@WrappedService Service service, AmbientDisplayConfiguration config, + WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle, + BatteryController batteryController, DozeLog dozeLog, DockManager dockManager, + DozeHost dozeHost, Part[] parts) { mDozeService = service; mConfig = config; mWakefulnessLifecycle = wakefulnessLifecycle; @@ -157,6 +164,10 @@ public class DozeMachine { mDozeLog = dozeLog; mDockManager = dockManager; mDozeHost = dozeHost; + mParts = parts; + for (Part part : parts) { + part.setDozeMachine(this); + } } /** @@ -168,12 +179,6 @@ public class DozeMachine { } } - /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */ - public void setParts(Part[] parts) { - Preconditions.checkState(mParts == null); - mParts = parts; - } - /** * Requests transitioning to {@code requestedState}. * @@ -432,6 +437,9 @@ public class DozeMachine { /** Alerts that the screenstate is being changed. */ default void onScreenState(int state) {} + + /** Sets the {@link DozeMachine} when this Part is associated with one. */ + default void setDozeMachine(DozeMachine dozeMachine) {} } /** A wrapper interface for {@link android.service.dreams.DreamService} */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java index 58f144830650..2b4067865415 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java @@ -19,25 +19,35 @@ package com.android.systemui.doze; import android.app.AlarmManager; import android.os.Handler; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.util.AlarmTimeout; +import javax.inject.Inject; + /** * Moves the doze machine from the pausing to the paused state after a timeout. */ +@DozeScope public class DozePauser implements DozeMachine.Part { public static final String TAG = DozePauser.class.getSimpleName(); private final AlarmTimeout mPauseTimeout; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final AlwaysOnDisplayPolicy mPolicy; - public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager, + @Inject + public DozePauser(@Main Handler handler, AlarmManager alarmManager, AlwaysOnDisplayPolicy policy) { - mMachine = machine; mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler); mPolicy = policy; } @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + + @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD_PAUSING: diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 64cfb4bcd058..342818de3d1e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -19,7 +19,6 @@ package com.android.systemui.doze; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -31,12 +30,17 @@ import android.os.UserHandle; import android.provider.Settings; import android.view.Display; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.doze.dagger.BrightnessSensor; +import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.doze.dagger.WrappedService; +import com.android.systemui.util.sensors.AsyncSensorManager; + +import javax.inject.Inject; /** * Controls the screen brightness when dozing. */ +@DozeScope public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part, SensorEventListener { private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties @@ -51,10 +55,8 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private final Handler mHandler; private final SensorManager mSensorManager; private final Sensor mLightSensor; - private final BroadcastDispatcher mBroadcastDispatcher; private final int[] mSensorToBrightness; private final int[] mSensorToScrimOpacity; - private final boolean mDebuggable; private boolean mRegistered; private int mDefaultDozeBrightness; @@ -71,40 +73,20 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private int mDebugBrightnessBucket = -1; private DozeMachine.State mState; - @VisibleForTesting - public DozeScreenBrightness(Context context, DozeMachine.Service service, - SensorManager sensorManager, Sensor lightSensor, - BroadcastDispatcher broadcastDispatcher, DozeHost host, - Handler handler, int defaultDozeBrightness, int[] sensorToBrightness, - int[] sensorToScrimOpacity, boolean debuggable) { + @Inject + public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service, + AsyncSensorManager sensorManager, @BrightnessSensor Sensor lightSensor, + DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) { mContext = context; mDozeService = service; mSensorManager = sensorManager; mLightSensor = lightSensor; - mBroadcastDispatcher = broadcastDispatcher; mDozeHost = host; mHandler = handler; - mDebuggable = debuggable; - - mDefaultDozeBrightness = defaultDozeBrightness; - mSensorToBrightness = sensorToBrightness; - mSensorToScrimOpacity = sensorToScrimOpacity; - - if (mDebuggable) { - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_AOD_BRIGHTNESS); - mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL); - } - } - public DozeScreenBrightness(Context context, DozeMachine.Service service, - SensorManager sensorManager, Sensor lightSensor, - BroadcastDispatcher broadcastDispatcher, DozeHost host, Handler handler, - AlwaysOnDisplayPolicy policy) { - this(context, service, sensorManager, lightSensor, broadcastDispatcher, host, handler, - context.getResources().getInteger( - com.android.internal.R.integer.config_screenBrightnessDoze), - policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS); + mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness; + mSensorToBrightness = alwaysOnDisplayPolicy.screenBrightnessArray; + mSensorToScrimOpacity = alwaysOnDisplayPolicy.dimmingScrimArray; } @Override @@ -139,9 +121,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private void onDestroy() { setLightSensorEnabled(false); - if (mDebuggable) { - mBroadcastDispatcher.unregisterReceiver(this); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 915359374bfe..8c50a16b566f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -26,13 +26,19 @@ import android.os.Handler; import android.util.Log; import android.view.Display; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.doze.dagger.WrappedService; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; +import javax.inject.Inject; + /** * Controls the screen when dozing. */ +@DozeScope public class DozeScreenState implements DozeMachine.Part { private static final boolean DEBUG = DozeService.DEBUG; @@ -59,8 +65,9 @@ public class DozeScreenState implements DozeMachine.Part { private int mPendingScreenState = Display.STATE_UNKNOWN; private SettableWakeLock mWakeLock; - public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host, - DozeParameters parameters, WakeLock wakeLock) { + @Inject + public DozeScreenState(@WrappedService DozeMachine.Service service, @Main Handler handler, + DozeHost host, DozeParameters parameters, WakeLock wakeLock) { mDozeService = service; mHandler = handler; mParameters = parameters; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index aebf41b884c4..37bdda8a06a1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -21,7 +21,6 @@ import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_ import android.annotation.AnyThread; import android.app.ActivityManager; -import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -64,10 +63,8 @@ public class DozeSensors { private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); private final Context mContext; - private final AlarmManager mAlarmManager; private final AsyncSensorManager mSensorManager; private final ContentResolver mResolver; - private final DozeParameters mDozeParameters; private final AmbientDisplayConfiguration mConfig; private final WakeLock mWakeLock; private final Consumer<Boolean> mProxCallback; @@ -98,14 +95,12 @@ public class DozeSensors { } } - public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, + DozeSensors(Context context, AsyncSensorManager sensorManager, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog, ProximitySensor proximitySensor) { mContext = context; - mAlarmManager = alarmManager; mSensorManager = sensorManager; - mDozeParameters = dozeParameters; mConfig = config; mWakeLock = wakeLock; mProxCallback = proxCallback; @@ -206,7 +201,10 @@ public class DozeSensors { return findSensorWithType(mSensorManager, type); } - static Sensor findSensorWithType(SensorManager sensorManager, String type) { + /** + * Utility method to find a {@link Sensor} for the supplied string type. + */ + public static Sensor findSensorWithType(SensorManager sensorManager, String type) { if (TextUtils.isEmpty(type)) { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index d2bebb7b27d1..19b0ea1db04e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -22,6 +22,7 @@ import android.os.SystemClock; import android.service.dreams.DreamService; import android.util.Log; +import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.plugins.DozeServicePlugin; import com.android.systemui.plugins.DozeServicePlugin.RequestDoze; import com.android.systemui.plugins.PluginListener; @@ -36,16 +37,16 @@ public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final DozeFactory mDozeFactory; + private final DozeComponent.Builder mDozeComponentBuilder; private DozeMachine mDozeMachine; private DozeServicePlugin mDozePlugin; private PluginManager mPluginManager; @Inject - public DozeService(DozeFactory dozeFactory, PluginManager pluginManager) { + public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager) { + mDozeComponentBuilder = dozeComponentBuilder; setDebug(DEBUG); - mDozeFactory = dozeFactory; mPluginManager = pluginManager; } @@ -56,7 +57,8 @@ public class DozeService extends DreamService setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - mDozeMachine = mDozeFactory.assembleMachine(this); + DozeComponent dozeComponent = mDozeComponentBuilder.build(this); + mDozeMachine = dozeComponent.getDozeMachine(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index eb2463b02ae4..0800a201bd92 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -41,6 +41,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.Assert; import com.android.systemui.util.sensors.AsyncSensorManager; @@ -51,9 +52,12 @@ import java.io.PrintWriter; import java.util.Optional; import java.util.function.Consumer; +import javax.inject.Inject; + /** * Handles triggers for ambient state changes. */ +@DozeScope public class DozeTriggers implements DozeMachine.Part { private static final String TAG = "DozeTriggers"; @@ -73,7 +77,7 @@ public class DozeTriggers implements DozeMachine.Part { private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500; private final Context mContext; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final DozeLog mDozeLog; private final DozeSensors mDozeSensors; private final DozeHost mDozeHost; @@ -153,21 +157,21 @@ public class DozeTriggers implements DozeMachine.Part { } } - public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, + @Inject + public DozeTriggers(Context context, DozeHost dozeHost, AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters dozeParameters, AsyncSensorManager sensorManager, - WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, + WakeLock wakeLock, DockManager dockManager, ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) { mContext = context; - mMachine = machine; mDozeHost = dozeHost; mConfig = config; mDozeParameters = dozeParameters; mSensorManager = sensorManager; mWakeLock = wakeLock; - mAllowPulseTriggers = allowPulseTriggers; - mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, + mAllowPulseTriggers = true; + mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor); mUiModeManager = mContext.getSystemService(UiModeManager.class); mDockManager = dockManager; @@ -177,6 +181,11 @@ public class DozeTriggers implements DozeMachine.Part { } @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + + @Override public void destroy() { mDozeSensors.destroy(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 1c056215f1cb..0fdaae82e2d0 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -29,15 +29,20 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; import java.util.Calendar; +import javax.inject.Inject; + /** * The policy controlling doze. */ +@DozeScope public class DozeUi implements DozeMachine.Part { private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min @@ -45,7 +50,7 @@ public class DozeUi implements DozeMachine.Part { private final DozeHost mHost; private final Handler mHandler; private final WakeLock mWakeLock; - private final DozeMachine mMachine; + private DozeMachine mMachine; private final AlarmTimeout mTimeTicker; private final boolean mCanAnimateTransition; private final DozeParameters mDozeParameters; @@ -64,12 +69,12 @@ public class DozeUi implements DozeMachine.Part { private long mLastTimeTickElapsed = 0; - public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, - WakeLock wakeLock, DozeHost host, Handler handler, + @Inject + public DozeUi(Context context, AlarmManager alarmManager, + WakeLock wakeLock, DozeHost host, @Main Handler handler, DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, DozeLog dozeLog) { mContext = context; - mMachine = machine; mWakeLock = wakeLock; mHost = host; mHandler = handler; @@ -80,6 +85,11 @@ public class DozeUi implements DozeMachine.Part { mDozeLog = dozeLog; } + @Override + public void setDozeMachine(DozeMachine dozeMachine) { + mMachine = dozeMachine; + } + /** * Decide if we're taking over the screen-off animation * when the device was configured to skip doze after screen off. diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 7aeb7851bbd1..d5b6cb1a6250 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -21,15 +21,19 @@ import android.app.IWallpaperManager; import android.os.RemoteException; import android.util.Log; +import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import java.io.PrintWriter; +import javax.inject.Inject; + /** * Propagates doze state to wallpaper engine. */ +@DozeScope public class DozeWallpaperState implements DozeMachine.Part { private static final String TAG = "DozeWallpaperState"; @@ -41,8 +45,9 @@ public class DozeWallpaperState implements DozeMachine.Part { private final BiometricUnlockController mBiometricUnlockController; private boolean mIsAmbientMode; + @Inject public DozeWallpaperState( - IWallpaperManager wallpaperManagerService, + @Nullable IWallpaperManager wallpaperManagerService, BiometricUnlockController biometricUnlockController, DozeParameters parameters) { mWallpaperManagerService = wallpaperManagerService; diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java new file mode 100644 index 000000000000..5af8af366b69 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java @@ -0,0 +1,30 @@ +/* + * 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.doze.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface BrightnessSensor { +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java new file mode 100644 index 000000000000..e925927e7564 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java @@ -0,0 +1,40 @@ +/* + * 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.doze.dagger; + +import com.android.systemui.doze.DozeMachine; +import com.android.systemui.doze.DozeService; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Dagger component for items that require a {@link DozeService}. + */ +@Subcomponent(modules = {DozeModule.class}) +@DozeScope +public interface DozeComponent { + /** Simple Builder for {@link DozeComponent}. */ + @Subcomponent.Factory + interface Builder { + DozeComponent build(@BindsInstance DozeService dozeService); + } + + /** Supply a {@link DozeMachine}. */ + @DozeScope + DozeMachine getDozeMachine(); +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java new file mode 100644 index 000000000000..a12e280fcca6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java @@ -0,0 +1,99 @@ +/* + * 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.doze.dagger; + +import android.content.Context; +import android.hardware.Sensor; +import android.os.Handler; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.doze.DozeAuthRemover; +import com.android.systemui.doze.DozeBrightnessHostForwarder; +import com.android.systemui.doze.DozeDockHandler; +import com.android.systemui.doze.DozeFalsingManagerAdapter; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.doze.DozeMachine; +import com.android.systemui.doze.DozePauser; +import com.android.systemui.doze.DozeScreenBrightness; +import com.android.systemui.doze.DozeScreenState; +import com.android.systemui.doze.DozeScreenStatePreventingAdapter; +import com.android.systemui.doze.DozeSensors; +import com.android.systemui.doze.DozeService; +import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter; +import com.android.systemui.doze.DozeTriggers; +import com.android.systemui.doze.DozeUi; +import com.android.systemui.doze.DozeWallpaperState; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.wakelock.DelayedWakeLock; +import com.android.systemui.util.wakelock.WakeLock; + +import dagger.Module; +import dagger.Provides; + +/** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */ +@Module +public abstract class DozeModule { + @Provides + @DozeScope + @WrappedService + static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost, + DozeParameters dozeParameters) { + DozeMachine.Service wrappedService = dozeService; + wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost); + wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( + wrappedService, dozeParameters); + wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( + wrappedService, dozeParameters); + + return wrappedService; + } + + @Provides + @DozeScope + static WakeLock providesDozeWakeLock(DelayedWakeLock.Builder delayedWakeLockBuilder, + @Main Handler handler) { + return delayedWakeLockBuilder.setHandler(handler).setTag("Doze").build(); + } + + @Provides + static DozeMachine.Part[] providesDozeMachinePartes(DozePauser dozePauser, + DozeFalsingManagerAdapter dozeFalsingManagerAdapter, DozeTriggers dozeTriggers, + DozeUi dozeUi, DozeScreenState dozeScreenState, + DozeScreenBrightness dozeScreenBrightness, DozeWallpaperState dozeWallpaperState, + DozeDockHandler dozeDockHandler, DozeAuthRemover dozeAuthRemover) { + return new DozeMachine.Part[]{ + dozePauser, + dozeFalsingManagerAdapter, + dozeTriggers, + dozeUi, + dozeScreenState, + dozeScreenBrightness, + dozeWallpaperState, + dozeDockHandler, + dozeAuthRemover + }; + } + + @Provides + @BrightnessSensor + static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) { + return DozeSensors.findSensorWithType(sensorManager, + context.getString(R.string.doze_brightness_sensor_type)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java new file mode 100644 index 000000000000..7a8b8166a969 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.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.doze.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +/** + * Scope annotation for singleton items within the StatusBarComponent. + */ +@Documented +@Retention(RUNTIME) +@Scope +public @interface DozeScope {} diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java new file mode 100644 index 000000000000..ca8a158f21a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java @@ -0,0 +1,30 @@ +/* + * 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.doze.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface WrappedService { +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 127c5dd54d72..e12b7dd259a5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.content.res.Configuration import android.graphics.Color import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS +import android.util.Log import android.util.MathUtils import android.view.LayoutInflater import android.view.View @@ -25,6 +26,7 @@ import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton +private const val TAG = "MediaCarouselController" private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS) /** @@ -236,7 +238,9 @@ class MediaCarouselController @Inject constructor( val oldData = mediaPlayers[oldKey] if (oldData != null) { val oldData = mediaPlayers.remove(oldKey) - mediaPlayers.put(key, oldData!!) + mediaPlayers.put(key, oldData!!)?.let { + Log.wtf(TAG, "new key $key already exists when migrating from $oldKey") + } } var existingPlayer = mediaPlayers[key] if (existingPlayer == null) { @@ -271,6 +275,11 @@ class MediaCarouselController @Inject constructor( updatePageIndicator() mediaCarouselScrollHandler.onPlayersChanged() mediaCarousel.requiresRemeasuring = true + // Check postcondition: mediaContent should have the same number of children as there are + // elements in mediaPlayers. + if (mediaPlayers.size != mediaContent.childCount) { + Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") + } } private fun removePlayer(key: String) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index e8f0e069c98d..d0642ccf9714 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -33,23 +33,31 @@ class MediaDataCombineLatest @Inject constructor( init { dataSource.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (oldKey != null && !oldKey.equals(key)) { - val s = entries[oldKey]?.second - entries[key] = data to entries[oldKey]?.second - entries.remove(oldKey) + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = data to entries.remove(oldKey)?.second + update(key, oldKey) } else { entries[key] = data to entries[key]?.second + update(key, key) } - update(key, oldKey) } override fun onMediaDataRemoved(key: String) { remove(key) } }) deviceSource.addListener(object : MediaDeviceManager.Listener { - override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) { - entries[key] = entries[key]?.first to data - update(key, key) + override fun onMediaDeviceChanged( + key: String, + oldKey: String?, + data: MediaDeviceData? + ) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = entries.remove(oldKey)?.first to data + update(key, oldKey) + } else { + entries[key] = entries[key]?.first to data + update(key, key) + } } override fun onKeyRemoved(key: String) { remove(key) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 8cb93bfc6d4d..299ae5b50aa9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -517,22 +517,36 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() - if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) { + val removed = mediaEntries.remove(key) + if (useMediaResumption && removed?.resumeAction != null) { Log.d(TAG, "Not removing $key because resumable") - // Move to resume key aka package name - val data = mediaEntries.remove(key)!! - val resumeAction = getResumeMediaAction(data.resumeAction!!) - val updated = data.copy(token = null, actions = listOf(resumeAction), + // Move to resume key (aka package name) if that key doesn't already exist. + val resumeAction = getResumeMediaAction(removed.resumeAction!!) + val updated = removed.copy(token = null, actions = listOf(resumeAction), actionsToShowInCompact = listOf(0), active = false, resumption = true) - mediaEntries.put(data.packageName, updated) - // Notify listeners of "new" controls + val pkg = removed?.packageName + val migrate = mediaEntries.put(pkg, updated) == null + // Notify listeners of "new" controls when migrating or removed and update when not val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataLoaded(data.packageName, key, updated) + if (migrate) { + listenersCopy.forEach { + it.onMediaDataLoaded(pkg, key, updated) + } + } else { + // Since packageName is used for the key of the resumption controls, it is + // possible that another notification has already been reused for the resumption + // controls of this package. In this case, rather than renaming this player as + // packageName, just remove it and then send a update to the existing resumption + // controls. + listenersCopy.forEach { + it.onMediaDataRemoved(key) + } + listenersCopy.forEach { + it.onMediaDataLoaded(pkg, pkg, updated) + } } return } - val removed = mediaEntries.remove(key) if (removed != null) { val listenersCopy = listeners.toSet() listenersCopy.forEach { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 7ae2dc5c0941..143f8496e7aa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -71,7 +71,8 @@ class MediaDeviceManager @Inject constructor( val controller = data.token?.let { MediaController(context, it) } - entry = Token(key, controller, localMediaManagerFactory.create(data.packageName)) + entry = Token(key, oldKey, controller, + localMediaManagerFactory.create(data.packageName)) entries[key] = entry entry.start() } @@ -98,23 +99,24 @@ class MediaDeviceManager @Inject constructor( } } - private fun processDevice(key: String, device: MediaDevice?) { + private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) { val enabled = device != null val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name) listeners.forEach { - it.onMediaDeviceChanged(key, data) + it.onMediaDeviceChanged(key, oldKey, data) } } interface Listener { /** Called when the route has changed for a given notification. */ - fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) + fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?) /** Called when the notification was removed. */ fun onKeyRemoved(key: String) } private inner class Token( val key: String, + val oldKey: String?, val controller: MediaController?, val localMediaManager: LocalMediaManager ) : LocalMediaManager.DeviceCallback { @@ -125,7 +127,7 @@ class MediaDeviceManager @Inject constructor( set(value) { if (!started || value != field) { field = value - processDevice(key, value) + processDevice(key, oldKey, value) } } fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java index 5d9439f5f127..c581ac677532 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java @@ -16,6 +16,8 @@ package com.android.systemui.onehanded; +import android.view.SurfaceControl; + /** * Additional callback interface for OneHanded animation */ @@ -30,7 +32,7 @@ public interface OneHandedAnimationCallback { /** * Called when OneHanded animation is ended. */ - default void onOneHandedAnimationEnd( + default void onOneHandedAnimationEnd(SurfaceControl.Transaction tx, OneHandedAnimationController.OneHandedTransitionAnimator animator) { } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java index 08f08c585224..1926c44abcba 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java @@ -19,16 +19,16 @@ package com.android.systemui.onehanded; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; -import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.view.animation.OvershootInterpolator; import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; import javax.inject.Inject; @@ -39,18 +39,6 @@ public class OneHandedAnimationController { private static final float FRACTION_START = 0f; private static final float FRACTION_END = 1f; - public static final int ANIM_TYPE_TRANSLATE = 0; - public static final int ANIM_TYPE_SCALE = 1; - - // Note: ANIM_TYPE_SCALE reserve for the future development - @IntDef(prefix = {"ANIM_TYPE_"}, value = { - ANIM_TYPE_TRANSLATE, - ANIM_TYPE_SCALE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AnimationType { - } - public static final int TRANSITION_DIRECTION_NONE = 0; public static final int TRANSITION_DIRECTION_TRIGGER = 1; public static final int TRANSITION_DIRECTION_EXIT = 2; @@ -64,42 +52,57 @@ public class OneHandedAnimationController { public @interface TransitionDirection { } - private final Interpolator mFastOutSlowInInterpolator; + private final Interpolator mOvershootInterpolator; private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper; - private OneHandedTransitionAnimator mCurrentAnimator; + private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap = + new HashMap<>(); /** * Constructor of OneHandedAnimationController */ @Inject - public OneHandedAnimationController(Context context, + public OneHandedAnimationController( OneHandedSurfaceTransactionHelper surfaceTransactionHelper) { mSurfaceTransactionHelper = surfaceTransactionHelper; - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); + mOvershootInterpolator = new OvershootInterpolator(); } @SuppressWarnings("unchecked") OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) { - if (mCurrentAnimator == null) { - mCurrentAnimator = setupOneHandedTransitionAnimator( - OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)); - } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_TRANSLATE - && mCurrentAnimator.isRunning()) { - mCurrentAnimator.updateEndValue(endBounds); + final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash); + if (animator == null) { + mAnimatorMap.put(leash, setupOneHandedTransitionAnimator( + OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds))); + } else if (animator.isRunning()) { + animator.updateEndValue(endBounds); } else { - mCurrentAnimator.cancel(); - mCurrentAnimator = setupOneHandedTransitionAnimator( - OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)); + animator.cancel(); + mAnimatorMap.put(leash, setupOneHandedTransitionAnimator( + OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds))); + } + return mAnimatorMap.get(leash); + } + + HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() { + return mAnimatorMap; + } + + boolean isAnimatorsConsumed() { + return mAnimatorMap.isEmpty(); + } + + void removeAnimator(SurfaceControl key) { + final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key); + if (animator != null && animator.isRunning()) { + animator.cancel(); } - return mCurrentAnimator; } OneHandedTransitionAnimator setupOneHandedTransitionAnimator( OneHandedTransitionAnimator animator) { animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(mOvershootInterpolator); animator.setFloatValues(FRACTION_START, FRACTION_END); return animator; } @@ -114,7 +117,6 @@ public class OneHandedAnimationController { ValueAnimator.AnimatorListener { private final SurfaceControl mLeash; - private final @AnimationType int mAnimationType; private T mStartValue; private T mEndValue; private T mCurrentValue; @@ -127,10 +129,8 @@ public class OneHandedAnimationController { private @TransitionDirection int mTransitionDirection; private int mTransitionOffset; - private OneHandedTransitionAnimator(SurfaceControl leash, @AnimationType int animationType, - T startValue, T endValue) { + private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) { mLeash = leash; - mAnimationType = animationType; mStartValue = startValue; mEndValue = endValue; addListener(this); @@ -150,8 +150,10 @@ public class OneHandedAnimationController { @Override public void onAnimationEnd(Animator animation) { mCurrentValue = mEndValue; + final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); + onEndTransaction(mLeash, tx); if (mOneHandedAnimationCallback != null) { - mOneHandedAnimationCallback.onOneHandedAnimationEnd(this); + mOneHandedAnimationCallback.onOneHandedAnimationEnd(tx, this); } } @@ -196,6 +198,10 @@ public class OneHandedAnimationController { return this; } + SurfaceControl getLeash() { + return mLeash; + } + Rect getDestinationBounds() { return (Rect) mEndValue; } @@ -227,11 +233,6 @@ public class OneHandedAnimationController { return mEndValue; } - @AnimationType - int getAnimationType() { - return mAnimationType; - } - void setCurrentValue(T value) { mCurrentValue = value; } @@ -250,11 +251,9 @@ public class OneHandedAnimationController { @VisibleForTesting static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash, Rect startValue, Rect endValue) { - // At R, we only support translate type first. - final int animType = ANIM_TYPE_TRANSLATE; - return new OneHandedTransitionAnimator<Rect>(leash, animType, - new Rect(startValue), new Rect(endValue)) { + return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue), + new Rect(endValue)) { private final Rect mTmpRect = new Rect(); @@ -282,7 +281,7 @@ public class OneHandedAnimationController { void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { getSurfaceTransactionHelper() .alpha(tx, leash, 1f) - .crop(tx, leash, getStartValue()) + .translate(tx, leash, getEndValue().top - getStartValue().top) .round(tx, leash); tx.apply(); } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java index 063d7c843aa5..28d70491b6ee 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java @@ -22,11 +22,14 @@ import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSI import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER; import android.content.Context; +import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.view.Display; +import android.view.DisplayInfo; import android.view.SurfaceControl; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; @@ -39,10 +42,12 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.systemui.Dumpable; import com.android.systemui.wm.DisplayController; +import com.android.systemui.wm.DisplayLayout; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Objects; @@ -68,22 +73,22 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen @VisibleForTesting static final int MSG_OFFSET_FINISH = 3; - private final Handler mUpdateHandler; + private final Rect mLastVisualDisplayBounds = new Rect(); + private final Rect mDefaultDisplayBounds = new Rect(); + private Handler mUpdateHandler; private boolean mIsInOneHanded; private int mEnterExitAnimationDurationMs; @VisibleForTesting - DisplayAreaInfo mDisplayAreaInfo; + HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap(); private DisplayController mDisplayController; + private DisplayLayout mDisplayLayout; + private DisplayInfo mDisplayInfo = new DisplayInfo(); private OneHandedAnimationController mAnimationController; - private SurfaceControl mLeash; - private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>(); - private OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper; private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; - private Rect mLastVisualDisplayBounds; - private Rect mDefaultDisplayBounds; + private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>(); @VisibleForTesting OneHandedAnimationCallback mOneHandedAnimationCallback = @@ -94,17 +99,23 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen } @Override - public void onOneHandedAnimationEnd( + public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx, OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH, - obtainArgsFromAnimator(animator))); + mAnimationController.removeAnimator(animator.getLeash()); + if (mAnimationController.isAnimatorsConsumed()) { + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH, + obtainArgsFromAnimator(animator))); + } } @Override public void onOneHandedAnimationCancel( OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH, - obtainArgsFromAnimator(animator))); + mAnimationController.removeAnimator(animator.getLeash()); + if (mAnimationController.isAnimatorsConsumed()) { + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH, + obtainArgsFromAnimator(animator))); + } } }; @@ -112,34 +123,24 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen private Handler.Callback mUpdateCallback = (msg) -> { SomeArgs args = (SomeArgs) msg.obj; final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds; - final int xOffset = args.argi1; final int yOffset = args.argi2; final int direction = args.argi3; - if (mLeash == null || !mLeash.isValid()) { - Log.w(TAG, "mLeash is null, abort transition"); - return false; - } switch (msg.what) { case MSG_RESET_IMMEDIATE: - final OneHandedAnimationController.OneHandedTransitionAnimator animator = - mAnimationController.getAnimator(mLeash, currentBounds, - mDefaultDisplayBounds); - if (animator != null && animator.isRunning()) { - animator.cancel(); - } - final SurfaceControl.Transaction tx = - mSurfaceControlTransactionFactory.getTransaction(); - tx.setPosition(mLeash, 0, 0) - .setWindowCrop(mLeash, -1/* reset */, -1/* reset */) - .apply(); - finishOffset(currentBounds, yOffset, direction); + resetWindowsOffset(); + mLastVisualDisplayBounds.set(currentBounds); + finishOffset(0, TRANSITION_DIRECTION_EXIT); break; case MSG_OFFSET_ANIMATE: - offsetWindows(currentBounds, 0, yOffset, direction, mEnterExitAnimationDurationMs); + final Rect toBounds = new Rect(mDefaultDisplayBounds.left, + mDefaultDisplayBounds.top + yOffset, + mDefaultDisplayBounds.right, + mDefaultDisplayBounds.bottom + yOffset); + offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs); break; case MSG_OFFSET_FINISH: - finishOffset(currentBounds, yOffset, direction); + finishOffset(yOffset, direction); break; } args.recycle(); @@ -152,17 +153,15 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen @Inject public OneHandedDisplayAreaOrganizer(Context context, DisplayController displayController, - OneHandedAnimationController animationController, - OneHandedSurfaceTransactionHelper surfaceTransactionHelper) { - + OneHandedAnimationController animationController) { mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback); mAnimationController = animationController; mDisplayController = displayController; + mDisplayLayout = getDisplayLayout(context); + mDisplayLayout.getStableBounds(mDefaultDisplayBounds); + mLastVisualDisplayBounds.set(mDefaultDisplayBounds); mEnterExitAnimationDurationMs = context.getResources().getInteger( com.android.systemui.R.integer.config_one_handed_translate_animation_duration); - mDefaultDisplayBounds = new Rect(0, 0, getDisplayBounds().x, getDisplayBounds().y); - mLastVisualDisplayBounds = new Rect(mDefaultDisplayBounds); - mSurfaceTransactionHelper = surfaceTransactionHelper; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; } @@ -171,40 +170,52 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen @NonNull SurfaceControl leash) { Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null"); Objects.requireNonNull(leash, "leash must not be null"); - mDisplayAreaInfo = displayAreaInfo; - mLeash = leash; - } - @Override - public void onDisplayAreaInfoChanged(@NonNull DisplayAreaInfo displayAreaInfo) { - Objects.requireNonNull(displayAreaInfo, "displayArea must not be null"); - // Stop one handed without animation and reset cropped size immediately - if (displayAreaInfo.configuration.orientation - != mDisplayAreaInfo.configuration.orientation) { - final Rect newBounds = displayAreaInfo.configuration.windowConfiguration.getBounds(); - resetImmediately(newBounds); + if (displayAreaInfo.featureId != FEATURE_ONE_HANDED) { + Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo); + return; } - mDisplayAreaInfo = displayAreaInfo; + mDisplayAreaMap.put(displayAreaInfo, leash); } @Override public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) { Objects.requireNonNull(displayAreaInfo, "Requires valid displayArea, and displayArea must not be null"); - if (displayAreaInfo.token.asBinder() != mDisplayAreaInfo.token.asBinder()) { + + if (!mDisplayAreaMap.containsKey(displayAreaInfo)) { Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token); return; } - mDisplayAreaInfo = displayAreaInfo; + mDisplayAreaMap.remove(displayAreaInfo); + } - // Stop one handed immediately - if (isInOneHanded()) { - final Rect newBounds = mDisplayAreaInfo.configuration.windowConfiguration.getBounds(); - resetImmediately(newBounds); + @Override + public void unregisterOrganizer() { + super.unregisterOrganizer(); + if (mDisplayAreaMap != null) { + mDisplayAreaMap.clear(); } } /** + * Handler for display rotation changes. + */ + public void onRotateDisplay(Resources res, int toRotation) { + // Stop one handed without animation and reset cropped size immediately + final Rect newBounds = new Rect(); + mDisplayLayout.rotateTo(res, toRotation); + mDisplayLayout.getStableBounds(newBounds); + + SomeArgs args = SomeArgs.obtain(); + args.arg1 = newBounds; + args.argi1 = 0 /* xOffset */; + args.argi2 = 0 /* yOffset */; + args.argi3 = TRANSITION_DIRECTION_EXIT; + mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args)); + } + + /** * Offset the windows by a given offset on Y-axis, triggered also from screen rotation. * Directly perform manipulation/offset on the leash. */ @@ -217,55 +228,43 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args)); } - /** - * Immediately resize/reset leash from previous cropped boundary to default. - * (i.e Screen rotation need to reset crop and position before applying new bounds) - */ - public void resetImmediately(Rect newDisplayBounds) { - updateDisplayBounds(newDisplayBounds); - if (mDisplayAreaInfo != null && mLeash != null) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = newDisplayBounds; - args.argi1 = 0 /* xOffset */; - args.argi2 = 0 /* yOffset */; - args.argi3 = TRANSITION_DIRECTION_EXIT; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args)); - } - } - - private void offsetWindows(Rect currentbounds, int xOffset, int yOffset, int direction, - int durationMs) { + private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) { if (Looper.myLooper() != mUpdateHandler.getLooper()) { throw new RuntimeException("Callers should call scheduleOffset() instead of this " + "directly"); } - if (mDisplayAreaInfo == null || mLeash == null) { - Log.w(TAG, "mToken is not set"); - return; - } + mDisplayAreaMap.forEach( + (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction, + durationMs)); + } - Rect toBounds = new Rect(mDefaultDisplayBounds.left, - mDefaultDisplayBounds.top + yOffset, - mDefaultDisplayBounds.right, - mDefaultDisplayBounds.bottom + yOffset); - animateWindows(currentbounds, toBounds, direction, durationMs); + private void resetWindowsOffset() { + mUpdateHandler.post(() -> { + final SurfaceControl.Transaction tx = + mSurfaceControlTransactionFactory.getTransaction(); + mDisplayAreaMap.forEach( + (key, leash) -> { + final OneHandedAnimationController.OneHandedTransitionAnimator animator = + mAnimationController.getAnimatorMap().remove(leash); + if (animator != null && animator.isRunning()) { + animator.cancel(); + } + tx.setPosition(leash, 0, 0) + .setWindowCrop(leash, -1/* reset */, -1/* reset */); + }); + tx.apply(); + }); } - private void animateWindows(Rect fromBounds, Rect toBounds, + private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds, @OneHandedAnimationController.TransitionDirection int direction, int durationMs) { if (Looper.myLooper() != mUpdateHandler.getLooper()) { throw new RuntimeException("Callers should call scheduleOffset() instead of " + "this directly"); } - // Could happen when exit one handed - if (mDisplayAreaInfo == null || mLeash == null) { - Log.w(TAG, "Abort animation, invalid leash"); - return; - } - mUpdateHandler.post(() -> { final OneHandedAnimationController.OneHandedTransitionAnimator animator = - mAnimationController.getAnimator(mLeash, fromBounds, toBounds); + mAnimationController.getAnimator(leash, fromBounds, toBounds); if (animator != null) { animator.setTransitionDirection(direction) .setOneHandedAnimationCallback(mOneHandedAnimationCallback) @@ -275,7 +274,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen }); } - private void finishOffset(Rect currentBounds, int offset, + private void finishOffset(int offset, @OneHandedAnimationController.TransitionDirection int direction) { if (Looper.myLooper() != mUpdateHandler.getLooper()) { throw new RuntimeException( @@ -284,18 +283,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen // Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence, // the flag *MUST* be updated before dispatch mTransitionCallbacks mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER); - mLastVisualDisplayBounds.set(currentBounds); - if (direction == TRANSITION_DIRECTION_TRIGGER) { - mLastVisualDisplayBounds.offsetTo(0, offset); - for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) { - final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i); - callback.onStartFinished(mLastVisualDisplayBounds); - } - } else { - mLastVisualDisplayBounds.offsetTo(0, 0); - for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) { - final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i); - callback.onStopFinished(mLastVisualDisplayBounds); + mLastVisualDisplayBounds.offsetTo(0, + direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0); + for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) { + final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i); + if (direction == TRANSITION_DIRECTION_TRIGGER) { + callback.onStartFinished(getLastVisualDisplayBounds()); + } else { + callback.onStopFinished(getLastVisualDisplayBounds()); } } } @@ -332,6 +327,16 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen return mUpdateHandler; } + private DisplayLayout getDisplayLayout(Context context) { + final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY); + if (display != null) { + display.getDisplayInfo(mDisplayInfo); + } else { + Log.w(TAG, "get DEFAULT_DISPLAY return null"); + } + return new DisplayLayout(mDisplayInfo, context.getResources(), false, false); + } + /** * Register transition callback */ @@ -349,29 +354,23 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen return args; } - private void updateDisplayBounds(Rect newDisplayBounds) { - if (newDisplayBounds == null) { - mDefaultDisplayBounds.set(0, 0, getDisplayBounds().x, getDisplayBounds().y); - } else { - mDefaultDisplayBounds.set(newDisplayBounds); - } - } - @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mIsInOneHanded="); pw.println(mIsInOneHanded); - pw.print(innerPrefix + "mDisplayAreaInfo="); - pw.println(mDisplayAreaInfo); - pw.print(innerPrefix + "mLeash="); - pw.println(mLeash); + pw.print(innerPrefix + "mDisplayAreaMap="); + pw.println(mDisplayAreaMap); pw.print(innerPrefix + "mDefaultDisplayBounds="); pw.println(mDefaultDisplayBounds); pw.print(innerPrefix + "mLastVisualDisplayBounds="); pw.println(mLastVisualDisplayBounds); pw.print(innerPrefix + "getDisplayBounds()="); pw.println(getDisplayBounds()); + if (mDisplayLayout != null) { + pw.print(innerPrefix + "mDisplayLayout(w, h)="); + pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height()); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java index d60b43f7a0aa..4dacdf3158f8 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java @@ -27,11 +27,13 @@ import android.graphics.Rect; import androidx.annotation.NonNull; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.wm.DisplayChangeController; import com.android.systemui.wm.DisplayController; import java.io.FileDescriptor; @@ -50,15 +52,18 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { private boolean mIsOneHandedEnabled; private boolean mTaskChangeToExit; private float mOffSetFraction; - private DisplayController mDisplayController; + + private final DisplayController mDisplayController; + private final OneHandedGestureHandler mGestureHandler; + private final OneHandedTimeoutHandler mTimeoutHandler; + private final OneHandedTouchHandler mTouchHandler; + private final SysUiState mSysUiFlagContainer; + + private Context mContext; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; - private OneHandedGestureHandler mGestureHandler; private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback; - private OneHandedTimeoutHandler mTimeoutHandler; - private OneHandedTouchHandler mTouchHandler; private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback; private OneHandedTransitionCallback mTransitionCallback; - private SysUiState mSysUiFlagContainer; /** * Handler for system task stack changes, exit when user lunch new task or bring task to front @@ -84,6 +89,17 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { }; /** + * Handle rotation based on OnDisplayChangingListener callback + */ + @VisibleForTesting + private final DisplayChangeController.OnDisplayChangingListener mRotationController = + (display, fromRotation, toRotation, wct) -> { + if (mDisplayAreaOrganizer != null) { + mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation); + } + }; + + /** * Constructor of OneHandedManager */ @Inject @@ -93,9 +109,10 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { OneHandedTouchHandler touchHandler, OneHandedGestureHandler gestureHandler, SysUiState sysUiState) { - + mContext = context; mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; + mDisplayController.addDisplayChangingController(mRotationController); mSysUiFlagContainer = sysUiState; mOffSetFraction = context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index d7cc11b7fd15..32f8c126373d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -171,7 +171,6 @@ public class PipTouchHandler { private float mSavedSnapFraction = -1f; private boolean mSendingHoverAccessibilityEvents; private boolean mMovementWithinDismiss; - private boolean mHideMenuAfterShown = false; private PipAccessibilityInteractionConnection mConnection; // Touch state @@ -273,7 +272,9 @@ public class PipTouchHandler { mMagnetizedPip.setAnimateStuckToTarget( (target, velX, velY, flung, after) -> { - mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after); + if (mEnableDismissDragToEdge) { + mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after); + } return Unit.INSTANCE; }); mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { @@ -281,7 +282,9 @@ public class PipTouchHandler { public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { // Show the dismiss target, in case the initial touch event occurred within the // magnetic field radius. - showDismissTargetMaybe(); + if (mEnableDismissDragToEdge) { + showDismissTargetMaybe(); + } } @Override @@ -726,7 +729,6 @@ public class PipTouchHandler { // on and changing MotionEvents into HoverEvents. // Let's not enable menu show/hide for a11y services. if (!mAccessibilityManager.isTouchExplorationEnabled()) { - mHideMenuAfterShown = true; mTouchState.scheduleHoverExitTimeoutCallback(); } if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) { @@ -813,9 +815,6 @@ public class PipTouchHandler { mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds, mMovementBounds, mExpandedMovementBounds, callback); } - if (mHideMenuAfterShown) { - mMenuController.hideMenu(); - } } else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) { // Try and restore the PiP to the closest edge, using the saved snap fraction // if possible @@ -853,7 +852,6 @@ public class PipTouchHandler { } } mMenuState = menuState; - mHideMenuAfterShown = false; updateMovementBounds(); // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip // as well, or it can't handle a11y focus and pip menu can't perform any action. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java index c454048d0649..daf8ca324c74 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java @@ -104,4 +104,13 @@ public class QSDetailClipper { public void showBackground() { mBackground.showSecondLayer(); } + + /** + * Cancels the animator if it's running. + */ + public void cancelAnimator() { + if (mAnimator != null) { + mAnimator.cancel(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 3e2f9dec5807..e5ed88c10a2e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -208,6 +208,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public void showImmediately() { if (!isShown) { setVisibility(VISIBLE); + mClipper.cancelAnimator(); mClipper.showBackground(); isShown = true; setTileSpecs(); @@ -230,6 +231,10 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED); isShown = false; mToolbar.dismissPopupMenus(); + mClipper.cancelAnimator(); + // Make sure we're not opening (because we're closing). Nobody can think we are + // customizing after the next two lines. + mOpening = false; setCustomizing(false); save(); if (animate) { 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 fba7a22351e2..2ef693467d27 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -21,7 +21,6 @@ import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_ import android.content.Context; import android.content.Intent; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; @@ -33,6 +32,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.R; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; @@ -100,7 +100,8 @@ public class UserDetailView extends PseudoGridView { if (item.picture == null) { v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { - Drawable drawable = new BitmapDrawable(v.getResources(), item.picture); + int avatarSize = (int) v.getResources().getDimension(R.dimen.qs_framed_avatar_size); + Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize); drawable.setColorFilter( item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter()); v.bind(name, drawable, item.info.id); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 423f85f2ddd0..bd65ef06f3a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -282,7 +282,7 @@ public final class NotificationEntry extends ListEntry { + " doesn't match existing key " + mKey); } - mRanking = ranking; + mRanking = ranking.withAudiblyAlertedInfo(mRanking); } /* diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 673aa3903156..85a3bc91dc7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -142,7 +142,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { .expandableNotificationRow(row) .notificationEntry(entry) .onDismissRunnable(onDismissRunnable) - .rowContentBindStage(mRowContentBindStage) .onExpandClickListener(mPresenter) .build(); ExpandableNotificationRowController rowController = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 582e3e5b6c34..a7d83b3b2774 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -37,6 +37,8 @@ import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ImageMessageConsumer; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.media.MediaDataManagerKt; +import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; @@ -71,6 +73,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static final String TAG = "NotifContentInflater"; private boolean mInflateSynchronously = false; + private final boolean mIsMediaInQS; private final NotificationRemoteInputManager mRemoteInputManager; private final NotifRemoteViewCache mRemoteViewCache; private final Lazy<SmartReplyConstants> mSmartReplyConstants; @@ -85,12 +88,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder Lazy<SmartReplyConstants> smartReplyConstants, Lazy<SmartReplyController> smartReplyController, ConversationNotificationProcessor conversationProcessor, + MediaFeatureFlag mediaFeatureFlag, @Background Executor bgExecutor) { mRemoteViewCache = remoteViewCache; mRemoteInputManager = remoteInputManager; mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; mConversationProcessor = conversationProcessor; + mIsMediaInQS = mediaFeatureFlag.getEnabled(); mBgExecutor = bgExecutor; } @@ -135,7 +140,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, callback, - mRemoteInputManager.getRemoteViewsOnClickHandler()); + mRemoteInputManager.getRemoteViewsOnClickHandler(), + mIsMediaInQS); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { @@ -711,6 +717,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private RemoteViews.OnClickHandler mRemoteViewClickHandler; private CancellationSignal mCancellationSignal; private final ConversationNotificationProcessor mConversationProcessor; + private final boolean mIsMediaInQS; private AsyncInflationTask( Executor bgExecutor, @@ -726,7 +733,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, InflationCallback callback, - RemoteViews.OnClickHandler remoteViewClickHandler) { + RemoteViews.OnClickHandler remoteViewClickHandler, + boolean isMediaFlagEnabled) { mEntry = entry; mRow = row; mSmartReplyConstants = smartReplyConstants; @@ -742,6 +750,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mRemoteViewClickHandler = remoteViewClickHandler; mCallback = callback; mConversationProcessor = conversationProcessor; + mIsMediaInQS = isMediaFlagEnabled; entry.setInflationTask(this); } @@ -765,7 +774,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder packageContext = new RtlEnabledContext(packageContext); } Notification notification = sbn.getNotification(); - if (notification.isMediaNotification()) { + if (notification.isMediaNotification() && !(mIsMediaInQS + && MediaDataManagerKt.isMediaNotification(sbn))) { MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext, packageContext); processor.processNotification(notification, recoveredBuilder); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java index 9846f2dcd170..321656df504a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; -import com.android.systemui.statusbar.notification.row.RowContentBindStage; import com.android.systemui.statusbar.phone.StatusBar; import dagger.Binds; @@ -57,8 +56,6 @@ public interface ExpandableNotificationRowComponent { @BindsInstance Builder onDismissRunnable(@DismissRunnable Runnable runnable); @BindsInstance - Builder rowContentBindStage(RowContentBindStage rowContentBindStage); - @BindsInstance Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter); ExpandableNotificationRowComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index f5999f5c8294..f5ea1c880a41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -211,8 +211,4 @@ public class DozeParameters implements TunerService.Tunable, public void onTuningChanged(String key, String newValue) { mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); } - - public AlwaysOnDisplayPolicy getPolicy() { - return mAlwaysOnPolicy; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 304fe0090e77..539158c40d45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -614,8 +614,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa // Bubble controller will give us a valid display id if it should get the back event BubbleController bubbleController = Dependency.get(BubbleController.class); int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext); - if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) { + if (bubbleDisplayId != INVALID_DISPLAY) { ev.setDisplayId(bubbleDisplayId); + } else { + ev.setDisplayId(mContext.getDisplay().getDisplayId()); } InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 732f25f90eb5..e942d85790c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -371,12 +371,26 @@ public abstract class PanelViewController { float vectorVel = (float) Math.hypot( mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); - boolean expand = flingExpands(vel, vectorVel, x, y) - || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel; + final boolean onKeyguard = + mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + + final boolean expand; + if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) { + // If we get a cancel, put the shade back to the state it was in when the gesture + // started + if (onKeyguard) { + expand = true; + } else { + expand = !mPanelClosedOnDown; + } + } else { + expand = flingExpands(vel, vectorVel, x, y); + } + mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold, mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch()); // Log collapse gesture if on lock screen. - if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + if (!expand && onKeyguard) { float displayDensity = mStatusBar.getDisplayDensity(); int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity); int velocityDp = (int) Math.abs(vel / displayDensity); 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 60fc17d9474a..686b87127239 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -348,10 +348,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) { - // In case the user isn't unlocked, make sure to delay a bit because the system is hosed - // with too many things at this case, in order to not skip the initial frames. - mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16); mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY; + scheduleUpdate(); } else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD) || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) { // Scheduling a frame isn't enough when: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 812ce1c62677..6dd96f92b344 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -82,6 +82,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C private boolean mClockVisibleByUser = true; private boolean mAttached; + private boolean mScreenReceiverRegistered; private Calendar mCalendar; private String mClockFormatString; private SimpleDateFormat mClockFormat; @@ -213,6 +214,14 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + if (mScreenReceiverRegistered) { + mScreenReceiverRegistered = false; + mBroadcastDispatcher.unregisterReceiver(mScreenReceiver); + if (mSecondsHandler != null) { + mSecondsHandler.removeCallbacks(mSecondTick); + mSecondsHandler = null; + } + } if (mAttached) { mBroadcastDispatcher.unregisterReceiver(mIntentReceiver); mAttached = false; @@ -363,12 +372,14 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C mSecondsHandler.postAtTime(mSecondTick, SystemClock.uptimeMillis() / 1000 * 1000 + 1000); } + mScreenReceiverRegistered = true; IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter); } } else { if (mSecondsHandler != null) { + mScreenReceiverRegistered = false; mBroadcastDispatcher.unregisterReceiver(mScreenReceiver); mSecondsHandler.removeCallbacks(mSecondTick); mSecondsHandler = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 07de388598b7..c43ad36d4462 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -106,9 +106,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { public void updateNotification(@NonNull String key, boolean alert) { super.updateNotification(key, alert); - AlertEntry alertEntry = getHeadsUpEntry(key); - if (alert && alertEntry != null) { - setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry)); + HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); + if (alert && headsUpEntry != null) { + setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry)); } } 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 512d0f3910f8..5ec5ec662164 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -24,7 +24,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.database.DataSetObserver; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; @@ -36,6 +35,7 @@ import android.view.ViewStub; import android.widget.FrameLayout; import com.android.settingslib.animation.AppearAnimationUtils; +import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -288,7 +288,8 @@ public class KeyguardUserSwitcher { if (item.picture == null) { v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { - Drawable drawable = new BitmapDrawable(v.getResources(), item.picture); + int avatarSize = (int) v.getResources().getDimension(R.dimen.kg_framed_avatar_size); + Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize); drawable.setColorFilter( item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter()); v.bind(name, drawable, item.info.id); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 3bd33ccca911..251693e162d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.policy; +import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; + import static com.android.settingslib.Utils.updateLocationEnabled; import android.app.ActivityManager; -import android.app.AppOpsManager; -import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -36,8 +36,9 @@ import android.provider.Settings; import androidx.annotation.VisibleForTesting; import com.android.systemui.BootCompleteCache; +import com.android.systemui.appops.AppOpItem; +import com.android.systemui.appops.AppOpsController; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.Utils; @@ -51,41 +52,32 @@ import javax.inject.Singleton; * A controller to manage changes of location related states and update the views accordingly. */ @Singleton -public class LocationControllerImpl extends BroadcastReceiver implements LocationController { - - private static final int[] mHighPowerRequestAppOpArray - = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION}; +public class LocationControllerImpl extends BroadcastReceiver implements LocationController, + AppOpsController.Callback { - private Context mContext; + private final Context mContext; + private final AppOpsController mAppOpsController; + private final BootCompleteCache mBootCompleteCache; + private final H mHandler; - private AppOpsManager mAppOpsManager; - private StatusBarManager mStatusBarManager; - private BroadcastDispatcher mBroadcastDispatcher; - private BootCompleteCache mBootCompleteCache; private boolean mAreActiveLocationRequests; - private final H mHandler; - @Inject - public LocationControllerImpl(Context context, @Main Looper mainLooper, - @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher, + public LocationControllerImpl(Context context, AppOpsController appOpsController, + @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) { mContext = context; - mBroadcastDispatcher = broadcastDispatcher; + mAppOpsController = appOpsController; mBootCompleteCache = bootCompleteCache; mHandler = new H(mainLooper); // Register to listen for changes in location settings. IntentFilter filter = new IntentFilter(); - filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); filter.addAction(LocationManager.MODE_CHANGED_ACTION); - mBroadcastDispatcher.registerReceiverWithHandler(this, filter, - new Handler(bgLooper), UserHandle.ALL); + broadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, UserHandle.ALL); - mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mStatusBarManager - = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); + mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this); // Examine the current location state and initialize the status view. updateActiveLocationRequests(); @@ -160,27 +152,12 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio */ @VisibleForTesting protected boolean areActiveHighPowerLocationRequests() { - List<AppOpsManager.PackageOps> packages - = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray); - // AppOpsManager can return null when there is no requested data. - if (packages != null) { - final int numPackages = packages.size(); - for (int packageInd = 0; packageInd < numPackages; packageInd++) { - AppOpsManager.PackageOps packageOp = packages.get(packageInd); - List<AppOpsManager.OpEntry> opEntries = packageOp.getOps(); - if (opEntries != null) { - final int numOps = opEntries.size(); - for (int opInd = 0; opInd < numOps; opInd++) { - AppOpsManager.OpEntry opEntry = opEntries.get(opInd); - // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because - // of the mHighPowerRequestAppOpArray filter, but checking defensively. - if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { - if (opEntry.isRunning()) { - return true; - } - } - } - } + List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps(); + + final int numItems = appOpsItems.size(); + for (int i = 0; i < numItems; i++) { + if (appOpsItems.get(i).getCode() == OP_MONITOR_HIGH_POWER_LOCATION) { + return true; } } @@ -198,14 +175,16 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio @Override public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) { - updateActiveLocationRequests(); - } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) { - mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED); + if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) { + mHandler.locationSettingsChanged(); } } + @Override + public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { + updateActiveLocationRequests(); + } + private final class H extends Handler { private static final int MSG_LOCATION_SETTINGS_CHANGED = 1; private static final int MSG_LOCATION_ACTIVE_CHANGED = 2; diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java index 3f05657ed09e..e08936cbf75e 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Assert.java +++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java @@ -25,16 +25,21 @@ import androidx.annotation.VisibleForTesting; */ public class Assert { private static final Looper sMainLooper = Looper.getMainLooper(); - private static Looper sTestLooper = null; + private static Thread sTestThread = null; @VisibleForTesting public static void setTestableLooper(Looper testLooper) { - sTestLooper = testLooper; + setTestThread(testLooper == null ? null : testLooper.getThread()); + } + + @VisibleForTesting + public static void setTestThread(Thread thread) { + sTestThread = thread; } public static void isMainThread() { if (!sMainLooper.isCurrentThread() - && (sTestLooper == null || !sTestLooper.isCurrentThread())) { + && (sTestThread == null || sTestThread != Thread.currentThread())) { throw new IllegalStateException("should be called from the main thread." + " sMainLooper.threadName=" + sMainLooper.getThread().getName() + " Thread.currentThread()=" + Thread.currentThread().getName()); @@ -43,7 +48,7 @@ public class Assert { public static void isNotMainThread() { if (sMainLooper.isCurrentThread() - && (sTestLooper == null || sTestLooper.isCurrentThread())) { + && (sTestThread == null || sTestThread == Thread.currentThread())) { throw new IllegalStateException("should not be called from the main thread."); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java index 7729965b56c4..bf22a9897d16 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java @@ -32,6 +32,7 @@ import java.util.concurrent.Executors; import javax.inject.Singleton; +import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -199,4 +200,10 @@ public abstract class ConcurrencyModule { public static Executor provideUiBackgroundExecutor() { return Executors.newSingleThreadExecutor(); } + + /** + * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}. + */ + @Binds + public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java new file mode 100644 index 000000000000..0352fb51bc21 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java @@ -0,0 +1,44 @@ +/* + * 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.concurrency; + +import java.util.concurrent.Executor; + +/** + * Factory for building Executors running on a unique named thread. + * + * Use this when our generally available @Main, @Background, @UiBackground, @LongRunning, or + * similar global qualifiers don't quite cut it. Note that the methods here create entirely new + * threads; there are no singletons here. Use responsibly. + */ +public interface ThreadFactory { + /** + * Return an {@link java.util.concurrent.Executor} running on a named thread. + * + * The thread is implicitly started and may be left running indefinitely, depending on the + * implementation. Assume this is the case and use responsibly. + **/ + Executor buildExecutorOnNewThread(String threadName); + + /** + * Return an {@link DelayableExecutor} running on a named thread. + * + * The thread is implicitly started and may be left running indefinitely, depending on the + * implementation. Assume this is the case and use responsibly. + **/ + DelayableExecutor buildDelayableExecutorOnNewThread(String threadName); +} diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java new file mode 100644 index 000000000000..ca8d83607634 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java @@ -0,0 +1,38 @@ +/* + * 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.concurrency; + +import android.os.HandlerThread; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +class ThreadFactoryImpl implements ThreadFactory { + @Inject + ThreadFactoryImpl() {} + + public Executor buildExecutorOnNewThread(String threadName) { + return buildDelayableExecutorOnNewThread(threadName); + } + + public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) { + HandlerThread handlerThread = new HandlerThread(threadName); + handlerThread.start(); + return new ExecutorImpl(handlerThread.getLooper()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java index 450336a6b73b..ed4df175b1a6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java @@ -25,18 +25,18 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.TriggerEventListener; import android.os.Handler; -import android.os.HandlerThread; import android.os.MemoryFile; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.util.concurrency.ThreadFactory; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Singleton; @@ -56,25 +56,14 @@ public class AsyncSensorManager extends SensorManager private final SensorManager mInner; private final List<Sensor> mSensorCache; - private final Handler mHandler; + private final Executor mExecutor; private final List<SensorManagerPlugin> mPlugins; @Inject - public AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) { - this(sensorManager, pluginManager, null); - } - - @VisibleForTesting - public AsyncSensorManager( - SensorManager sensorManager, PluginManager pluginManager, Handler handler) { + public AsyncSensorManager(SensorManager sensorManager, ThreadFactory threadFactory, + PluginManager pluginManager) { mInner = sensorManager; - if (handler == null) { - HandlerThread handlerThread = new HandlerThread("async_sensor"); - handlerThread.start(); - mHandler = new Handler(handlerThread.getLooper()); - } else { - mHandler = handler; - } + mExecutor = threadFactory.buildExecutorOnNewThread("async_sensor"); mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL); mPlugins = new ArrayList<>(); if (pluginManager != null) { @@ -97,7 +86,7 @@ public class AsyncSensorManager extends SensorManager protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs, int reservedFlags) { - mHandler.post(() -> { + mExecutor.execute(() -> { if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) { Log.e(TAG, "Registering " + listener + " for " + sensor + " failed."); } @@ -129,12 +118,12 @@ public class AsyncSensorManager extends SensorManager @Override protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback, Handler handler) { - mHandler.post(() -> mInner.registerDynamicSensorCallback(callback, handler)); + mExecutor.execute(() -> mInner.registerDynamicSensorCallback(callback, handler)); } @Override protected void unregisterDynamicSensorCallbackImpl(DynamicSensorCallback callback) { - mHandler.post(() -> mInner.unregisterDynamicSensorCallback(callback)); + mExecutor.execute(() -> mInner.unregisterDynamicSensorCallback(callback)); } @Override @@ -145,7 +134,7 @@ public class AsyncSensorManager extends SensorManager if (sensor == null) { throw new IllegalArgumentException("sensor cannot be null"); } - mHandler.post(() -> { + mExecutor.execute(() -> { if (!mInner.requestTriggerSensor(listener, sensor)) { Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed."); } @@ -158,7 +147,7 @@ public class AsyncSensorManager extends SensorManager boolean disable) { Preconditions.checkArgument(disable); - mHandler.post(() -> { + mExecutor.execute(() -> { if (!mInner.cancelTriggerSensor(listener, sensor)) { Log.e(TAG, "Canceling " + listener + " for " + sensor + " failed."); } @@ -178,7 +167,7 @@ public class AsyncSensorManager extends SensorManager Log.w(TAG, "No plugins registered"); return false; } - mHandler.post(() -> { + mExecutor.execute(() -> { for (int i = 0; i < mPlugins.size(); i++) { mPlugins.get(i).registerListener(sensor, listener); } @@ -194,7 +183,7 @@ public class AsyncSensorManager extends SensorManager */ public void unregisterPluginListener(SensorManagerPlugin.Sensor sensor, SensorManagerPlugin.SensorEventListener listener) { - mHandler.post(() -> { + mExecutor.execute(() -> { for (int i = 0; i < mPlugins.size(); i++) { mPlugins.get(i).unregisterListener(sensor, listener); } @@ -214,14 +203,14 @@ public class AsyncSensorManager extends SensorManager @Override protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) { - mHandler.post(() -> mInner.setOperationParameter(parameter)); + mExecutor.execute(() -> mInner.setOperationParameter(parameter)); return true; } @Override protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { - mHandler.post(() -> { + mExecutor.execute(() -> { if (sensor == null) { mInner.unregisterListener(listener); } else { diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 8ba5b9951c54..38b20c030116 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -226,6 +226,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (!activeControl.getSurfacePosition().equals(lastSurfacePosition) && mAnimation != null) { startAnimation(mImeShowing, true /* forceRestart */); + } else if (!mImeShowing) { + removeImeSurface(); } }); } @@ -251,6 +253,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */)); } + @Override + public void topFocusedWindowChanged(String packageName) { + // no-op + } + /** * Sends the local visibility state back to window manager. Needed for legacy adjustForIme. */ @@ -370,16 +377,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged dispatchEndPositioning(mDisplayId, mCancelled, t); if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { t.hide(mImeSourceControl.getLeash()); - final IInputMethodManager imms = getImms(); - if (imms != null) { - try { - // Remove the IME surface to make the insets invisible for - // non-client controlled insets. - imms.removeImeSurface(); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to remove IME surface.", e); - } - } + removeImeSurface(); } t.apply(); mTransactionPool.release(t); @@ -402,6 +400,19 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + void removeImeSurface() { + final IInputMethodManager imms = getImms(); + if (imms != null) { + try { + // Remove the IME surface to make the insets invisible for + // non-client controlled insets. + imms.removeImeSurface(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to remove IME surface.", e); + } + } + } + /** * Allows other things to synchronize with the ime position */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java index f29f04244901..3d679deaa426 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java @@ -26,11 +26,14 @@ import android.util.ArraySet; import android.util.Log; import android.view.Display; +import com.android.internal.annotations.GuardedBy; + import java.util.Set; public class SysuiTestableContext extends TestableContext { - private Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>(); + @GuardedBy("mRegisteredReceivers") + private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>(); public SysuiTestableContext(Context base) { super(base); @@ -54,7 +57,11 @@ public class SysuiTestableContext extends TestableContext { } public void cleanUpReceivers(String testName) { - Set<BroadcastReceiver> copy = new ArraySet<>(mRegisteredReceivers); + Set<BroadcastReceiver> copy; + synchronized (mRegisteredReceivers) { + copy = new ArraySet<>(mRegisteredReceivers); + mRegisteredReceivers.clear(); + } for (BroadcastReceiver r : copy) { try { unregisterReceiver(r); @@ -68,7 +75,9 @@ public class SysuiTestableContext extends TestableContext { @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { if (receiver != null) { - mRegisteredReceivers.add(receiver); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.add(receiver); + } } return super.registerReceiver(receiver, filter); } @@ -77,7 +86,9 @@ public class SysuiTestableContext extends TestableContext { public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { if (receiver != null) { - mRegisteredReceivers.add(receiver); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.add(receiver); + } } return super.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @@ -86,7 +97,9 @@ public class SysuiTestableContext extends TestableContext { public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler) { if (receiver != null) { - mRegisteredReceivers.add(receiver); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.add(receiver); + } } return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler); } @@ -94,7 +107,9 @@ public class SysuiTestableContext extends TestableContext { @Override public void unregisterReceiver(BroadcastReceiver receiver) { if (receiver != null) { - mRegisteredReceivers.remove(receiver); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.remove(receiver); + } } super.unregisterReceiver(receiver); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java index 2bcc22c4b99e..f7f3a377f31d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java @@ -35,10 +35,10 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.tests.R; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java index 8bc2e2bc77b6..43d2ad1dfe0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java @@ -20,7 +20,7 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import com.android.systemui.tests.R; +import com.android.systemui.R; /** * Referenced by NotificationTestHelper#makeBubbleMetadata diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index 00d333fb593b..c591c1bd42bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -16,13 +16,13 @@ package com.android.systemui.doze; -import android.hardware.display.AmbientDisplayConfiguration; - import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; +import android.hardware.display.AmbientDisplayConfiguration; + import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.FakeSensorManager; @@ -37,7 +37,6 @@ public class DozeConfigurationUtil { when(params.getPulseOnSigMotion()).thenReturn(false); when(params.getPickupVibrationThreshold()).thenReturn(0); when(params.getProxCheckBeforePulse()).thenReturn(true); - when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class)); when(params.doubleTapReportsTouchCoordinates()).thenReturn(false); when(params.getDisplayNeedsBlanking()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index dc027997578f..5c2b153bf452 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -57,7 +57,8 @@ public class DozeDockHandlerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mConfig = DozeConfigurationUtil.createMockConfig(); mDockManagerFake = spy(new DockManagerFake()); - mDockHandler = new DozeDockHandler(mConfig, mMachine, mDockManagerFake); + mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake); + mDockHandler.setDozeMachine(mMachine); when(mMachine.getState()).thenReturn(State.DOZE_AOD); doReturn(true).when(mConfig).alwaysOnEnabled(anyInt()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 1f07f46bf764..8078b6c8bda4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -88,8 +88,7 @@ public class DozeMachineTest extends SysuiTestCase { mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog, mDockManager, - mHost); - mMachine.setParts(new DozeMachine.Part[]{mPartMock}); + mHost, new DozeMachine.Part[]{mPartMock}); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 3ef60274cd76..3e60e016a0a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -40,14 +40,17 @@ import android.content.Intent; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; +import android.testing.AndroidTestingRunner; import android.view.Display; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; -import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.FakeThreadFactory; +import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.FakeSensorManager; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -55,22 +58,24 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(AndroidJUnit4.class) @SmallTest +@RunWith(AndroidTestingRunner.class) public class DozeScreenBrightnessTest extends SysuiTestCase { - static final int DEFAULT_BRIGHTNESS = 10; - static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4}; - static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0}; + private static final int DEFAULT_BRIGHTNESS = 10; + private static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4}; + private static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0}; - DozeServiceFake mServiceFake; - FakeSensorManager.FakeGenericSensor mSensor; - FakeSensorManager mSensorManager; + private DozeServiceFake mServiceFake; + private FakeSensorManager.FakeGenericSensor mSensor; + private AsyncSensorManager mSensorManager; + private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy; @Mock DozeHost mDozeHost; - @Mock - BroadcastDispatcher mBroadcastDispatcher; - DozeScreenBrightness mScreen; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor); + + private DozeScreenBrightness mScreen; @Before public void setUp() throws Exception { @@ -83,12 +88,17 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { return null; }).when(mDozeHost).prepareForGentleSleep(any()); mServiceFake = new DozeServiceFake(); - mSensorManager = new FakeSensorManager(mContext); - mSensor = mSensorManager.getFakeLightSensor(); + FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext); + mSensorManager = new AsyncSensorManager(fakeSensorManager, mFakeThreadFactory, null); + + mAlwaysOnDisplayPolicy = new AlwaysOnDisplayPolicy(mContext); + mAlwaysOnDisplayPolicy.defaultDozeBrightness = DEFAULT_BRIGHTNESS; + mAlwaysOnDisplayPolicy.screenBrightnessArray = SENSOR_TO_BRIGHTNESS; + mAlwaysOnDisplayPolicy.dimmingScrimArray = SENSOR_TO_OPACITY; + mSensor = fakeSensorManager.getFakeLightSensor(); mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - mSensor.getSensor(), mBroadcastDispatcher, mDozeHost, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, - true /* debuggable */); + mSensor.getSensor(), mDozeHost, null /* handler */, + mAlwaysOnDisplayPolicy); mScreen.onScreenState(Display.STATE_ON); } @@ -106,6 +116,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(3); @@ -116,6 +127,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testAod_usesDebugValue() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS); intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1); @@ -141,6 +154,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -153,9 +167,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, - true /* debuggable */); + null /* sensor */, mDozeHost, null /* handler */, + mAlwaysOnDisplayPolicy); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); reset(mDozeHost); @@ -174,6 +187,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE); mScreen.transitionTo(DOZE_PULSE_DONE, DOZE); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -183,9 +197,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, - true /* debuggable */); + null /* sensor */, mDozeHost, null /* handler */, + mAlwaysOnDisplayPolicy); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); @@ -198,6 +211,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.transitionTo(DOZE_AOD, FINISH); + waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -205,10 +219,11 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() throws Exception { + public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(1); mSensor.sendSensorEvent(0); @@ -222,6 +237,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(2); @@ -233,6 +249,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { reset(mDozeHost); mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(2); verify(mDozeHost).setAodDimmingScrim(eq(0f)); } @@ -242,6 +259,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); + waitForSensorManager(); mSensor.sendSensorEvent(2); mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); @@ -251,4 +269,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); verify(mDozeHost).setAodDimmingScrim(eq(0f)); } + + private void waitForSensorManager() { + mFakeExecutor.runAllReady(); + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index ebd2c3afe646..7ebead8a33fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -152,7 +152,7 @@ public class DozeSensorsTest extends SysuiTestCase { private class TestableDozeSensors extends DozeSensors { TestableDozeSensors() { - super(getContext(), mAlarmManager, mSensorManager, mDozeParameters, + super(getContext(), mSensorManager, mDozeParameters, mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog, mProximitySensor); for (TriggerSensor sensor : mSensors) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 655f933d28fe..d3af835873e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -30,9 +30,7 @@ import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; @@ -43,6 +41,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.FakeProximitySensor; import com.android.systemui.util.sensors.FakeSensorManager; @@ -92,15 +91,16 @@ public class DozeTriggersTest extends SysuiTestCase { mTapSensor = mSensors.getFakeTapSensor().getSensor(); WakeLock wakeLock = new WakeLockFake(); AsyncSensorManager asyncSensorManager = - new AsyncSensorManager(mSensors, null, new Handler()); + new AsyncSensorManager(mSensors, new FakeThreadFactory(mExecutor), null); FakeThresholdSensor thresholdSensor = new FakeThresholdSensor(); thresholdSensor.setLoaded(true); mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor); - mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters, - asyncSensorManager, wakeLock, true, mDockManager, mProximitySensor, + mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters, + asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher); + mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -186,6 +186,6 @@ public class DozeTriggersTest extends SysuiTestCase { } private void waitForSensorManager() { - TestableLooper.get(this).processAllMessages(); + mExecutor.runAllReady(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index c5bddc1f096f..069699c271f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -81,8 +81,9 @@ public class DozeUiTest extends SysuiTestCase { mWakeLock = new WakeLockFake(); mHandler = mHandlerThread.getThreadHandler(); - mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, + mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, mDozeParameters, mKeyguardUpdateMonitor, mDozeLog); + mDozeUi.setDozeMachine(mMachine); } @After @@ -136,8 +137,9 @@ public class DozeUiTest extends SysuiTestCase { reset(mDozeParameters); reset(mHost); when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); - mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, + mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, mDozeParameters, mKeyguardUpdateMonitor, mDozeLog); + mDozeUi.setDozeMachine(mMachine); // Never animate if display doesn't support it. mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index dbc5596d9f4e..492b33e3c4a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import android.graphics.Color; @@ -47,6 +48,7 @@ import java.util.Map; public class MediaDataCombineLatestTest extends SysuiTestCase { private static final String KEY = "TEST_KEY"; + private static final String OLD_KEY = "TEST_KEY_OLD"; private static final String APP = "APP"; private static final String PACKAGE = "PKG"; private static final int BG_COLOR = Color.RED; @@ -97,7 +99,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutMedia() { // WHEN device source emits an event without media data - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -105,7 +107,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterDeviceFirst() { // GIVEN that a device event has already been received - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received mDataListener.onMediaDataLoaded(KEY, null, mMediaData); // THEN the listener receives a combined event @@ -119,7 +121,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { // GIVEN that media event has already been received mDataListener.onMediaDataLoaded(KEY, null, mMediaData); // WHEN device event is received - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -127,6 +129,64 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { } @Test + public void migrateKeyMediaFirst() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + reset(mListener); + // WHEN a key migration event is received + mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + // THEN the listener receives a combined event + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void migrateKeyDeviceFirst() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + reset(mListener); + // WHEN a key migration event is received + mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + // THEN the listener receives a combined event + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void migrateKeyMediaAfter() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + reset(mListener); + // WHEN a second key migration event is received for media + mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + // THEN the key has already been migrated + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void migrateKeyDeviceAfter() { + // GIVEN that media and device info has already been received + mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + reset(mListener); + // WHEN a second key migration event is received for the device + mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + // THEN the key has already be migrated + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test public void mediaDataRemoved() { // WHEN media data is removed without first receiving device or data mDataListener.onMediaDataRemoved(KEY); @@ -143,7 +203,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemovedAfterDeviceEvent() { - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); mDataListener.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @@ -152,7 +212,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); // THEN the listener gets a load event with the correct keys @@ -163,7 +223,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void getDataIncludesDevice() { // GIVEN that device and media events have been received - mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); mDataListener.onMediaDataLoaded(KEY, null, mMediaData); // THEN the result of getData includes device info diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 6761b282b26a..59c2d0e86c56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -31,6 +31,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever private const val KEY = "KEY" +private const val KEY_2 = "KEY_2" private const val PACKAGE_NAME = "com.android.systemui" private const val APP_NAME = "SystemUI" private const val SESSION_ARTIST = "artist" @@ -156,8 +157,43 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed mediaDataManager.onNotificationRemoved(KEY) - // THEN the media data indicates that it is + // THEN the media data indicates that it is for resumption + assertThat(listener.data!!.resumption).isTrue() + // AND the new key is the package name + assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.oldKey!!).isEqualTo(KEY) + assertThat(listener.removedKey).isNull() + } + + @Test + fun testOnNotificationRemoved_twoWithResumption() { + // GIVEN that the manager has two notifications with resume actions + val listener = TestListener() + mediaDataManager.addListener(listener) + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + mediaDataManager.onNotificationAdded(KEY_2, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) + val data = listener.data!! + assertThat(data.resumption).isFalse() + val resumableData = data.copy(resumeAction = Runnable {}) + mediaDataManager.onMediaDataLoaded(KEY, null, resumableData) + mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData) + // WHEN the first is removed + mediaDataManager.onNotificationRemoved(KEY) + // THEN the data is for resumption and the key is migrated to the package name + assertThat(listener.data!!.resumption).isTrue() + assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.oldKey!!).isEqualTo(KEY) + assertThat(listener.removedKey).isNull() + // WHEN the second is removed + mediaDataManager.onNotificationRemoved(KEY_2) + // THEN the data is for resumption and the second key is removed assertThat(listener.data!!.resumption).isTrue() + assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME) + assertThat(listener.removedKey!!).isEqualTo(KEY_2) } @Test @@ -190,6 +226,7 @@ class MediaDataManagerTest : SysuiTestCase() { var data: MediaData? = null var key: String? = null var oldKey: String? = null + var removedKey: String? = null override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { this.key = key @@ -198,9 +235,7 @@ class MediaDataManagerTest : SysuiTestCase() { } override fun onMediaDataRemoved(key: String) { - this.key = key - oldKey = null - data = null + removedKey = key } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index fc22eeb3ea68..3c6e19f0ec6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -166,7 +166,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { // THEN the listener for the old key should removed. verify(lmm).unregisterCallback(any()) // AND a new device event emitted - val data = captureDeviceData(KEY) + val data = captureDeviceData(KEY, KEY_OLD) assertThat(data.enabled).isTrue() assertThat(data.name).isEqualTo(DEVICE_NAME) } @@ -179,13 +179,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() { // WHEN the new key is the same as the old key manager.onMediaDataLoaded(KEY, KEY, mediaData) // THEN no event should be emitted - verify(listener, never()).onMediaDeviceChanged(eq(KEY), any()) + verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any()) } @Test fun unknownOldKey() { - manager.onMediaDataLoaded(KEY, "unknown", mediaData) - verify(listener).onMediaDeviceChanged(eq(KEY), any()) + val oldKey = "unknown" + manager.onMediaDataLoaded(KEY, oldKey, mediaData) + verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any()) } @Test @@ -223,7 +224,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { manager.removeListener(listener) // THEN it doesn't receive device events manager.onMediaDataLoaded(KEY, null, mediaData) - verify(listener, never()).onMediaDeviceChanged(eq(KEY), any()) + verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any()) } @Test @@ -318,9 +319,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() { return captor.getValue() } - fun captureDeviceData(key: String): MediaDeviceData { + fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData { val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java) - verify(listener).onMediaDeviceChanged(eq(key), captor.capture()) + verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture()) return captor.getValue() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java index 2d9fa1bbd5ca..583d0692565f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java @@ -16,7 +16,7 @@ package com.android.systemui.onehanded; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -52,12 +52,12 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mOneHandedAnimationController = new OneHandedAnimationController(mContext, + mOneHandedAnimationController = new OneHandedAnimationController( new OneHandedSurfaceTransactionHelper(mContext)); } @Test - public void testGetAnimator_withSameBounds_returnTranslateAnimator() { + public void testGetAnimator_withSameBounds_returnAnimator() { final Rect originalBounds = new Rect(0, 0, TEST_BOUNDS_WIDTH, TEST_BOUNDS_HEIGHT); final Rect destinationBounds = originalBounds; destinationBounds.offset(0, 300); @@ -65,7 +65,6 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { mOneHandedAnimationController .getAnimator(mMockLeash, originalBounds, destinationBounds); - assertEquals("Expected translate animation", - OneHandedAnimationController.ANIM_TYPE_TRANSLATE, animator.getAnimationType()); + assertNotNull(animator); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java index 28dad14f221a..07a32a977f82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -29,11 +29,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.DisplayInfo; +import android.view.Display; +import android.view.Surface; import android.view.SurfaceControl; import android.window.DisplayAreaInfo; import android.window.IWindowContainerToken; @@ -58,7 +58,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { static final int DISPLAY_HEIGHT = 1000; DisplayAreaInfo mDisplayAreaInfo; - DisplayInfo mDisplayInfo; + Display mDisplay; Handler mUpdateHandler; OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator; @@ -80,20 +80,13 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext, - mMockDisplayController, - mMockAnimationController, - mMockSurfaceTransactionHelper); - mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler()); mToken = new WindowContainerToken(mMockRealToken); mLeash = new SurfaceControl(); + mDisplay = mContext.getDisplay(); mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED); mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT; - mDisplayInfo = new DisplayInfo(); - mDisplayInfo.logicalWidth = DISPLAY_WIDTH; - mDisplayInfo.logicalHeight = DISPLAY_HEIGHT; - when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null); + when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn( mMockSurfaceTransactionHelper); when(mMockSurfaceTransactionHelper.crop(any(), any(), any())).thenReturn( @@ -106,8 +99,11 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator); when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH); when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT); - when(mMockDisplayController.getDisplay(anyInt())).thenReturn(null); + mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext, + mMockDisplayController, + mMockAnimationController); + mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler()); } @Test @@ -135,7 +131,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo); - assertThat(mDisplayAreaOrganizer.mDisplayAreaInfo).isEqualTo(newDisplayAreaInfo); + assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue(); } @Test @@ -154,13 +150,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { public void testResetImmediately() { // To prevent mNativeObject of Surface is null in the test flow when(mMockLeash.isValid()).thenReturn(false); - final Rect newBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); - final DisplayAreaInfo newDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, - FEATURE_ONE_HANDED); - newDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE; - - mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mMockLeash); - mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo); + mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90); assertThat(mUpdateHandler.hasMessages( OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java index f4fc7eafe247..b6b2217837b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.Display; import androidx.test.filters.SmallTest; @@ -45,6 +46,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class OneHandedManagerImplTest extends OneHandedTestCase { + Display mDisplay; OneHandedManagerImpl mOneHandedManagerImpl; OneHandedTimeoutHandler mTimeoutHandler; @@ -53,8 +55,6 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @Mock - OneHandedSurfaceTransactionHelper mMockSurfaceTransactionHelper; - @Mock OneHandedTouchHandler mMockTouchHandler; @Mock OneHandedGestureHandler mMockGestureHandler; @@ -64,6 +64,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mDisplay = mContext.getDisplay(); mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(), mMockDisplayController, mMockDisplayAreaOrganizer, @@ -72,19 +73,18 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { mMockSysUiState); mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get()); + when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); } - @Test public void testDefaultShouldNotInOneHanded() { final OneHandedSurfaceTransactionHelper transactionHelper = new OneHandedSurfaceTransactionHelper(mContext); final OneHandedAnimationController animationController = new OneHandedAnimationController( - mContext, transactionHelper); + transactionHelper); OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( - mContext, mMockDisplayController, animationController, - mMockSurfaceTransactionHelper); + mContext, mMockDisplayController, animationController); assertThat(displayAreaOrganizer.isInOneHanded()).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java index e6287e7063d3..7eeae67c9fdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java @@ -38,8 +38,8 @@ import android.widget.RemoteViews; import androidx.palette.graphics.Palette; import androidx.test.runner.AndroidJUnit4; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.tests.R; import org.junit.After; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 25da74137a90..a2f8c1cb0ad3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -50,7 +50,9 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; import androidx.test.filters.Suppress; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; @@ -59,7 +61,6 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.policy.SmartReplyConstants; -import com.android.systemui.tests.R; import org.junit.Assert; import org.junit.Before; @@ -110,6 +111,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { () -> smartReplyConstants, () -> smartReplyController, mConversationNotificationProcessor, + mock(MediaFeatureFlag.class), mock(Executor.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 5640f08264a6..c5374b2eb049 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; @@ -86,6 +87,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; @@ -120,8 +122,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private NotificationMediaManager mNotificationMediaManager; - @Mock private ExpandableNotificationRowComponent.Builder - mExpandableNotificationRowComponentBuilder; + @Mock(answer = Answers.RETURNS_SELF) + private ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder; @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent; @Mock private FalsingManager mFalsingManager; @Mock private KeyguardBypassController mKeyguardBypassController; @@ -197,6 +199,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { () -> mock(SmartReplyConstants.class), () -> mock(SmartReplyController.class), mock(ConversationNotificationProcessor.class), + mock(MediaFeatureFlag.class), mBgExecutor); mRowContentBindStage = new RowContentBindStage( binder, @@ -209,21 +212,9 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { when(mExpandableNotificationRowComponentBuilder .expandableNotificationRow(viewCaptor.capture())) .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .notificationEntry(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .onDismissRunnable(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .rowContentBindStage(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder - .onExpandClickListener(any())) - .thenReturn(mExpandableNotificationRowComponentBuilder); - when(mExpandableNotificationRowComponentBuilder.build()) .thenReturn(mExpandableNotificationRowComponent); + when(mExpandableNotificationRowComponent.getExpandableNotificationRowController()) .thenAnswer((Answer<ExpandableNotificationRowController>) invocation -> new ExpandableNotificationRowController( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 362914d6f848..8ccbb2ebb0db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -43,9 +43,11 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.systemui.R; import com.android.systemui.TestableDependency; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; +import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; @@ -68,7 +70,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.SmartReplyConstants; -import com.android.systemui.tests.R; import org.mockito.ArgumentCaptor; @@ -133,6 +134,7 @@ public class NotificationTestHelper { () -> mock(SmartReplyConstants.class), () -> mock(SmartReplyController.class), mock(ConversationNotificationProcessor.class), + mock(MediaFeatureFlag.class), mock(Executor.class)); contentBinder.setInflateSynchronously(true); mBindStage = new RowContentBindStage(contentBinder, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java index 45f7c5a6fdc0..a147c8d0f121 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java @@ -24,10 +24,10 @@ import android.widget.RemoteViews; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.tests.R; import org.junit.Assert; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java index 5ce209b1f249..4d6922c02c41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java @@ -15,12 +15,13 @@ package com.android.systemui.statusbar.policy; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import android.app.AppOpsManager; import android.content.Intent; import android.location.LocationManager; import android.testing.AndroidTestingRunner; @@ -31,12 +32,15 @@ import androidx.test.filters.SmallTest; import com.android.systemui.BootCompleteCache; import com.android.systemui.SysuiTestCase; +import com.android.systemui.appops.AppOpsController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -46,11 +50,15 @@ public class LocationControllerImplTest extends SysuiTestCase { private LocationControllerImpl mLocationController; private TestableLooper mTestableLooper; + @Mock private AppOpsController mAppOpsController; + @Before public void setup() { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); mLocationController = spy(new LocationControllerImpl(mContext, - mTestableLooper.getLooper(), + mAppOpsController, mTestableLooper.getLooper(), mock(BroadcastDispatcher.class), mock(BootCompleteCache.class))); @@ -67,12 +75,12 @@ public class LocationControllerImplTest extends SysuiTestCase { mLocationController.addCallback(callback); mLocationController.addCallback(mock(LocationChangeCallback.class)); - when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(false); - mLocationController.onReceive(mContext, new Intent( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION)); - when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(true); - mLocationController.onReceive(mContext, new Intent( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION)); + doReturn(false).when(mLocationController).areActiveHighPowerLocationRequests(); + mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0, + "", false); + doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests(); + mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0, + "", true); mTestableLooper.processAllMessages(); } @@ -107,11 +115,22 @@ public class LocationControllerImplTest extends SysuiTestCase { LocationChangeCallback callback = mock(LocationChangeCallback.class); mLocationController.addCallback(callback); + + mTestableLooper.processAllMessages(); + mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION)); mTestableLooper.processAllMessages(); verify(callback, times(2)).onLocationSettingsChanged(anyBoolean()); + + doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests(); + mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0, + "", true); + + mTestableLooper.processAllMessages(); + + verify(callback, times(1)).onLocationActiveChanged(anyBoolean()); } @Test @@ -124,6 +143,8 @@ public class LocationControllerImplTest extends SysuiTestCase { verify(callback).onLocationSettingsChanged(anyBoolean()); mLocationController.removeCallback(callback); + mTestableLooper.processAllMessages(); + mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION)); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java new file mode 100644 index 000000000000..8c9248222014 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java @@ -0,0 +1,40 @@ +/* + * 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.concurrency; + +import java.util.concurrent.Executor; + +/** + * Implementation of {@link ThreadFactory} that returns {@link FakeExecutor} where it can. + */ +public class FakeThreadFactory implements ThreadFactory { + private final FakeExecutor mFakeExecutor; + + public FakeThreadFactory(FakeExecutor fakeExecutor) { + mFakeExecutor = fakeExecutor; + } + + @Override + public Executor buildExecutorOnNewThread(String threadName) { + return mFakeExecutor; + } + + @Override + public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) { + return mFakeExecutor; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java index 9149599f2c7c..0d8dd2c0f140 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java @@ -23,15 +23,16 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.hardware.SensorEventListener; -import android.os.Handler; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.FakeThreadFactory; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -39,20 +40,20 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class AsyncSensorManagerTest extends SysuiTestCase { private AsyncSensorManager mAsyncSensorManager; private SensorEventListener mListener; private FakeSensorManager.FakeProximitySensor mSensor; private PluginManager mPluginManager; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); @Before public void setUp() throws Exception { mPluginManager = mock(PluginManager.class); FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext); mAsyncSensorManager = new AsyncSensorManager( - fakeSensorManager, mPluginManager, new Handler()); + fakeSensorManager, new FakeThreadFactory(mFakeExecutor), mPluginManager); mSensor = fakeSensorManager.getFakeProximitySensor(); mListener = mock(SensorEventListener.class); } @@ -99,6 +100,6 @@ public class AsyncSensorManagerTest extends SysuiTestCase { } public void waitUntilRequestsCompleted() { - TestableLooper.get(this).processAllMessages(); + mFakeExecutor.runAllReady(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java index 8ba7d62ba843..d3a35a735f6d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java @@ -20,12 +20,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.os.Handler; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.Assert; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.FakeThreadFactory; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -33,21 +35,20 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class ThresholdSensorImplTest extends SysuiTestCase { private ThresholdSensorImpl mThresholdSensor; private FakeSensorManager mSensorManager; private AsyncSensorManager mAsyncSensorManager; private FakeSensorManager.FakeProximitySensor mFakeProximitySensor; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); @Before public void setUp() throws Exception { - allowTestableLooperAsMainThread(); mSensorManager = new FakeSensorManager(getContext()); mAsyncSensorManager = new AsyncSensorManager( - mSensorManager, null, new Handler()); + mSensorManager, new FakeThreadFactory(mFakeExecutor), null); mFakeProximitySensor = mSensorManager.getFakeProximitySensor(); ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder( @@ -60,6 +61,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testSingleListener() { + Assert.setTestThread(Thread.currentThread()); TestableListener listener = new TestableListener(); assertFalse(mThresholdSensor.isRegistered()); @@ -81,6 +83,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testMultiListener() { + Assert.setTestThread(Thread.currentThread()); TestableListener listenerA = new TestableListener(); TestableListener listenerB = new TestableListener(); @@ -114,6 +117,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testDuplicateListener() { + Assert.setTestThread(Thread.currentThread()); TestableListener listenerA = new TestableListener(); assertFalse(mThresholdSensor.isRegistered()); @@ -138,6 +142,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { } @Test public void testUnregister() { + Assert.setTestThread(Thread.currentThread()); TestableListener listener = new TestableListener(); assertFalse(mThresholdSensor.isRegistered()); @@ -157,6 +162,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testPauseAndResume() { + Assert.setTestThread(Thread.currentThread()); TestableListener listener = new TestableListener(); assertFalse(mThresholdSensor.isRegistered()); @@ -199,6 +205,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testAlertListeners() { + Assert.setTestThread(Thread.currentThread()); TestableListener listenerA = new TestableListener(); TestableListener listenerB = new TestableListener(); @@ -230,6 +237,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testHysteresis() { + Assert.setTestThread(Thread.currentThread()); float lowValue = 10f; float highValue = 100f; FakeSensorManager.FakeGenericSensor sensor = mSensorManager.getFakeLightSensor(); @@ -278,6 +286,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { @Test public void testAlertAfterPause() { + Assert.setTestThread(Thread.currentThread()); TestableListener listener = new TestableListener(); mThresholdSensor.register(listener); @@ -307,7 +316,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { } private void waitForSensorManager() { - TestableLooper.get(this).processAllMessages(); + mFakeExecutor.runAllReady(); } } diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 74df11370e50..e10bab4b36b5 100644 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -42,6 +42,7 @@ import android.net.dhcp.DhcpPacket; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import android.os.SystemProperties; import android.system.Os; import android.util.Log; @@ -224,9 +225,19 @@ public class EthernetTetheringTest { } + private boolean isAdbOverNetwork() { + // If adb TCP port opened, this test may running by adb over network. + return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) + || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); + } + @Test public void testPhysicalEthernet() throws Exception { assumeTrue(mEm.isAvailable()); + // Do not run this test if adb is over network and ethernet is connected. + // It is likely the adb run over ethernet, the adb would break when ethernet is switching + // from client mode to server mode. See b/160389275. + assumeFalse(isAdbOverNetwork()); // Get an interface to use. final String iface = mTetheredInterfaceRequester.getInterface(); diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index c82dff22e328..a4c5a2341d77 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -295,6 +295,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mEventTypes = info.eventTypes; mFeedbackType = info.feedbackType; String[] packageNames = info.packageNames; + mPackageNames.clear(); if (packageNames != null) { mPackageNames.addAll(Arrays.asList(packageNames)); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 468e93a8f683..669bb24e0e77 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -747,6 +747,13 @@ public class AccessibilityWindowManager { * Dumps all {@link AccessibilityWindowInfo}s here. */ void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) { + pw.append("Global Info [ "); + pw.println("Top focused display Id = " + mTopFocusedDisplayId); + pw.println(" Active Window Id = " + mActiveWindowId); + pw.println(" Top Focused Window Id = " + mTopFocusedWindowId); + pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId + + " ]"); + pw.println(); if (mWindows != null) { final int windowCount = mWindows.size(); for (int j = 0; j < windowCount; j++) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java index 68eeb0a3ca2e..b2daae48bb0e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java @@ -103,6 +103,8 @@ final class AutofillInlineSuggestionsRequestSession { private boolean mDestroyed = false; @GuardedBy("mLock") private boolean mPreviousHasNonPinSuggestionShow; + @GuardedBy("mLock") + private boolean mImeSessionInvalidated = false; AutofillInlineSuggestionsRequestSession( @NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId, @@ -157,7 +159,7 @@ final class AutofillInlineSuggestionsRequestSession { Slog.d(TAG, "onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId()); } - if (mImeRequest == null || mResponseCallback == null) { + if (mImeRequest == null || mResponseCallback == null || mImeSessionInvalidated) { return false; } // TODO(b/151123764): each session should only correspond to one field. @@ -191,6 +193,7 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed) { return; } + mImeSessionInvalidated = false; if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId); mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId, new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras), @@ -291,6 +294,7 @@ final class AutofillInlineSuggestionsRequestSession { return; } mImeRequestReceived = true; + mImeSessionInvalidated = false; if (request != null && callback != null) { mImeRequest = request; @@ -346,6 +350,20 @@ final class AutofillInlineSuggestionsRequestSession { } } + /** + * Handles the IME session status received from the IME. + * + * <p> Should only be invoked in the {@link #mHandler} thread. + */ + private void handleOnReceiveImeSessionInvalidated() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + mImeSessionInvalidated = true; + } + } + private static final class InlineSuggestionsRequestCallbackImpl extends IInlineSuggestionsRequestCallback.Stub { @@ -433,6 +451,18 @@ final class AutofillInlineSuggestionsRequestSession { session, false, false)); } } + + @BinderThread + @Override + public void onInlineSuggestionsSessionInvalidated() throws RemoteException { + if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called."); + final AutofillInlineSuggestionsRequestSession session = mSession.get(); + if (session != null) { + session.mHandler.sendMessage(obtainMessage( + AutofillInlineSuggestionsRequestSession + ::handleOnReceiveImeSessionInvalidated, session)); + } + } } private static boolean match(@Nullable AutofillId autofillId, diff --git a/services/backup/OWNERS b/services/backup/OWNERS index ba61d1c0849b..7c7e74285bf5 100644 --- a/services/backup/OWNERS +++ b/services/backup/OWNERS @@ -1,7 +1,12 @@ # Bug component: 656484 +aabhinav@google.com alsutton@google.com bryanmawhinney@google.com +jstemmer@google.com nathch@google.com +niagra@google.com +niamhfw@google.com +philippov@google.com rthakohov@google.com tobiast@google.com diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 7334c86d90ac..84bd59b7730e 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -43,8 +43,6 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIE import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST; -import static android.os.storage.StorageManager.PROP_FUSE; -import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; @@ -131,7 +129,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.DataUnit; -import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -233,13 +230,6 @@ class StorageManagerService extends IStorageManager.Stub */ private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled"; - /** - * If {@code 1}, enables FuseDaemon to intercept file system ops. If {@code -1}, - * disables FuseDaemon. If {@code 0}, uses the default value from the build system. - */ - private static final String FUSE_ENABLED = "fuse_enabled"; - private static final boolean DEFAULT_FUSE_ENABLED = true; - @GuardedBy("mLock") private final Set<Integer> mFuseMountedUser = new ArraySet<>(); @@ -609,8 +599,6 @@ class StorageManagerService extends IStorageManager.Stub // Not guarded by a lock. private final StorageSessionController mStorageSessionController; - private final boolean mIsFuseEnabled; - private final boolean mVoldAppDataIsolationEnabled; @GuardedBy("mLock") @@ -926,7 +914,6 @@ class StorageManagerService extends IStorageManager.Stub DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, mContext.getMainExecutor(), (properties) -> { refreshIsolatedStorageSettings(); - refreshFuseSettings(); }); refreshIsolatedStorageSettings(); } @@ -993,27 +980,6 @@ class StorageManagerService extends IStorageManager.Stub } /** - * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is - * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse) - * whenever the user reboots. - */ - private void refreshFuseSettings() { - int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - FUSE_ENABLED, 0); - if (isFuseEnabled == 1) { - Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on"); - SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX - + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true"); - } else if (isFuseEnabled == -1) { - Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off"); - SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX - + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false"); - } - // else, keep the build config. - // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse - } - - /** * MediaProvider has a ton of code that makes assumptions about storage * paths never changing, so we outright kill them to pick up new state. */ @@ -1091,13 +1057,9 @@ class StorageManagerService extends IStorageManager.Stub final UserManager userManager = mContext.getSystemService(UserManager.class); final List<UserInfo> users = userManager.getUsers(); - if (mIsFuseEnabled) { - mStorageSessionController.onReset(mVold, () -> { - mHandler.removeCallbacksAndMessages(null); - }); - } else { - killMediaProvider(users); - } + mStorageSessionController.onReset(mVold, () -> { + mHandler.removeCallbacksAndMessages(null); + }); final int[] systemUnlockedUsers; synchronized (mLock) { @@ -1490,8 +1452,7 @@ class StorageManagerService extends IStorageManager.Stub final ActivityManagerInternal amInternal = LocalServices.getService(ActivityManagerInternal.class); - if (mIsFuseEnabled && vol.mountUserId >= 0 - && !amInternal.isUserRunning(vol.mountUserId, 0)) { + if (vol.mountUserId >= 0 && !amInternal.isUserRunning(vol.mountUserId, 0)) { Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user " + Integer.toString(vol.mountUserId) + " is no longer running."); return; @@ -1803,12 +1764,8 @@ class StorageManagerService extends IStorageManager.Stub SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); - // If there is no value in the property yet (first boot after data wipe), this value may be - // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if - // different - mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED); - mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean( - ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); + mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( + ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mContext = context; mResolver = mContext.getContentResolver(); mCallbacks = new Callbacks(FgThread.get().getLooper()); @@ -1821,7 +1778,7 @@ class StorageManagerService extends IStorageManager.Stub // Add OBB Action Handler to StorageManagerService thread. mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); - mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled); + mStorageSessionController = new StorageSessionController(mContext); mInstaller = new Installer(mContext); mInstaller.onStart(); @@ -1869,26 +1826,6 @@ class StorageManagerService extends IStorageManager.Stub PackageManager.FEATURE_AUTOMOTIVE); } - /** - * Checks if user changed the persistent settings_fuse flag from Settings UI - * and updates PROP_FUSE (reboots if changed). - */ - private void updateFusePropFromSettings() { - boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE, - DEFAULT_FUSE_ENABLED); - Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag - + ". Default: " + DEFAULT_FUSE_ENABLED); - - if (mIsFuseEnabled != settingsFuseFlag) { - Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag); - // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like - // init, zygote, installd and vold - SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag)); - // Then perform hard reboot to kick policy into place - mContext.getSystemService(PowerManager.class).reboot("fuse_prop"); - } - } - private void start() { connectStoraged(); connectVold(); @@ -1987,15 +1924,6 @@ class StorageManagerService extends IStorageManager.Stub if (provider != null) { mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); } - - if (!mIsFuseEnabled) { - try { - mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, - mAppOpsCallback); - mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback); - } catch (RemoteException e) { - } - } } private ProviderInfo getProviderInfo(String authority) { @@ -2071,7 +1999,6 @@ class StorageManagerService extends IStorageManager.Stub private void bootCompleted() { mBootCompleted = true; mHandler.obtainMessage(H_BOOT_COMPLETED).sendToTarget(); - updateFusePropFromSettings(); } private void handleBootCompleted() { @@ -4269,14 +4196,14 @@ class StorageManagerService extends IStorageManager.Stub return Zygote.MOUNT_EXTERNAL_NONE; } - if (mIsFuseEnabled && mStorageManagerInternal.isExternalStorageService(uid)) { + if (mStorageManagerInternal.isExternalStorageService(uid)) { // Determine if caller requires pass_through mount; note that we do this for // all processes that share a UID with MediaProvider; but this is fine, since // those processes anyway share the same rights as MediaProvider. return Zygote.MOUNT_EXTERNAL_PASS_THROUGH; } - if (mIsFuseEnabled && (mDownloadsAuthorityAppId == UserHandle.getAppId(uid) + if ((mDownloadsAuthorityAppId == UserHandle.getAppId(uid) || mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) { // DownloadManager can write in app-private directories on behalf of apps; // give it write access to Android/ @@ -4286,7 +4213,7 @@ class StorageManagerService extends IStorageManager.Stub final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) == PERMISSION_GRANTED; - if (mIsFuseEnabled && hasMtp) { + if (hasMtp) { ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName, 0, UserHandle.getUserId(uid)); if (ai != null && ai.isSignedWithPlatformKey()) { @@ -4749,27 +4676,25 @@ class StorageManagerService extends IStorageManager.Stub public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) { final long token = Binder.clearCallingIdentity(); try { - if (mIsFuseEnabled) { - // When using FUSE, we may need to kill the app if the op changes - switch(code) { - case OP_REQUEST_INSTALL_PACKAGES: - // Always kill regardless of op change, to remount apps /storage + // When using FUSE, we may need to kill the app if the op changes + switch(code) { + case OP_REQUEST_INSTALL_PACKAGES: + // Always kill regardless of op change, to remount apps /storage + killAppForOpChange(code, uid); + return; + case OP_MANAGE_EXTERNAL_STORAGE: + if (mode != MODE_ALLOWED) { + // Only kill if op is denied, to lose external_storage gid + // Killing when op is granted to pickup the gid automatically, + // results in a bad UX, especially since the gid only gives access + // to unreliable volumes, USB OTGs that are rarely mounted. The app + // will get the external_storage gid on next organic restart. killAppForOpChange(code, uid); - return; - case OP_MANAGE_EXTERNAL_STORAGE: - if (mode != MODE_ALLOWED) { - // Only kill if op is denied, to lose external_storage gid - // Killing when op is granted to pickup the gid automatically, - // results in a bad UX, especially since the gid only gives access - // to unreliable volumes, USB OTGs that are rarely mounted. The app - // will get the external_storage gid on next organic restart. - killAppForOpChange(code, uid); - } - return; - case OP_LEGACY_STORAGE: - updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED); - return; - } + } + return; + case OP_LEGACY_STORAGE: + updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED); + return; } if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 4ff9eb11c142..72f29b431880 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -43,6 +43,7 @@ import android.os.IBinder; import android.os.IExternalVibratorService; import android.os.IVibratorService; import android.os.IVibratorStateListener; +import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; @@ -70,6 +71,7 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; @@ -134,7 +136,7 @@ public class VibratorService extends IVibratorService.Stub private final SparseArray<VibrationEffect> mFallbackEffects; private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); private final WorkSource mTmpWorkSource = new WorkSource(); - private final Handler mH = new Handler(); + private final Handler mH; private final Object mLock = new Object(); private final Context mContext; @@ -147,6 +149,7 @@ public class VibratorService extends IVibratorService.Stub private Vibrator mVibrator; private SettingsObserver mSettingObserver; + private final NativeWrapper mNativeWrapper; private volatile VibrateThread mThread; // mInputDeviceVibrators lock should be acquired after mLock, if both are @@ -208,7 +211,12 @@ public class VibratorService extends IVibratorService.Stub } }; - private class Vibration implements IBinder.DeathRecipient { + /** + * Holder for a vibration to be played. This class can be shared with native methods for + * hardware callback support. + */ + @VisibleForTesting + public final class Vibration implements IBinder.DeathRecipient { public final IBinder token; // Start time in CLOCK_BOOTTIME base. public final long startTime; @@ -248,9 +256,9 @@ public class VibratorService extends IVibratorService.Stub } } - // Called by native - @SuppressWarnings("unused") - private void onComplete() { + /** Callback for when vibration is complete, to be called by native. */ + @VisibleForTesting + public void onComplete() { synchronized (mLock) { if (this == mCurrentVibration) { doCancelVibrateLocked(); @@ -354,15 +362,23 @@ public class VibratorService extends IVibratorService.Stub } VibratorService(Context context) { - vibratorInit(); + this(context, new Injector()); + } + + @VisibleForTesting + VibratorService(Context context, Injector injector) { + mNativeWrapper = injector.getNativeWrapper(); + mH = injector.createHandler(Looper.myLooper()); + + mNativeWrapper.vibratorInit(); // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. - vibratorOff(); + mNativeWrapper.vibratorOff(); - mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl(); - mSupportsExternalControl = vibratorSupportsExternalControl(); - mSupportedEffects = asList(vibratorGetSupportedEffects()); - mCapabilities = vibratorGetCapabilities(); + mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl(); + mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl(); + mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects()); + mCapabilities = mNativeWrapper.vibratorGetCapabilities(); mContext = context; PowerManager pm = context.getSystemService(PowerManager.class); @@ -419,7 +435,7 @@ public class VibratorService extends IVibratorService.Stub mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH)); mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH)); - ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); + injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); } private VibrationEffect createEffectFromResource(int resId) { @@ -642,7 +658,7 @@ public class VibratorService extends IVibratorService.Stub if (effect == null) { synchronized (mLock) { mAlwaysOnEffects.delete(alwaysOnId); - vibratorAlwaysOnDisable(alwaysOnId); + mNativeWrapper.vibratorAlwaysOnDisable(alwaysOnId); } } else { if (!verifyVibrationEffect(effect)) { @@ -1198,11 +1214,11 @@ public class VibratorService extends IVibratorService.Stub private void updateAlwaysOnLocked(int id, Vibration vib) { final int intensity = getCurrentIntensityLocked(vib); if (!shouldVibrate(vib, intensity)) { - vibratorAlwaysOnDisable(id); + mNativeWrapper.vibratorAlwaysOnDisable(id); } else { final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; final int strength = intensityToEffectStrength(intensity); - vibratorAlwaysOnEnable(id, prebaked.getId(), strength); + mNativeWrapper.vibratorAlwaysOnEnable(id, prebaked.getId(), strength); } } @@ -1238,7 +1254,7 @@ public class VibratorService extends IVibratorService.Stub //synchronized (mInputDeviceVibrators) { // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); //} - return vibratorExists(); + return mNativeWrapper.vibratorExists(); } private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) { @@ -1262,7 +1278,7 @@ public class VibratorService extends IVibratorService.Stub // Note: ordering is important here! Many haptic drivers will reset their // amplitude when enabled, so we always have to enable first, then set the // amplitude. - vibratorOn(millis); + mNativeWrapper.vibratorOn(millis); doVibratorSetAmplitude(amplitude); } } @@ -1273,7 +1289,7 @@ public class VibratorService extends IVibratorService.Stub private void doVibratorSetAmplitude(int amplitude) { if (mSupportsAmplitudeControl) { - vibratorSetAmplitude(amplitude); + mNativeWrapper.vibratorSetAmplitude(amplitude); } } @@ -1291,7 +1307,7 @@ public class VibratorService extends IVibratorService.Stub mInputDeviceVibrators.get(i).cancel(); } } else { - vibratorOff(); + mNativeWrapper.vibratorOff(); } } } finally { @@ -1310,7 +1326,7 @@ public class VibratorService extends IVibratorService.Stub } // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { - long duration = vibratorPerformEffect(prebaked.getId(), + long duration = mNativeWrapper.vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength(), vib, hasCapability(IVibrator.CAP_PERFORM_CALLBACK)); long timeout = duration; @@ -1363,7 +1379,7 @@ public class VibratorService extends IVibratorService.Stub PrimitiveEffect[] primitiveEffects = composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]); - vibratorPerformComposedEffect(primitiveEffects, vib); + mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib); // Composed effects don't actually give us an estimated duration, so we just guess here. noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length); @@ -1454,7 +1470,7 @@ public class VibratorService extends IVibratorService.Stub } } mVibratorUnderExternalControl = externalControl; - vibratorSetExternalControl(externalControl); + mNativeWrapper.vibratorSetExternalControl(externalControl); } private void dumpInternal(PrintWriter pw) { @@ -1688,6 +1704,100 @@ public class VibratorService extends IVibratorService.Stub } } + /** Wrapper around the static-native methods of {@link VibratorService} for tests. */ + @VisibleForTesting + public static class NativeWrapper { + + /** Checks if vibrator exists on device. */ + public boolean vibratorExists() { + return VibratorService.vibratorExists(); + } + + /** Initializes connection to vibrator HAL service. */ + public void vibratorInit() { + VibratorService.vibratorInit(); + } + + /** Turns vibrator on for given time. */ + public void vibratorOn(long milliseconds) { + VibratorService.vibratorOn(milliseconds); + } + + /** Turns vibrator off. */ + public void vibratorOff() { + VibratorService.vibratorOff(); + } + + /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */ + public boolean vibratorSupportsAmplitudeControl() { + return VibratorService.vibratorSupportsAmplitudeControl(); + } + + /** Sets the amplitude for the vibrator to run. */ + public void vibratorSetAmplitude(int amplitude) { + VibratorService.vibratorSetAmplitude(amplitude); + } + + /** Returns all predefined effects supported by the device vibrator. */ + public int[] vibratorGetSupportedEffects() { + return VibratorService.vibratorGetSupportedEffects(); + } + + /** Turns vibrator on to perform one of the supported effects. */ + public long vibratorPerformEffect(long effect, long strength, Vibration vibration, + boolean withCallback) { + return VibratorService.vibratorPerformEffect(effect, strength, vibration, withCallback); + } + + /** Turns vibrator on to perform one of the supported composed effects. */ + public void vibratorPerformComposedEffect( + VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) { + VibratorService.vibratorPerformComposedEffect(effect, vibration); + } + + /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */ + public boolean vibratorSupportsExternalControl() { + return VibratorService.vibratorSupportsExternalControl(); + } + + /** Enabled the device vibrator to be controlled by another service. */ + public void vibratorSetExternalControl(boolean enabled) { + VibratorService.vibratorSetExternalControl(enabled); + } + + /** Returns all capabilities of the device vibrator. */ + public long vibratorGetCapabilities() { + return VibratorService.vibratorGetCapabilities(); + } + + /** Enable always-on vibration with given id and effect. */ + public void vibratorAlwaysOnEnable(long id, long effect, long strength) { + VibratorService.vibratorAlwaysOnEnable(id, effect, strength); + } + + /** Disable always-on vibration for given id. */ + public void vibratorAlwaysOnDisable(long id) { + VibratorService.vibratorAlwaysOnDisable(id); + } + } + + /** Point of injection for test dependencies */ + @VisibleForTesting + static class Injector { + + NativeWrapper getNativeWrapper() { + return new NativeWrapper(); + } + + Handler createHandler(Looper looper) { + return new Handler(looper); + } + + void addService(String name, IBinder service) { + ServiceManager.addService(name, service); + } + } + BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f00527d91436..25e6eb6e943f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -232,7 +232,6 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Rect; import android.hardware.display.DisplayManagerInternal; -import android.location.LocationManager; import android.media.audiofx.AudioEffect; import android.net.Proxy; import android.net.Uri; @@ -3318,6 +3317,9 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean setProcessMemoryTrimLevel(String process, int userId, int level) throws RemoteException { + if (!isCallerShell()) { + throw new SecurityException("Only shell can call it"); + } synchronized (this) { final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel"); if (app == null) { @@ -15860,7 +15862,6 @@ public class ActivityManagerService extends IActivityManager.Stub || Intent.ACTION_FACTORY_RESET.equals(action) || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action) - || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action) || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action) || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action) || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action) diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 5124c4a4797e..a2eea1348d5c 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -470,22 +470,23 @@ public final class BroadcastQueue { // if this receiver was slow, impose deferral policy on the app. This will kick in // when processNextBroadcastLocked() next finds this uid as a receiver identity. if (!r.timeoutExempt) { - if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { + // r.curApp can be null if finish has raced with process death - benign + // edge case, and we just ignore it because we're already cleaning up + // as expected. + if (r.curApp != null + && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { // Core system packages are exempt from deferral policy if (!UserHandle.isCore(r.curApp.uid)) { if (DEBUG_BROADCAST_DEFERRAL) { Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1) + " was slow: " + receiver + " br=" + r); } - if (r.curApp != null) { - mDispatcher.startDeferring(r.curApp.uid); - } else { - Slog.d(TAG_BROADCAST, "finish receiver curApp is null? " + r); - } + mDispatcher.startDeferring(r.curApp.uid); } else { if (DEBUG_BROADCAST_DEFERRAL) { Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid - + " receiver was slow but not deferring: " + receiver + " br=" + r); + + " receiver was slow but not deferring: " + + receiver + " br=" + r); } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 04089e3a6c30..ebff0691c1f7 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -110,7 +110,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.os.RuntimeInit; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -155,9 +154,6 @@ public final class ProcessList { static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled"; - // A system property to control if fuse is enabled. - static final String ANDROID_FUSE_ENABLED = "persist.sys.fuse"; - // The minimum time we allow between crashes, for us to consider this // application to be bad and stop and its services and reject broadcasts. static final int MIN_CRASH_INTERVAL = 60 * 1000; @@ -719,13 +715,8 @@ public final class ProcessList { // want some apps enabled while some apps disabled mAppDataIsolationEnabled = SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); - boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false); - boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean( - ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); - if (!fuseEnabled && voldAppDataIsolationEnabled) { - Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled"); - } - mVoldAppDataIsolationEnabled = fuseEnabled && voldAppDataIsolationEnabled; + mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( + ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mAppDataIsolationWhitelistedApps = new ArrayList<>( SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 4fd9ae256592..c8e4ee02e603 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1320,6 +1320,11 @@ public class AudioService extends IAudioService.Stub device, caller, true /*hasModifyAudioSettings*/); } mStreamStates[streamType].checkFixedVolumeDevices(); + + // Unmute streams if device is full volume + if (mFullVolumeDevices.contains(device)) { + mStreamStates[streamType].mute(false); + } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index 466465e0a3f5..68cc8845ec5c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -47,12 +47,17 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> private final VibrationEffect mSuccessVibrationEffect; private final VibrationEffect mErrorVibrationEffect; - AcquisitionClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int userId, + /** + * Stops the HAL operation specific to the ClientMonitor subclass. + */ + protected abstract void stopHalOperation(); + + AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, int statsClient) { - super(context, token, listener, userId, owner, cookie, sensorId, statsModality, statsAction, - statsClient); + super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality, + statsAction, statsClient); mPowerManager = context.getSystemService(PowerManager.class); mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); @@ -78,7 +83,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke sendError", e); } - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } /** @@ -110,7 +115,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> } } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke sendAcquired", e); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index a9cefbaf8ac0..df836d599e26 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -52,12 +52,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> { protected boolean mAuthAttempted; - public AuthenticationClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, - boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, - int sensorId, boolean isStrongBiometric, int statsModality, int statsClient, - @NonNull TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker) { - super(context, token, listener, targetUserId, owner, cookie, sensorId, + public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, + int targetUserId, long operationId, boolean restricted, @NonNull String owner, + int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, + int statsModality, int statsClient, @NonNull TaskStackListener taskStackListener, + @NonNull LockoutTracker lockoutTracker) { + super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; @@ -179,7 +180,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> { } } catch (RemoteException e) { Slog.e(TAG, "Unable to notify listener, finishing", e); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } @@ -187,8 +188,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> { * Start authentication */ @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); final @LockoutTracker.LockoutMode int lockoutMode = mLockoutTracker.getLockoutModeForUser(getTargetUserId()); diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java index a784b4b57eea..9f5abd873b4f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java @@ -34,17 +34,12 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricService; -import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.fingerprint.Fingerprint; import android.os.Binder; -import android.os.Bundle; -import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.IHwBinder; -import android.os.IRemoteCallback; import android.os.Looper; -import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -82,11 +77,9 @@ public abstract class BiometricServiceBase<T> extends SystemService private final Context mContext; private final String mKeyguardPackage; protected final IActivityTaskManager mActivityTaskManager; - private final PowerManager mPowerManager; protected final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener(); private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable(); - private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>(); protected final IStatusBarService mStatusBarService; protected final Map<Integer, Long> mAuthenticatorIds = @@ -109,21 +102,14 @@ public abstract class BiometricServiceBase<T> extends SystemService } }; - protected final ClientMonitor.FinishCallback mClientFinishCallback = clientMonitor -> { - if (clientMonitor instanceof RemovalConsumer) { - // When the last biometric of a group is removed, update the authenticator id. - // Note that 1) multiple ClientMonitors may be cause onRemoved (e.g. internal cleanup), - // and 2) updateActiveGroup updates/relies on global state, so there's no good way to - // compartmentalize this yet. - final int userId = clientMonitor.getTargetUserId(); - if (!hasEnrolledBiometrics(userId)) { - Slog.d(getTag(), "Last biometric removed for user: " + userId - + ", updating active group"); - updateActiveGroup(userId); - } - } - + protected final ClientMonitor.FinishCallback mClientFinishCallback = + (clientMonitor, success) -> { removeClient(clientMonitor); + // When enrollment finishes, update this group's authenticator id, as the HAL has + // already generated a new authenticator id when the new biometric is enrolled. + if (clientMonitor instanceof EnrollClient) { + updateActiveGroup(clientMonitor.getTargetUserId()); + } }; private IBiometricService mBiometricService; @@ -263,65 +249,6 @@ public abstract class BiometricServiceBase<T> extends SystemService } } - - - private final class LockoutResetMonitor implements IBinder.DeathRecipient { - private static final long WAKELOCK_TIMEOUT_MS = 2000; - private final IBiometricServiceLockoutResetCallback mCallback; - private final PowerManager.WakeLock mWakeLock; - - public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) { - mCallback = callback; - mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - "lockout reset callback"); - try { - mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0); - } catch (RemoteException e) { - Slog.w(getTag(), "caught remote exception in linkToDeath", e); - } - } - - public void sendLockoutReset() { - if (mCallback != null) { - try { - mWakeLock.acquire(WAKELOCK_TIMEOUT_MS); - mCallback.onLockoutReset(new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) throws RemoteException { - releaseWakelock(); - } - }); - } catch (DeadObjectException e) { - Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e); - mHandler.post(mRemoveCallbackRunnable); - } catch (RemoteException e) { - Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e); - releaseWakelock(); - } - } - } - - private final Runnable mRemoveCallbackRunnable = new Runnable() { - @Override - public void run() { - releaseWakelock(); - removeLockoutResetCallback(LockoutResetMonitor.this); - } - }; - - @Override - public void binderDied() { - Slog.e(getTag(), "Lockout reset callback binder died"); - mHandler.post(mRemoveCallbackRunnable); - } - - private void releaseWakelock() { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } - } - } - /** * Initializes the system service. * <p> @@ -341,7 +268,6 @@ public abstract class BiometricServiceBase<T> extends SystemService mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null; mAppOps = context.getSystemService(AppOpsManager.class); mActivityTaskManager = ActivityTaskManager.getService(); - mPowerManager = mContext.getSystemService(PowerManager.class); mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId()); } @@ -452,15 +378,7 @@ public abstract class BiometricServiceBase<T> extends SystemService } final EnrollClient enrollClient = (EnrollClient) client; - - if (enrollClient.onEnrollResult(identifier, remaining)) { - removeClient(enrollClient); - // When enrollment finishes, update this group's authenticator id, as the HAL has - // already generated a new authenticator id when the new biometric is enrolled. - if (identifier instanceof Fingerprint) { - updateActiveGroup(((Fingerprint)identifier).getGroupId()); - } - } + enrollClient.onEnrollResult(identifier, remaining); } protected void handleError(int error, int vendorCode) { @@ -630,7 +548,8 @@ public abstract class BiometricServiceBase<T> extends SystemService }); } - protected void cleanupInternal(InternalCleanupClient<T> client) { + protected void cleanupInternal( + InternalCleanupClient<? extends BiometricAuthenticator.Identifier, T> client) { mHandler.post(() -> { if (DEBUG) { Slog.v(getTag(), "Cleaning up templates for user(" @@ -647,19 +566,6 @@ public abstract class BiometricServiceBase<T> extends SystemService startClient(client, true /* initiatedByClient */); } - protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) { - if (callback == null) { - Slog.w(getTag(), "Null LockoutResetCallback"); - return; - } - mHandler.post(() -> { - final LockoutResetMonitor monitor = new LockoutResetMonitor(callback); - if (!mLockoutMonitors.contains(monitor)) { - mLockoutMonitors.add(monitor); - } - }); - } - /** * Helper methods. */ @@ -816,7 +722,7 @@ public abstract class BiometricServiceBase<T> extends SystemService return; } - final T daemon = getDaemon(); + final T daemon = mCurrentClient.getFreshDaemon(); if (daemon == null) { Slog.e(getTag(), "Daemon null, unable to start: " + mCurrentClient.getClass().getSimpleName()); @@ -825,7 +731,7 @@ public abstract class BiometricServiceBase<T> extends SystemService return; } - mCurrentClient.start(daemon, mClientFinishCallback); + mCurrentClient.start(mClientFinishCallback); notifyClientActiveCallbacks(true); } @@ -931,12 +837,6 @@ public abstract class BiometricServiceBase<T> extends SystemService doTemplateCleanupForUser(userId); } - protected void notifyLockoutResetMonitors() { - for (int i = 0; i < mLockoutMonitors.size(); i++) { - mLockoutMonitors.get(i).sendLockoutReset(); - } - } - private void listenForUserSwitches() { try { ActivityManager.getService().registerUserSwitchObserver( @@ -951,8 +851,4 @@ public abstract class BiometricServiceBase<T> extends SystemService Slog.w(getTag(), "Failed to listen for user switching event" ,e); } } - - private void removeLockoutResetCallback(LockoutResetMonitor monitor) { - mLockoutMonitors.remove(monitor); - } } diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java index 5a2d63d667db..3f301ccb602e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -47,11 +47,23 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde * implementation. * * @param clientMonitor Reference of the ClientMonitor that finished. + * @param success True if the operation completed successfully. */ - void onClientFinished(ClientMonitor clientMonitor); + void onClientFinished(ClientMonitor clientMonitor, boolean success); + } + + /** + * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL. + */ + public interface LazyDaemon<T> { + /** + * @return A fresh instance to the biometric HAL + */ + T getDaemon(); } @NonNull private final Context mContext; + @NonNull protected final LazyDaemon<T> mLazyDaemon; private final int mTargetUserId; @NonNull private final String mOwner; private final int mSensorId; // sensorId as configured by the framework @@ -63,11 +75,11 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde private final int mCookie; boolean mAlreadyDone; - @NonNull protected T mDaemon; @NonNull protected FinishCallback mFinishCallback; /** * @param context system_server context + * @param lazyDaemon pointer for lazy retrieval of the HAL * @param token a unique token for the client * @param listener recipient of related events (e.g. authentication) * @param userId target user id for operation @@ -78,12 +90,13 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants */ - public ClientMonitor(@NonNull Context context, @Nullable IBinder token, - @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, - int cookie, int sensorId, int statsModality, int statsAction, + public ClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, int statsClient) { super(statsModality, statsAction, statsClient); mContext = context; + mLazyDaemon = lazyDaemon; mToken = token; mListener = listener; mTargetUserId = userId; @@ -107,18 +120,16 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde /** * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null). * If such a problem is detected, the scheduler will not invoke - * {@link #start(Object, FinishCallback)}. + * {@link #start(FinishCallback)}. */ public abstract void unableToStart(); /** * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book * keeping is complete. - * @param daemon reference to the HAL * @param finishCallback invoked when the operation is complete (succeeds, fails, etc) */ - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - mDaemon = daemon; + public void start(@NonNull FinishCallback finishCallback) { mFinishCallback = finishCallback; } @@ -127,11 +138,6 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde */ protected abstract void startHalOperation(); - /** - * Stops the HAL operation specific to the ClientMonitor subclass. - */ - protected abstract void stopHalOperation(); - public boolean isAlreadyDone() { return mAlreadyDone; } @@ -190,4 +196,8 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde public final int getSensorId() { return mSensorId; } + + public final T getFreshDaemon() { + return mLazyDaemon.getDaemon(); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 6637ede62b80..a3d96778d177 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -41,54 +41,46 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> { private long mEnrollmentStartTimeMs; private boolean mAlreadyCancelled; - public EnrollClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int userId, + public EnrollClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, int timeoutSec, int statsModality, int sensorId, boolean shouldVibrate) { - super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality, - BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN); + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, + statsModality, BiometricsProtoEnums.ACTION_ENROLL, + BiometricsProtoEnums.CLIENT_UNKNOWN); mBiometricUtils = utils; mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length); mTimeoutSec = timeoutSec; mShouldVibrate = shouldVibrate; } - public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, - int remaining) { - if (remaining == 0) { - mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier); - logOnEnrolled(getTargetUserId(), - System.currentTimeMillis() - mEnrollmentStartTimeMs, - true /* enrollSuccessful */); - } - notifyUserActivity(); - return sendEnrollResult(identifier, remaining); - } - - /* - * @return true if we're done. - */ - private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { + public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { if (mShouldVibrate) { vibrateSuccess(); } + final ClientMonitorCallbackConverter listener = getListener(); try { - final ClientMonitorCallbackConverter listener = getListener(); if (listener != null) { listener.onEnrollResult(identifier, remaining); } - return remaining == 0; } catch (RemoteException e) { - Slog.w(TAG, "Failed to notify EnrollResult:", e); - return true; + Slog.e(TAG, "Remote exception", e); } + + if (remaining == 0) { + mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier); + logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs, + true /* enrollSuccessful */); + mFinishCallback.onClientFinished(this, true /* success */); + } + notifyUserActivity(); } @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); mEnrollmentStartTimeMs = System.currentTimeMillis(); startHalOperation(); diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index 12584cfa7af3..dad5cad1ed8d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -29,9 +29,10 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> { protected long mChallenge; - public GenerateChallengeClient(Context context, IBinder token, - ClientMonitorCallbackConverter listener, String owner, int sensorId) { - super(context, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId, + public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, + @NonNull String owner, int sensorId) { + super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); } @@ -46,20 +47,16 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> { } @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); startHalOperation(); try { getListener().onChallengeGenerated(mChallenge); + mFinishCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); + mFinishCallback.onClientFinished(this, false /* success */); } - mFinishCallback.onClientFinished(this); - } - - @Override - protected void stopHalOperation() { - // Not supported for GenerateChallenge } } diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index f3ade65d6748..6d7b0fd3d5f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -27,6 +27,7 @@ import com.android.internal.util.FrameworkStatsLog; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the @@ -37,8 +38,8 @@ import java.util.List; * 2) The HAL and Framework are not in sync, and * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/ */ -public abstract class InternalCleanupClient<T> extends ClientMonitor<T> - implements EnumerateConsumer, RemovalConsumer { +public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T> + extends ClientMonitor<T> implements EnumerateConsumer, RemovalConsumer { private static final String TAG = "Biometrics/InternalCleanupClient"; @@ -57,10 +58,11 @@ public abstract class InternalCleanupClient<T> extends ClientMonitor<T> private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>(); private final BiometricUtils mBiometricUtils; - private final List<? extends BiometricAuthenticator.Identifier> mEnrolledList; + private final Map<Integer, Long> mAuthenticatorIds; + private final List<S> mEnrolledList; private ClientMonitor<T> mCurrentTask; - private final FinishCallback mEnumerateFinishCallback = clientMonitor -> { + private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> { final List<BiometricAuthenticator.Identifier> unknownHALTemplates = ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates(); @@ -75,45 +77,46 @@ public abstract class InternalCleanupClient<T> extends ClientMonitor<T> if (mUnknownHALTemplates.isEmpty()) { // No unknown HAL templates. Unknown framework templates are already cleaned up in // InternalEnumerateClient. Finish this client. - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, success); } else { startCleanupUnknownHalTemplates(); } }; - private final FinishCallback mRemoveFinishCallback = clientMonitor -> { - mFinishCallback.onClientFinished(this); + private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> { + mFinishCallback.onClientFinished(this, success); }; - protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, IBinder token, - int userId, String owner, - List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils, - int sensorId); + protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, + LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner, + List<S> enrolledList, BiometricUtils utils, int sensorId); - protected abstract RemovalClient<T> getRemovalClient(Context context, IBinder token, - int biometricId, int userId, String owner, BiometricUtils utils, int sensorId); + protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon, + IBinder token, int biometricId, int userId, String owner, BiometricUtils utils, + int sensorId, Map<Integer, Long> authenticatorIds); - protected InternalCleanupClient(@NonNull Context context, int userId, - @NonNull String owner, int sensorId, int statsModality, - @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, - @NonNull BiometricUtils utils) { - super(context, null /* token */, null /* ClientMonitorCallbackConverter */, + protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + int userId, @NonNull String owner, int sensorId, int statsModality, + @NonNull List<S> enrolledList, @NonNull BiometricUtils utils, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN); mBiometricUtils = utils; + mAuthenticatorIds = authenticatorIds; mEnrolledList = enrolledList; } private void startCleanupUnknownHalTemplates() { UserTemplate template = mUnknownHALTemplates.get(0); mUnknownHALTemplates.remove(template); - mCurrentTask = getRemovalClient(getContext(), getToken(), + mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(), template.mIdentifier.getBiometricId(), template.mUserId, - getContext().getPackageName(), mBiometricUtils, getSensorId()); + getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds); FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, mStatsModality, BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); - mCurrentTask.start(mDaemon, mRemoveFinishCallback); + mCurrentTask.start(mRemoveFinishCallback); } @Override @@ -122,13 +125,13 @@ public abstract class InternalCleanupClient<T> extends ClientMonitor<T> } @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); // Start enumeration. Removal will start if necessary, when enumeration is completed. - mCurrentTask = getEnumerateClient(getContext(), getToken(), getTargetUserId(), + mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(), getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId()); - mCurrentTask.start(daemon, mEnumerateFinishCallback); + mCurrentTask.start(mEnumerateFinishCallback); } @Override @@ -138,11 +141,6 @@ public abstract class InternalCleanupClient<T> extends ClientMonitor<T> } @Override - protected void stopHalOperation() { - // Internal cleanup's cannot be stopped. - } - - @Override public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { if (!(mCurrentTask instanceof RemovalClient)) { Slog.e(TAG, "onRemoved received during client: " diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 9ce271c656c8..3f73cd56e6c3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -43,13 +43,13 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T> // List of templates to remove from the HAL private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>(); - protected InternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId, - @NonNull String owner, + protected InternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, @NonNull BiometricUtils utils, int sensorId, int statsModality) { // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. - super(context, token, null /* ClientMonitorCallbackConverter */, userId, owner, + super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN); mEnrolledList = enrolledList; @@ -62,7 +62,7 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T> handleEnumeratedTemplate(identifier); if (remaining == 0) { doTemplateCleanup(); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, true /* success */); } } @@ -72,8 +72,8 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T> } @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); // The biometric template ids will be removed when we get confirmation from the HAL startHalOperation(); diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java new file mode 100644 index 000000000000..9bb5c6e77332 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java @@ -0,0 +1,122 @@ +/* + * 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.biometrics.sensors; + +import android.content.Context; +import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.PowerManager; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.ArrayList; + +/** + * Allows clients (such as keyguard) to register for notifications on when biometric lockout + * ends. + */ +public class LockoutResetTracker implements IBinder.DeathRecipient { + + private static final String TAG = "LockoutResetTracker"; + + private final Context mContext; + private final ArrayList<ClientCallback> mClientCallbacks; + + private static class ClientCallback { + private static final long WAKELOCK_TIMEOUT_MS = 2000; + + private final String mOpPackageName; + private final IBiometricServiceLockoutResetCallback mCallback; + private final PowerManager.WakeLock mWakeLock; + + ClientCallback(Context context, IBiometricServiceLockoutResetCallback callback, + String opPackageName) { + final PowerManager pm = context.getSystemService(PowerManager.class); + mOpPackageName = opPackageName; + mCallback = callback; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "LockoutResetMonitor:SendLockoutReset"); + } + + void sendLockoutReset() { + if (mCallback != null) { + try { + mWakeLock.acquire(WAKELOCK_TIMEOUT_MS); + mCallback.onLockoutReset(new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) { + releaseWakelock(); + } + }); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to invoke onLockoutReset: ", e); + releaseWakelock(); + } + } + } + + private void releaseWakelock() { + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + } + + public LockoutResetTracker(Context context) { + mContext = context; + mClientCallbacks = new ArrayList<>(); + } + + public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) { + if (callback == null) { + Slog.w(TAG, "Callback from : " + opPackageName + " is null"); + return; + } + + mClientCallbacks.add(new ClientCallback(mContext, callback, opPackageName)); + try { + callback.asBinder().linkToDeath(this, 0 /* flags */); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to link to death", e); + } + } + + @Override + public void binderDied() { + // Do nothing, handled below + } + + @Override + public void binderDied(IBinder who) { + Slog.e(TAG, "Callback binder died: " + who); + for (ClientCallback callback : mClientCallbacks) { + if (callback.mCallback.asBinder().equals(who)) { + Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName); + callback.releaseWakelock(); + mClientCallbacks.remove(callback); + } + } + } + + public void notifyLockoutResetCallbacks() { + for (ClientCallback callback : mClientCallbacks) { + callback.sendLockoutReset(); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index b734516322e4..1c49bcdbadf4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -24,6 +24,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import java.util.Map; + /** * A class to keep track of the remove state for a given client. */ @@ -33,14 +35,18 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov protected final int mBiometricId; private final BiometricUtils mBiometricUtils; + private final Map<Integer, Long> mAuthenticatorIds; - public RemovalClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId, - @NonNull String owner, @NonNull BiometricUtils utils, int sensorId, int statsModality) { - super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality, - BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN); + public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, + int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils, + int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) { + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, + statsModality, BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN); mBiometricId = biometricId; mBiometricUtils = utils; + mAuthenticatorIds = authenticatorIds; } @Override @@ -49,8 +55,8 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov } @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); // The biometric template ids will be removed when we get confirmation from the HAL startHalOperation(); @@ -72,7 +78,14 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov } if (remaining == 0) { - mFinishCallback.onClientFinished(this); + if (mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty()) { + Slog.d(TAG, "Last biometric removed for user: " + getTargetUserId()); + // When the last biometric of a group is removed, update the authenticator id. + // Note that multiple ClientMonitors may be cause onRemoved (e.g. internal + // cleanup). + mAuthenticatorIds.put(getTargetUserId(), 0L); + } + mFinishCallback.onClientFinished(this, true /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java index e00396b054e6..b78ee49826f2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -23,10 +23,11 @@ import android.os.IBinder; public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> { - public RevokeChallengeClient(Context context, IBinder token, String owner, int sensorId) { - super(context, token, null /* listener */, 0 /* userId */, owner, 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, + @NonNull IBinder token, @NonNull String owner, int sensorId) { + super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner, + 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); } @Override @@ -35,15 +36,10 @@ public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> { } @Override - public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); startHalOperation(); - mFinishCallback.onClientFinished(this); - } - - @Override - protected void stopHalOperation() { - // Not supported for RevokeChallenge + mFinishCallback.onClientFinished(this, true /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java index a54357e922d2..118cadc9f9fa 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java @@ -62,13 +62,14 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { private int mLastAcquire; - FaceAuthenticationClient(@NonNull Context context, @NonNull IBinder token, + FaceAuthenticationClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsClient, @NonNull TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats) { - super(context, token, listener, targetUserId, operationId, restricted, + super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, isStrongBiometric, BiometricsProtoEnums.MODALITY_FACE, statsClient, taskStackListener, lockoutTracker); @@ -89,22 +90,22 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { @Override protected void startHalOperation() { try { - mDaemon.authenticate(mOperationId); + getFreshDaemon().authenticate(mOperationId); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting auth", e); onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } @Override protected void stopHalOperation() { try { - mDaemon.cancel(); + getFreshDaemon().cancel(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } @@ -132,7 +133,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { // 1) Authenticated == true // 2) Error occurred // 3) Authenticated == false - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, true /* success */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java index b63b39e3cf6c..4f9f46aff4c0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java @@ -51,13 +51,14 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { @NonNull private final int[] mEnrollIgnoreList; @NonNull private final int[] mEnrollIgnoreListVendor; - FaceEnrollClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int userId, + FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle, int sensorId) { - super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, - BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */); + super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, + timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, + false /* shouldVibrate */); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); mSurfaceHandle = surfaceHandle; mEnrollIgnoreList = getContext().getResources() @@ -89,36 +90,36 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { } android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 = - android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(mDaemon); + android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(getFreshDaemon()); try { final int status; if (daemon11 != null) { status = daemon11.enroll_1_1(token, mTimeoutSec, disabledFeatures, mSurfaceHandle); } else if (mSurfaceHandle == null) { - status = mDaemon.enroll(token, mTimeoutSec, disabledFeatures); + status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures); } else { Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL"); status = BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS; } if (status != Status.OK) { onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } @Override protected void stopHalOperation() { try { - mDaemon.cancel(); + getFreshDaemon().cancel(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java index 72188a516d65..67f2712d0b9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java @@ -36,15 +36,16 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet private static final String TAG = "FaceGenerateChallengeClient"; private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes - FaceGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token, + FaceGenerateChallengeClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) { - super(context, token, listener, owner, sensorId); + super(context, lazyDaemon, token, listener, owner, sensorId); } @Override protected void startHalOperation() { try { - mChallenge = mDaemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value; + mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value; } catch (RemoteException e) { Slog.e(TAG, "generateChallenge failed", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java index 227d817e31ff..ce57cb7686ed 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java @@ -40,10 +40,10 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { private final int mFeature; private final int mFaceId; - FaceGetFeatureClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, - int sensorId, int feature, int faceId) { - super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, + FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int sensorId, int feature, int faceId) { + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); mFeature = feature; @@ -61,24 +61,20 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); startHalOperation(); } @Override protected void startHalOperation() { try { - final OptionalBool result = mDaemon.getFeature(mFeature, mFaceId); + final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId); getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value); + mFinishCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to getFeature", e); + mFinishCallback.onClientFinished(this, false /* success */); } - mFinishCallback.onClientFinished(this); - } - - @Override - protected void stopHalOperation() { - // Not supported for GetFeature } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java index 388baa226dee..93f35f4c90cd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java @@ -18,9 +18,9 @@ package com.android.server.biometrics.sensors.face; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.hardware.face.Face; import android.os.IBinder; import com.android.server.biometrics.sensors.BiometricUtils; @@ -29,36 +29,40 @@ import com.android.server.biometrics.sensors.InternalEnumerateClient; import com.android.server.biometrics.sensors.RemovalClient; import java.util.List; +import java.util.Map; /** * Face-specific internal cleanup client supporting the * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1} * HIDL interfaces. */ -class FaceInternalCleanupClient extends InternalCleanupClient<IBiometricsFace> { +class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsFace> { - FaceInternalCleanupClient(@NonNull Context context, int userId, @NonNull String owner, - int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, - @NonNull BiometricUtils utils) { - super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, enrolledList, - utils); + FaceInternalCleanupClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner, + int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, + enrolledList, utils, authenticatorIds); } @Override protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context, - IBinder token, int userId, String owner, - List<? extends BiometricAuthenticator.Identifier> enrolledList, - BiometricUtils utils, int sensorId) { - return new FaceInternalEnumerateClient(context, token, userId, owner, enrolledList, utils, - sensorId); + LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner, + List<Face> enrolledList, BiometricUtils utils, int sensorId) { + return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner, + enrolledList, utils, sensorId); } @Override - protected RemovalClient<IBiometricsFace> getRemovalClient(Context context, IBinder token, - int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) { + protected RemovalClient<IBiometricsFace> getRemovalClient(Context context, + LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, + int biometricId, int userId, String owner, BiometricUtils utils, int sensorId, + Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. - return new FaceRemovalClient(context, token, null /* ClientMonitorCallbackConverter */, - biometricId, userId, owner, utils, sensorId); + return new FaceRemovalClient(context, lazyDaemon, token, + null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils, + sensorId, authenticatorIds); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java index c6749c5713d9..f25242ee9b85 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java @@ -18,9 +18,9 @@ package com.android.server.biometrics.sensors.face; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -38,31 +38,21 @@ import java.util.List; class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> { private static final String TAG = "FaceInternalEnumerateClient"; - FaceInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId, - @NonNull String owner, - @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, - @NonNull BiometricUtils utils, int sensorId) { - super(context, token, userId, owner, enrolledList, utils, sensorId, + FaceInternalEnumerateClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId, + @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils, + int sensorId) { + super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, BiometricsProtoEnums.MODALITY_FACE); } @Override protected void startHalOperation() { try { - mDaemon.enumerate(); + getFreshDaemon().enumerate(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enumerate", e); - mFinishCallback.onClientFinished(this); - } - } - - @Override - protected void stopHalOperation() { - try { - mDaemon.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when requesting cancel", e); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java index b0ee9810a49b..00d5f500b241 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java @@ -28,6 +28,8 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.RemovalClient; +import java.util.Map; + /** * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0} * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces. @@ -35,30 +37,21 @@ import com.android.server.biometrics.sensors.RemovalClient; class FaceRemovalClient extends RemovalClient<IBiometricsFace> { private static final String TAG = "FaceRemovalClient"; - FaceRemovalClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId, - @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) { - super(context, token, listener, biometricId, userId, owner, utils, sensorId, - BiometricsProtoEnums.MODALITY_FACE); + FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, + int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils, + int sensorId, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId, + authenticatorIds, BiometricsProtoEnums.MODALITY_FACE); } @Override protected void startHalOperation() { try { - mDaemon.remove(mBiometricId); + getFreshDaemon().remove(mBiometricId); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting remove", e); - mFinishCallback.onClientFinished(this); - } - } - - @Override - protected void stopHalOperation() { - try { - mDaemon.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when requesting cancel", e); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java index 441cb14f6600..69070da0491e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java @@ -37,10 +37,11 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> { private final ArrayList<Byte> mHardwareAuthToken; - FaceResetLockoutClient(@NonNull Context context, int userId, String owner, int sensorId, - byte[] hardwareAuthToken) { - super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, - sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, + FaceResetLockoutClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId, + @NonNull byte[] hardwareAuthToken) { + super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, + 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); mHardwareAuthToken = new ArrayList<>(); @@ -55,24 +56,19 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); - + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); startHalOperation(); } @Override protected void startHalOperation() { try { - mDaemon.resetLockout(mHardwareAuthToken); + getFreshDaemon().resetLockout(mHardwareAuthToken); + mFinishCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to reset lockout", e); + mFinishCallback.onClientFinished(this, false /* success */); } - mFinishCallback.onClientFinished(this); - } - - @Override - protected void stopHalOperation() { - // Not supported for resetLockout } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java index 992a678d8205..a10c573f34fc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java @@ -33,15 +33,16 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometrics private static final String TAG = "FaceRevokeChallengeClient"; - FaceRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token, + FaceRevokeChallengeClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull String owner, int sensorId) { - super(context, token, owner, sensorId); + super(context, lazyDaemon, token, owner, sensorId); } @Override protected void startHalOperation() { try { - mDaemon.revokeChallenge(); + getFreshDaemon().revokeChallenge(); } catch (RemoteException e) { Slog.e(TAG, "revokeChallenge failed", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 050fba28043e..a7f82202ca3e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -25,7 +25,6 @@ import android.app.AppOpsManager; import android.app.NotificationManager; import android.content.Context; import android.content.pm.UserInfo; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -60,6 +59,7 @@ import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.GenerateChallengeClient; +import com.android.server.biometrics.sensors.LockoutResetTracker; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalClient; @@ -97,6 +97,9 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { static final String NOTIFICATION_TAG = "FaceService"; static final int NOTIFICATION_ID = 1; + private final LockoutResetTracker mLockoutResetTracker; + private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon; + /** * Receives the incoming binder calls from FaceManager. */ @@ -113,7 +116,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { checkPermission(MANAGE_BIOMETRIC); final GenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(), - token, new ClientMonitorCallbackConverter(receiver), opPackageName, + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId()); generateChallengeInternal(client); } @@ -122,8 +125,8 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { public void revokeChallenge(IBinder token, String owner) { checkPermission(MANAGE_BIOMETRIC); - final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), token, - owner, getSensorId()); + final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), + mLazyDaemon, token, owner, getSensorId()); // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks. if (getCurrentClient() == null) { @@ -149,7 +152,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { UserHandle.CURRENT); }); - final EnrollClient client = new FaceEnrollClient(getContext(), token, + final EnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC, convertSurfaceToNativeHandle(surface), getSensorId()); @@ -180,11 +183,11 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { final boolean restricted = isRestricted(); final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD : BiometricsProtoEnums.CLIENT_UNKNOWN; - final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token, - new ClientMonitorCallbackConverter(receiver), userId, opId, restricted, - opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(), - isStrongBiometric(), statsClient, mTaskStackListener, mLockoutTracker, - mUsageStats); + final AuthenticationClient client = new FaceAuthenticationClient(getContext(), + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId, + restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */, + getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener, + mLockoutTracker, mUsageStats); authenticateInternal(client, opPackageName); } @@ -197,11 +200,11 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { updateActiveGroup(userId); final boolean restricted = true; // BiometricPrompt is always restricted - final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token, - new ClientMonitorCallbackConverter(sensorReceiver), userId, opId, restricted, - opPackageName, cookie, requireConfirmation, getSensorId(), isStrongBiometric(), - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener, - mLockoutTracker, mUsageStats); + final AuthenticationClient client = new FaceAuthenticationClient(getContext(), + mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId, + opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(), + isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + mTaskStackListener, mLockoutTracker, mUsageStats); authenticateInternal(client, opPackageName, callingUid, callingPid, callingUserId); } @@ -238,16 +241,19 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { return; } - final RemovalClient client = new FaceRemovalClient(getContext(), token, + final RemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, - getBiometricUtils(), getSensorId()); + getBiometricUtils(), getSensorId(), mAuthenticatorIds); removeInternal(client); } @Override - public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) { + public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback, + final String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); - FaceService.super.addLockoutResetCallback(callback); + mHandler.post(() -> { + mLockoutResetTracker.addCallback(callback, opPackageName); + }); } @Override // Binder call @@ -340,7 +346,8 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { updateActiveGroup(userId); final FaceResetLockoutClient client = new FaceResetLockoutClient(getContext(), - userId, getContext().getOpPackageName(), getSensorId(), hardwareAuthToken); + mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), + hardwareAuthToken); startClient(client, true /* initiatedByClient */); }); } @@ -364,8 +371,8 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { final int faceId = getFirstTemplateForUser(mCurrentUserId); final FaceSetFeatureClient client = new FaceSetFeatureClient(getContext(), - token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, - getSensorId(), feature, enabled, hardwareAuthToken, faceId); + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, + opPackageName, getSensorId(), feature, enabled, hardwareAuthToken, faceId); startClient(client, true /* initiatedByClient */); }); @@ -391,9 +398,9 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { // TODO: Support multiple faces final int faceId = getFirstTemplateForUser(mCurrentUserId); - final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(), token, - new ClientMonitorCallbackConverter(receiver), userId, opPackageName, - getSensorId(), feature, faceId); + final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(), + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, + opPackageName, getSensorId(), feature, faceId); startClient(client, true /* initiatedByClient */); }); @@ -539,7 +546,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { mHandler.post(() -> { if (duration == 0) { - notifyLockoutResetMonitors(); + mLockoutResetTracker.notifyLockoutResetCallbacks(); } }); } @@ -547,6 +554,8 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { public FaceService(Context context) { super(context); + mLazyDaemon = FaceService.this::getFaceDaemon; + mLockoutResetTracker = new LockoutResetTracker(context); mLockoutTracker = new LockoutHalImpl(); mUsageStats = new UsageStats(context); mNotificationManager = getContext().getSystemService(NotificationManager.class); @@ -673,7 +682,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @Override protected List<Face> getEnrolledTemplates(int userId) { - return getBiometricUtils().getBiometricsForUser(getContext(), userId); + return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId); } @Override @@ -693,10 +702,10 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @Override protected void doTemplateCleanupForUser(int userId) { - final List<? extends BiometricAuthenticator.Identifier> enrolledList = - getEnrolledTemplates(userId); - final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(), userId, - getContext().getOpPackageName(), getSensorId(), enrolledList, getBiometricUtils()); + final List<Face> enrolledList = getEnrolledTemplates(userId); + final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(), + mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), enrolledList, + getBiometricUtils(), mAuthenticatorIds); cleanupInternal(client); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java index 91f63e185ff8..e7d041a11ccb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java @@ -43,11 +43,11 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> { private final ArrayList<Byte> mHardwareAuthToken; private final int mFaceId; - FaceSetFeatureClient(@NonNull Context context, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, int userId, + FaceSetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, int feature, boolean enabled, byte[] hardwareAuthToken, int faceId) { - super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); mFeature = feature; @@ -70,25 +70,22 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) { - super.start(daemon, finishCallback); + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); startHalOperation(); - mFinishCallback.onClientFinished(this); } @Override protected void startHalOperation() { try { - final int result = mDaemon.setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId); + final int result = getFreshDaemon() + .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId); getListener().onFeatureSet(result == Status.OK, mFeature); + mFinishCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e); + mFinishCallback.onClientFinished(this, false /* success */); } } - - @Override - protected void stopHalOperation() { - // Not supported for SetFeature - } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java index ba89d51b1f61..b01b8564466b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java @@ -47,13 +47,14 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi private final LockoutFrameworkImpl mLockoutFrameworkImpl; - FingerprintAuthenticationClient(@NonNull Context context, @NonNull IBinder token, + FingerprintAuthenticationClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient, @NonNull TaskStackListener taskStackListener, @NonNull LockoutFrameworkImpl lockoutTracker) { - super(context, token, listener, targetUserId, operationId, restricted, + super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, isStrongBiometric, BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, lockoutTracker); @@ -72,7 +73,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi if (authenticated) { resetFailedAttempts(getTargetUserId()); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, true /* success */); } else { final @LockoutTracker.LockoutMode int lockoutMode = mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId()); @@ -83,7 +84,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; onError(errorCode, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, true /* success */); } } } @@ -102,24 +103,24 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi protected void startHalOperation() { try { // GroupId was never used. In fact, groupId is always the same as userId. - mDaemon.authenticate(mOperationId, getTargetUserId()); + getFreshDaemon().authenticate(mOperationId, getTargetUserId()); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting auth", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } @Override protected void stopHalOperation() { try { - mDaemon.cancel(); + getFreshDaemon().cancel(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java index 34681c38808b..1d56882c0fd2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java @@ -38,36 +38,38 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint private static final String TAG = "FingerprintEnrollClient"; - FingerprintEnrollClient(@NonNull Context context, @NonNull IBinder token, + FingerprintEnrollClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, int timeoutSec, int sensorId) { - super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, - BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */); + super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, + timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, + true /* shouldVibrate */); } @Override protected void startHalOperation() { try { // GroupId was never used. In fact, groupId is always the same as userId. - mDaemon.enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec); + getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } @Override protected void stopHalOperation() { try { - mDaemon.cancel(); + getFreshDaemon().cancel(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java index 2fa433305070..8fb8c992268d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java @@ -36,15 +36,16 @@ public class FingerprintGenerateChallengeClient private static final String TAG = "FingerprintGenerateChallengeClient"; - FingerprintGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token, + FingerprintGenerateChallengeClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) { - super(context, token, listener, owner, sensorId); + super(context, lazyDaemon, token, listener, owner, sensorId); } @Override protected void startHalOperation() { try { - mChallenge = mDaemon.preEnroll(); + mChallenge = getFreshDaemon().preEnroll(); } catch (RemoteException e) { Slog.e(TAG, "preEnroll failed", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java index 71e76703b9d5..1d72c2e0a2ca 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java @@ -21,6 +21,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import com.android.server.biometrics.sensors.BiometricUtils; @@ -29,37 +30,43 @@ import com.android.server.biometrics.sensors.InternalEnumerateClient; import com.android.server.biometrics.sensors.RemovalClient; import java.util.List; +import java.util.Map; /** * Fingerprint-specific internal cleanup client supporting the * {@link android.hardware.biometrics.fingerprint.V2_1} and * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces. */ -class FingerprintInternalCleanupClient extends InternalCleanupClient<IBiometricsFingerprint> { +class FingerprintInternalCleanupClient + extends InternalCleanupClient<Fingerprint, IBiometricsFingerprint> { - FingerprintInternalCleanupClient(@NonNull Context context,int userId, @NonNull String owner, - int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, - @NonNull BiometricUtils utils) { - super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, - enrolledList, utils); + FingerprintInternalCleanupClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId, + @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList, + @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, + BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds); } @Override protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient( - Context context, IBinder token, int userId, String owner, - List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils, + Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token, + int userId, String owner, + List<Fingerprint> enrolledList, BiometricUtils utils, int sensorId) { - return new FingerprintInternalEnumerateClient(context, token, userId, owner, enrolledList, - utils, sensorId); + return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner, + enrolledList, utils, sensorId); } @Override - protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context, IBinder token, - int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) { + protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context, + LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token, + int biometricId, int userId, String owner, BiometricUtils utils, int sensorId, + Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. - return new FingerprintRemovalClient(context, token, + return new FingerprintRemovalClient(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils, - sensorId); + sensorId, authenticatorIds); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java index ba412e3956ce..240c3c56f75e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java @@ -18,9 +18,9 @@ package com.android.server.biometrics.sensors.fingerprint; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -38,31 +38,21 @@ import java.util.List; class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFingerprint> { private static final String TAG = "FingerprintInternalEnumerateClient"; - FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId, - @NonNull String owner, - @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, + FingerprintInternalEnumerateClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, + int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList, @NonNull BiometricUtils utils, int sensorId) { - super(context, token, userId, owner, enrolledList, utils, sensorId, + super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT); } @Override protected void startHalOperation() { try { - mDaemon.enumerate(); + getFreshDaemon().enumerate(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enumerate", e); - mFinishCallback.onClientFinished(this); - } - } - - @Override - protected void stopHalOperation() { - try { - mDaemon.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when requesting cancel", e); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java index 6d7e76162029..a9336ef6a6c2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java @@ -28,6 +28,8 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.RemovalClient; +import java.util.Map; + /** * Fingerprint-specific removal client supporting the * {@link android.hardware.biometrics.fingerprint.V2_1} and @@ -36,31 +38,23 @@ import com.android.server.biometrics.sensors.RemovalClient; class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> { private static final String TAG = "FingerprintRemovalClient"; - FingerprintRemovalClient(@NonNull Context context, @NonNull IBinder token, + FingerprintRemovalClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId, - @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) { - super(context, token, listener, biometricId, userId, owner, utils, sensorId, - BiometricsProtoEnums.MODALITY_FINGERPRINT); + @NonNull String owner, @NonNull BiometricUtils utils, int sensorId, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId, + authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT); } @Override protected void startHalOperation() { try { // GroupId was never used. In fact, groupId is always the same as userId. - mDaemon.remove(getTargetUserId(), mBiometricId); + getFreshDaemon().remove(getTargetUserId(), mBiometricId); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting remove", e); - mFinishCallback.onClientFinished(this); - } - } - - @Override - protected void stopHalOperation() { - try { - mDaemon.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when requesting cancel", e); - mFinishCallback.onClientFinished(this); + mFinishCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java index ccbea31d7c48..882660e7a618 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java @@ -35,15 +35,16 @@ public class FingerprintRevokeChallengeClient private static final String TAG = "FingerprintRevokeChallengeClient"; - FingerprintRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token, + FingerprintRevokeChallengeClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull String owner, int sensorId) { - super(context, token, owner, sensorId); + super(context, lazyDaemon, token, owner, sensorId); } @Override protected void startHalOperation() { try { - mDaemon.postEnroll(); + getFreshDaemon().postEnroll(); } catch (RemoteException e) { Slog.e(TAG, "revokeChallenge/postEnroll failed", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index d2a25dba7936..b05400a0e426 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -29,7 +29,6 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -64,9 +63,11 @@ import com.android.server.biometrics.fingerprint.PerformanceStatsProto; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BiometricServiceBase; import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.GenerateChallengeClient; +import com.android.server.biometrics.sensors.LockoutResetTracker; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalClient; @@ -82,7 +83,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; /** * A service to manage multiple clients that want to access the fingerprint HAL API. @@ -97,6 +97,10 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr private static final boolean DEBUG = true; private static final String FP_DATA_DIR = "fpdata"; + private final LockoutResetTracker mLockoutResetTracker; + private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon; + private final GestureAvailabilityTracker mGestureAvailabilityTracker; + /** * Receives the incoming binder calls from FingerprintManager. */ @@ -113,7 +117,7 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr checkPermission(MANAGE_FINGERPRINT); final GenerateChallengeClient client = new FingerprintGenerateChallengeClient( - getContext(), token, new ClientMonitorCallbackConverter(receiver), + getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId()); generateChallengeInternal(client); } @@ -123,7 +127,7 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr checkPermission(MANAGE_FINGERPRINT); final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(getContext(), - token, owner, getSensorId()); + mLazyDaemon, token, owner, getSensorId()); revokeChallengeInternal(client); } @@ -134,8 +138,8 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr checkPermission(MANAGE_FINGERPRINT); updateActiveGroup(userId); - final EnrollClient client = new FingerprintEnrollClient(getContext(), token, - new ClientMonitorCallbackConverter(receiver), userId, cryptoToken, + final EnrollClient client = new FingerprintEnrollClient(getContext(), mLazyDaemon, + token, new ClientMonitorCallbackConverter(receiver), userId, cryptoToken, opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId()); enrollInternal(client, userId); @@ -165,9 +169,10 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD : BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER; final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(), - token, new ClientMonitorCallbackConverter(receiver), userId, opId, restricted, - opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(), - isStrongBiometric, surface, statsClient, mTaskStackListener, mLockoutTracker); + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId, + restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */, + getSensorId(), isStrongBiometric, surface, statsClient, mTaskStackListener, + mLockoutTracker); authenticateInternal(client, opPackageName); } @@ -175,14 +180,14 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr public void prepareForAuthentication(IBinder token, long opId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId, - Surface surface) throws RemoteException { + Surface surface) { checkPermission(MANAGE_BIOMETRIC); updateActiveGroup(userId); final boolean restricted = true; // BiometricPrompt is always restricted final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(), - token, new ClientMonitorCallbackConverter(sensorReceiver), userId, opId, - restricted, opPackageName, cookie, false /* requireConfirmation */, + mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId, + opId, restricted, opPackageName, cookie, false /* requireConfirmation */, getSensorId(), isStrongBiometric(), surface, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener, mLockoutTracker); @@ -223,17 +228,19 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr return; } - final RemovalClient client = new FingerprintRemovalClient(getContext(), token, - new ClientMonitorCallbackConverter(receiver), fingerId, userId, opPackageName, - getBiometricUtils(), getSensorId()); + final RemovalClient client = new FingerprintRemovalClient(getContext(), mLazyDaemon, + token, new ClientMonitorCallbackConverter(receiver), fingerId, userId, + opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds); removeInternal(client); } @Override - public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) - throws RemoteException { + public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback, + final String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); - FingerprintService.super.addLockoutResetCallback(callback); + mHandler.post(() -> { + mLockoutResetTracker.addCallback(callback, opPackageName); + }); } @Override // Binder call @@ -350,13 +357,13 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr @Override public void addClientActiveCallback(IFingerprintClientActiveCallback callback) { checkPermission(MANAGE_FINGERPRINT); - mClientActiveCallbacks.add(callback); + mGestureAvailabilityTracker.registerCallback(callback); } @Override public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) { checkPermission(MANAGE_FINGERPRINT); - mClientActiveCallbacks.remove(callback); + mGestureAvailabilityTracker.removeCallback(callback); } @Override // Binder call @@ -464,17 +471,11 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr } private final LockoutFrameworkImpl mLockoutTracker; - private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = - new CopyOnWriteArrayList<>(); private IUdfpsOverlayController mUdfpsOverlayController; @GuardedBy("this") private IBiometricsFingerprint mDaemon; - private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback = userId -> { - notifyLockoutResetMonitors(); - }; - /** * Receives callbacks from the HAL. */ @@ -550,7 +551,13 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr public FingerprintService(Context context) { super(context); - mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback); + mLazyDaemon = FingerprintService.this::getFingerprintDaemon; + mGestureAvailabilityTracker = new GestureAvailabilityTracker(); + mLockoutResetTracker = new LockoutResetTracker(context); + final LockoutFrameworkImpl.LockoutResetCallback lockoutResetCallback = userId -> { + mLockoutResetTracker.notifyLockoutResetCallbacks(); + }; + mLockoutTracker = new LockoutFrameworkImpl(context, lockoutResetCallback); } @Override @@ -677,20 +684,12 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr if (userId != UserHandle.getCallingUserId()) { checkPermission(INTERACT_ACROSS_USERS); } - return getBiometricUtils().getBiometricsForUser(getContext(), userId); + return FingerprintUtils.getInstance().getBiometricsForUser(getContext(), userId); } @Override protected void notifyClientActiveCallbacks(boolean isActive) { - List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks; - for (int i = 0; i < callbacks.size(); i++) { - try { - callbacks.get(i).onClientActiveChanged(isActive); - } catch (RemoteException re) { - // If the remote is dead, stop notifying it - mClientActiveCallbacks.remove(callbacks.get(i)); - } - } + mGestureAvailabilityTracker.notifyClientActiveCallbacks(isActive); } @Override @@ -705,11 +704,10 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr @Override protected void doTemplateCleanupForUser(int userId) { - final List<? extends BiometricAuthenticator.Identifier> enrolledList = - getEnrolledTemplates(userId); + final List<Fingerprint> enrolledList = getEnrolledTemplates(userId); final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( - getContext(), userId, getContext().getOpPackageName(), getSensorId(), enrolledList, - getBiometricUtils()); + getContext(), mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), + enrolledList, getBiometricUtils(), mAuthenticatorIds); cleanupInternal(client); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java new file mode 100644 index 000000000000..6292ecf0e207 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java @@ -0,0 +1,50 @@ +/* + * 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.biometrics.sensors.fingerprint; + +import android.hardware.fingerprint.IFingerprintClientActiveCallback; +import android.os.RemoteException; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its + * availability changes + */ +class GestureAvailabilityTracker { + private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = + new CopyOnWriteArrayList<>(); + + void registerCallback(IFingerprintClientActiveCallback callback) { + mClientActiveCallbacks.add(callback); + } + + void removeCallback(IFingerprintClientActiveCallback callback) { + mClientActiveCallbacks.remove(callback); + } + + void notifyClientActiveCallbacks(boolean isActive) { + for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) { + try { + callback.onClientActiveChanged(isActive); + } catch (RemoteException re) { + // If the remote is dead, stop notifying it + mClientActiveCallbacks.remove(callback); + } + } + } +} diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index c5aa8d5361ec..a75a80a606eb 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -82,6 +82,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse private final PackageManager mPackageManager; private final UserManager mUserManager; private final INetd mNetd; + private final Dependencies mDeps; // Values are User IDs. @GuardedBy("this") @@ -102,10 +103,30 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse @GuardedBy("this") private final Set<Integer> mAllApps = new HashSet<>(); - public PermissionMonitor(Context context, INetd netd) { + /** + * Dependencies of PermissionMonitor, for injection in tests. + */ + @VisibleForTesting + public static class Dependencies { + /** + * Get device first sdk version. + */ + public int getDeviceFirstSdkInt() { + return Build.VERSION.FIRST_SDK_INT; + } + } + + public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) { + this(context, netd, new Dependencies()); + } + + @VisibleForTesting + PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd, + @NonNull final Dependencies deps) { mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mNetd = netd; + mDeps = deps; } // Intended to be called only once at startup, after the system is ready. Installs a broadcast @@ -186,11 +207,6 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse } @VisibleForTesting - protected int getDeviceFirstSdkInt() { - return Build.VERSION.FIRST_SDK_INT; - } - - @VisibleForTesting boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) { if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) { return false; @@ -212,7 +228,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse if (app.applicationInfo != null) { // Backward compatibility for b/114245686, on devices that launched before Q daemons // and apps running as the system UID are exempted from this check. - if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) { + if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) { return true; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 70f0399d1070..05cf40a091b6 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -120,6 +120,11 @@ public abstract class InputMethodManagerInternal { public abstract void reportImeControl(@Nullable IBinder windowToken); /** + * Destroys the IME surface. + */ + public abstract void removeImeSurface(); + + /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = @@ -166,6 +171,10 @@ public abstract class InputMethodManagerInternal { @Override public void reportImeControl(@Nullable IBinder windowToken) { } + + @Override + public void removeImeSurface() { + } }; /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 15e8a92ba0e4..d8ee32e7bd74 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -211,6 +211,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_INITIALIZE_IME = 1040; static final int MSG_CREATE_SESSION = 1050; static final int MSG_REMOVE_IME_SURFACE = 1060; + static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061; static final int MSG_START_INPUT = 2000; @@ -2128,6 +2129,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void onInputMethodFinishInput() throws RemoteException { mCallback.onInputMethodFinishInput(); } + + @Override + public void onInlineSuggestionsSessionInvalidated() throws RemoteException { + mCallback.onInlineSuggestionsSessionInvalidated(); + } } /** @@ -4000,6 +4006,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); } + @Override + public void removeImeSurfaceFromWindow(IBinder windowToken) { + // No permission check, because we'll only execute the request if the calling window is + // also the current IME client. + mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget(); + } + @BinderThread private void notifyUserAction(@NonNull IBinder token) { if (DEBUG) { @@ -4273,11 +4286,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; } case MSG_REMOVE_IME_SURFACE: { - try { - if (mEnabledSession != null && mEnabledSession.session != null) { - mEnabledSession.session.removeImeSurface(); + synchronized (mMethodMap) { + try { + if (mEnabledSession != null && mEnabledSession.session != null + && !mShowRequested) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { + } + } + return true; + } + case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: { + IBinder windowToken = (IBinder) msg.obj; + synchronized (mMethodMap) { + try { + if (windowToken == mCurFocusedWindow + && mEnabledSession != null && mEnabledSession.session != null) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } return true; } @@ -5111,6 +5140,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void reportImeControl(@Nullable IBinder windowToken) { mService.reportImeControl(windowToken); } + + @Override + public void removeImeSurface() { + mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); + } } @BinderThread diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 23392db0690d..2516e289f099 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -226,6 +226,11 @@ public final class MultiClientInputMethodManagerService { @Override public void reportImeControl(@Nullable IBinder windowToken) { } + + @Override + public void removeImeSurface() { + reportNotSupported(); + } }); } @@ -1482,6 +1487,12 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override + public void removeImeSurfaceFromWindow(IBinder windowToken) { + reportNotSupported(); + } + + @BinderThread + @Override public boolean showSoftInput( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 5c1d1826b88a..f69c8239762d 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -25,7 +25,6 @@ import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED; import static android.location.LocationManager.EXTRA_PROVIDER_NAME; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; -import static android.location.LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION; import static android.location.LocationManager.KEY_LOCATION_CHANGED; import static android.location.LocationManager.KEY_PROVIDER_ENABLED; import static android.location.LocationManager.MODE_CHANGED_ACTION; @@ -1222,21 +1221,10 @@ public class LocationManagerService extends ILocationManager.Stub { false); // Now update monitoring of high power requests only. - boolean wasHighPowerMonitoring = mOpHighPowerMonitoring; mOpHighPowerMonitoring = updateMonitoring( requestingHighPowerLocation, mOpHighPowerMonitoring, true); - if (mOpHighPowerMonitoring != wasHighPowerMonitoring) { - long identity = Binder.clearCallingIdentity(); - try { - // Send an intent to notify that a high power request has been added/removed. - Intent intent = new Intent(HIGH_POWER_REQUEST_CHANGE_ACTION); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } finally { - Binder.restoreCallingIdentity(identity); - } - } } private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring, diff --git a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java index 70d83a9dd7dd..631dbbf0f1fd 100644 --- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java @@ -27,7 +27,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.location.LocationManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; @@ -582,17 +581,9 @@ class GnssVisibilityControl { mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, uid, proxyAppPkgName); mAppOps.finishOp(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, uid, proxyAppPkgName); } - sendHighPowerMonitoringBroadcast(); return true; } - private void sendHighPowerMonitoringBroadcast() { - // Send an intent to notify that a high power request has been added/removed so that - // the SystemUi checks the state of AppOps and updates the location icon accordingly. - Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } - private void handleEmergencyNfwNotification(NfwNotification nfwNotification) { boolean isPermissionMismatched = false; if (!nfwNotification.isRequestAccepted()) { diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java index 78af79b533d8..a95383695ae8 100644 --- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java +++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java @@ -16,12 +16,8 @@ package com.android.server.location.util; -import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; - import android.app.AppOpsManager; import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; import android.location.util.identity.CallerIdentity; import android.os.Binder; @@ -66,22 +62,13 @@ public class SystemAppOpsHelper extends AppOpsHelper { long identity = Binder.clearCallingIdentity(); try { - boolean allowed = mAppOps.startOpNoThrow( + return mAppOps.startOpNoThrow( appOp, callerIdentity.getUid(), callerIdentity.getPackageName(), false, callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; - - if (allowed && appOp == OP_MONITOR_HIGH_POWER_LOCATION) { - // notify of possible location icon change - mContext.sendBroadcast( - new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags( - Intent.FLAG_RECEIVER_FOREGROUND)); - } - - return allowed; } finally { Binder.restoreCallingIdentity(identity); } @@ -98,13 +85,6 @@ public class SystemAppOpsHelper extends AppOpsHelper { callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag()); - - if (appOp == OP_MONITOR_HIGH_POWER_LOCATION) { - // notify of possible location icon change - mContext.sendBroadcast( - new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags( - Intent.FLAG_RECEIVER_FOREGROUND)); - } } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 7b767b86f0d4..c4581c88f49e 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -155,7 +155,7 @@ class LockSettingsShellCommand extends ShellCommand { pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>"); pw.println(" Sets the lock screen as PIN, using the given PIN to unlock."); pw.println(""); - pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>"); + pw.println(" set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>"); pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock."); pw.println(""); pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]"); diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java index 55e0795f8b50..b9822fcb096a 100644 --- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java +++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java @@ -26,12 +26,12 @@ import android.os.Message; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.IntArray; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -104,7 +104,7 @@ class AudioPlayerStateMonitor { // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID. @GuardedBy("mLock") @SuppressWarnings("WeakerAccess") /* synthetic access */ - final IntArray mSortedAudioPlaybackClientUids = new IntArray(); + final List<Integer> mSortedAudioPlaybackClientUids = new ArrayList<>(); static AudioPlayerStateMonitor getInstance(Context context) { synchronized (AudioPlayerStateMonitor.class) { @@ -145,8 +145,8 @@ class AudioPlayerStateMonitor { * audio/video) The UID whose audio is currently playing comes first, then the UID whose audio * playback becomes active at the last comes next. */ - public IntArray getSortedAudioPlaybackClientUids() { - IntArray sortedAudioPlaybackClientUids = new IntArray(); + public List<Integer> getSortedAudioPlaybackClientUids() { + List<Integer> sortedAudioPlaybackClientUids = new ArrayList(); synchronized (mLock) { sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index dbf1abc0ca70..0f6748366e16 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -50,7 +50,6 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; import android.util.Log; -import android.util.Slog; import android.view.KeyEvent; import com.android.server.LocalServices; @@ -996,7 +995,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } return true; } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in sendMediaRequest.", e); + Log.e(TAG, "Remote failure in sendMediaRequest.", e); } return false; } @@ -1013,7 +1012,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } return true; } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in sendMediaRequest.", e); + Log.e(TAG, "Remote failure in sendMediaRequest.", e); } return false; } @@ -1023,7 +1022,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onCommand(packageName, pid, uid, command, args, cb); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in sendCommand.", e); + Log.e(TAG, "Remote failure in sendCommand.", e); } } @@ -1032,7 +1031,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onCustomAction(packageName, pid, uid, action, args); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in sendCustomAction.", e); + Log.e(TAG, "Remote failure in sendCustomAction.", e); } } @@ -1040,7 +1039,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPrepare(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in prepare.", e); + Log.e(TAG, "Remote failure in prepare.", e); } } @@ -1049,7 +1048,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in prepareFromMediaId.", e); + Log.e(TAG, "Remote failure in prepareFromMediaId.", e); } } @@ -1058,7 +1057,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPrepareFromSearch(packageName, pid, uid, query, extras); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in prepareFromSearch.", e); + Log.e(TAG, "Remote failure in prepareFromSearch.", e); } } @@ -1066,7 +1065,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPrepareFromUri(packageName, pid, uid, uri, extras); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in prepareFromUri.", e); + Log.e(TAG, "Remote failure in prepareFromUri.", e); } } @@ -1074,7 +1073,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPlay(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in play.", e); + Log.e(TAG, "Remote failure in play.", e); } } @@ -1083,7 +1082,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in playFromMediaId.", e); + Log.e(TAG, "Remote failure in playFromMediaId.", e); } } @@ -1092,7 +1091,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPlayFromSearch(packageName, pid, uid, query, extras); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in playFromSearch.", e); + Log.e(TAG, "Remote failure in playFromSearch.", e); } } @@ -1100,7 +1099,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPlayFromUri(packageName, pid, uid, uri, extras); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in playFromUri.", e); + Log.e(TAG, "Remote failure in playFromUri.", e); } } @@ -1108,7 +1107,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onSkipToTrack(packageName, pid, uid, id); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in skipToTrack", e); + Log.e(TAG, "Remote failure in skipToTrack", e); } } @@ -1116,7 +1115,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPause(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in pause.", e); + Log.e(TAG, "Remote failure in pause.", e); } } @@ -1124,7 +1123,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onStop(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in stop.", e); + Log.e(TAG, "Remote failure in stop.", e); } } @@ -1132,7 +1131,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onNext(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in next.", e); + Log.e(TAG, "Remote failure in next.", e); } } @@ -1140,7 +1139,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onPrevious(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in previous.", e); + Log.e(TAG, "Remote failure in previous.", e); } } @@ -1148,7 +1147,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onFastForward(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in fastForward.", e); + Log.e(TAG, "Remote failure in fastForward.", e); } } @@ -1156,7 +1155,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onRewind(packageName, pid, uid); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in rewind.", e); + Log.e(TAG, "Remote failure in rewind.", e); } } @@ -1164,7 +1163,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onSeekTo(packageName, pid, uid, pos); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in seekTo.", e); + Log.e(TAG, "Remote failure in seekTo.", e); } } @@ -1172,7 +1171,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onRate(packageName, pid, uid, rating); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in rate.", e); + Log.e(TAG, "Remote failure in rate.", e); } } @@ -1180,7 +1179,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onSetPlaybackSpeed(packageName, pid, uid, speed); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e); + Log.e(TAG, "Remote failure in setPlaybackSpeed.", e); } } @@ -1194,7 +1193,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR mCb.onAdjustVolume(packageName, pid, uid, direction); } } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in adjustVolume.", e); + Log.e(TAG, "Remote failure in adjustVolume.", e); } } @@ -1202,7 +1201,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR try { mCb.onSetVolumeTo(packageName, pid, uid, value); } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in setVolumeTo.", e); + Log.e(TAG, "Remote failure in setVolumeTo.", e); } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 375804a395ef..9a83242a5955 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -75,7 +75,6 @@ import android.provider.Settings; import android.speech.RecognizerIntent; import android.text.TextUtils; import android.util.Log; -import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.KeyEvent; @@ -1140,7 +1139,7 @@ public class MediaSessionService extends SystemService implements Monitor { } return sessionBinder; } catch (Exception e) { - Slog.w(TAG, "Exception in creating a new session", e); + Log.w(TAG, "Exception in creating a new session", e); throw e; } finally { Binder.restoreCallingIdentity(token); @@ -1351,7 +1350,7 @@ public class MediaSessionService extends SystemService implements Monitor { if (!isUserSetupComplete()) { // Global media key handling can have the side-effect of starting new // activities which is undesirable while setup is in progress. - Slog.i(TAG, "Not dispatching media key event because user " + Log.i(TAG, "Not dispatching media key event because user " + "setup is in progress."); return; } @@ -1361,7 +1360,7 @@ public class MediaSessionService extends SystemService implements Monitor { if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { // Prevent dispatching key event through reflection while the global // priority session is active. - Slog.i(TAG, "Only the system can dispatch media key event " + Log.i(TAG, "Only the system can dispatch media key event " + "to the global priority session."); return; } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index b678c8962a21..953aae44d6a7 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -22,7 +22,6 @@ import android.media.Session2Token; import android.media.session.MediaSession; import android.os.Debug; import android.os.UserHandle; -import android.util.IntArray; import android.util.Log; import android.util.SparseArray; @@ -190,7 +189,8 @@ class MediaSessionStack { if (DEBUG) { Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); } - IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); + List<Integer> audioPlaybackUids = + mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); for (int i = 0; i < audioPlaybackUids.size(); i++) { int audioPlaybackUid = audioPlaybackUids.get(i); MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUid); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 951f462ed201..592db83b8721 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -77,6 +77,8 @@ public abstract class ApexManager { public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; static final int MATCH_FACTORY_PACKAGE = 1 << 1; + private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk."; + private static final Singleton<ApexManager> sApexManagerSingleton = new Singleton<ApexManager>() { @Override @@ -342,8 +344,17 @@ public abstract class ApexManager { public abstract boolean destroyDeSnapshots(int rollbackId); /** + * Deletes snapshots of the credential encrypted apex data directories for the specified user, + * for the given rollback id as long as the user is credential unlocked. + * + * @return boolean true if the delete was successful + */ + public abstract boolean destroyCeSnapshots(int userId, int rollbackId); + + /** * Deletes snapshots of the credential encrypted apex data directories for the specified user, - * where the rollback id is not included in {@code retainRollbackIds}. + * where the rollback id is not included in {@code retainRollbackIds} as long as the user is + * credential unlocked. * * @return boolean true if the delete was successful */ @@ -526,7 +537,9 @@ public abstract class ApexManager { activePackagesSet.add(packageInfo.packageName); } if (ai.isFactory) { - if (factoryPackagesSet.contains(packageInfo.packageName)) { + // Don't throw when the duplicating APEX is VNDK APEX + if (factoryPackagesSet.contains(packageInfo.packageName) + && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) { throw new IllegalStateException( "Two factory packages have the same name: " + packageInfo.packageName); @@ -875,6 +888,17 @@ public abstract class ApexManager { } @Override + public boolean destroyCeSnapshots(int userId, int rollbackId) { + try { + waitForApexService().destroyCeSnapshots(userId, rollbackId); + return true; + } catch (Exception e) { + Slog.e(TAG, e.getMessage(), e); + return false; + } + } + + @Override public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { try { waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds); @@ -1136,6 +1160,11 @@ public abstract class ApexManager { } @Override + public boolean destroyCeSnapshots(int userId, int rollbackId) { + return true; + } + + @Override public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { return true; } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 3a203d52edbe..c3c2e5e65103 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -708,12 +708,15 @@ public class AppsFilter { return ret; } + /** + * This method recomputes all component / intent-based visibility and is intended to match the + * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)} + */ private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) { mQueriesViaComponent.clear(); for (int i = existingSettings.size() - 1; i >= 0; i--) { PackageSetting setting = existingSettings.valueAt(i); - if (setting.pkg == null - || mForceQueryable.contains(setting.appId)) { + if (setting.pkg == null || requestsQueryAllPackages(setting.pkg)) { continue; } for (int j = existingSettings.size() - 1; j >= 0; j--) { @@ -721,7 +724,7 @@ public class AppsFilter { continue; } final PackageSetting otherSetting = existingSettings.valueAt(j); - if (otherSetting.pkg == null) { + if (otherSetting.pkg == null || mForceQueryable.contains(otherSetting.appId)) { continue; } if (canQueryViaComponents(setting.pkg, otherSetting.pkg, mProtectedBroadcasts)) { diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 2df4a920d5c2..eddab76de5ee 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; import android.content.Context; @@ -42,10 +43,9 @@ import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; /** * A service for A/B OTA dexopting. @@ -123,15 +123,20 @@ public class OtaDexoptService extends IOtaDexopt.Stub { } final List<PackageSetting> important; final List<PackageSetting> others; + Predicate<PackageSetting> isPlatformPackage = pkgSetting -> + PLATFORM_PACKAGE_NAME.equals(pkgSetting.pkg.getPackageName()); synchronized (mPackageManagerService.mLock) { // Important: the packages we need to run with ab-ota compiler-reason. important = PackageManagerServiceUtils.getPackagesForDexopt( mPackageManagerService.mSettings.mPackages.values(), mPackageManagerService, DEBUG_DEXOPT); + // Remove Platform Package from A/B OTA b/160735835. + important.removeIf(isPlatformPackage); // Others: we should optimize this with the (first-)boot compiler-reason. others = new ArrayList<>(mPackageManagerService.mSettings.mPackages.values()); others.removeAll(important); others.removeIf(PackageManagerServiceUtils.REMOVE_IF_NULL_PKG); + others.removeIf(isPlatformPackage); // Pre-size the array list by over-allocating by a factor of 1.5. mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2); @@ -147,7 +152,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("Found a core app that's not important"); } mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting, - PackageManagerService.REASON_FIRST_BOOT)); + PackageManagerService.REASON_FIRST_BOOT)); } completeSize = mDexoptCommands.size(); diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java index e355bb9f6e60..30b1c2c93a45 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelper.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Pair; @@ -27,6 +28,8 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import java.io.File; import java.util.Set; + + // TODO: Move to .parsing sub-package @VisibleForTesting public interface PackageAbiHelper { @@ -34,7 +37,8 @@ public interface PackageAbiHelper { * Derive and get the location of native libraries for the given package, * which varies depending on where and how the package was installed. */ - NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting, + @NonNull + NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isUpdatedSystemApp, File appLib32InstallDir); /** @@ -51,7 +55,7 @@ public interface PackageAbiHelper { * If {@code extractLibs} is true, native libraries are extracted from the app if required. */ Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp, - String cpuAbiOverride, boolean extractLibs) throws PackageManagerException; + String cpuAbiOverride) throws PackageManagerException; /** * Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java index fc58968a7325..8af7e1f4f6d1 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -131,16 +131,16 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { } @Override - public NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting, - File appLib32InstallDir) { + public NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, + boolean isUpdatedSystemApp, File appLib32InstallDir) { // Trying to derive the paths, thus need the raw ABI info from the parsed package, and the // current state in PackageSetting is irrelevant. - return getNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()), + return deriveNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()), appLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(), - pkgSetting.getPkgState().isUpdatedSystemApp()); + isUpdatedSystemApp); } - private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis, + private static NativeLibraryPaths deriveNativeLibraryPaths(final Abis abis, final File appLib32InstallDir, final String codePath, final String sourceDir, final boolean isSystemApp, final boolean isUpdatedSystemApp) { final File codeFile = new File(codePath); @@ -296,22 +296,19 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { @Override public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, - boolean isUpdatedSystemApp, String cpuAbiOverride, boolean extractLibs) + boolean isUpdatedSystemApp, String cpuAbiOverride) throws PackageManagerException { // Give ourselves some initial paths; we'll come back for another // pass once we've determined ABI below. String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(pkg); String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg); - final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths( + final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths( new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi), PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(), isUpdatedSystemApp); - // We shouldn't attempt to extract libs from system app when it was not updated. - if (pkg.isSystem() && !isUpdatedSystemApp) { - extractLibs = false; - } + final boolean extractLibs = shouldExtractLibs(pkg, isUpdatedSystemApp); final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir; final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa; @@ -455,11 +452,21 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); return new Pair<>(abis, - getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, + deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(), isUpdatedSystemApp)); } + private boolean shouldExtractLibs(AndroidPackage pkg, boolean isUpdatedSystemApp) { + // We shouldn't extract libs if the package is a library or if extractNativeLibs=false + boolean extractLibs = !AndroidPackageUtils.isLibrary(pkg) && pkg.isExtractNativeLibs(); + // We shouldn't attempt to extract libs from system app when it was not updated. + if (pkg.isSystem() && !isUpdatedSystemApp) { + extractLibs = false; + } + return extractLibs; + } + /** * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. * i.e, so that all packages can be run inside a single process if required. diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 07f756771326..312dcddd577d 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -84,6 +84,8 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; +import com.android.server.SystemService; +import com.android.server.SystemServiceManager; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -201,6 +203,27 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; + private static final class Lifecycle extends SystemService { + private final PackageInstallerService mPackageInstallerService; + + Lifecycle(Context context, PackageInstallerService service) { + super(context); + mPackageInstallerService = service; + } + + @Override + public void onStart() { + // no-op + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { + mPackageInstallerService.onBroadcastReady(); + } + } + } + public PackageInstallerService(Context context, PackageManagerService pm, Supplier<PackageParser2> apexParserSupplier) { mContext = context; @@ -222,6 +245,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mApexManager = ApexManager.getInstance(); mStagingManager = new StagingManager(this, context, apexParserSupplier); + + LocalServices.getService(SystemServiceManager.class).startService( + new Lifecycle(context, this)); } boolean okToSendBroadcasts() { @@ -259,6 +285,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + private void onBroadcastReady() { + // Broadcasts are not sent while we restore sessions on boot, since no processes would be + // ready to listen to them. From now on, it is safe to send broadcasts which otherwise will + // be rejected by ActivityManagerService if its systemReady() is not completed. + mOkToSendBroadcasts = true; + } + void restoreAndApplyStagedSessionIfNeeded() { List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>(); synchronized (mSessions) { @@ -281,16 +314,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } mStagingManager.restoreSession(session, isDeviceUpgrading); } - // Broadcasts are not sent while we restore sessions on boot, since no processes would be - // ready to listen to them. From now on, we greedily assume that broadcasts requests are - // safe to send out. The worst that can happen is that a broadcast is attempted before - // ActivityManagerService completes its own systemReady(), in which case it will be rejected - // with an otherwise harmless exception. - // A more appropriate way to do this would be to wait until the correct boot phase is - // reached, but since we are not a SystemService we can't override onBootPhase. - // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable - // way either. - mOkToSendBroadcasts = true; } @GuardedBy("mSessions") @@ -592,12 +615,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } - if (mBypassNextStagedInstallerCheck) { - mBypassNextStagedInstallerCheck = false; - } else if (params.isStaged - && !isCalledBySystemOrShell(callingUid) - && !isWhitelistedStagedInstaller(requestedInstallerPackageName)) { - throw new SecurityException("Installer not allowed to commit staged install"); + if (params.isStaged && !isCalledBySystemOrShell(callingUid)) { + if (mBypassNextStagedInstallerCheck) { + mBypassNextStagedInstallerCheck = false; + } else if (!isStagedInstallerAllowed(requestedInstallerPackageName)) { + throw new SecurityException("Installer not allowed to commit staged install"); + } } if (!params.isMultiPackage) { @@ -729,7 +752,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements || callingUid == Process.SHELL_UID; } - private boolean isWhitelistedStagedInstaller(String installerName) { + private boolean isStagedInstallerAllowed(String installerName) { return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName); } @@ -869,7 +892,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public ParceledListSlice<SessionInfo> getStagedSessions() { - return mStagingManager.getSessions(Binder.getCallingUid()); + final List<SessionInfo> result = new ArrayList<>(); + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (session.isStaged() && !session.isDestroyed()) { + result.add(session.generateInfoForCaller(false, Binder.getCallingUid())); + } + } + } + return new ParceledListSlice<>(result); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index daf7f72b2ee7..9f3db704253e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -11492,15 +11492,14 @@ public class PackageManagerService extends IPackageManager.Stub } final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting); + final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp(); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); - final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage); final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = - packageAbiHelper.derivePackageAbi(parsedPackage, - pkgSetting.getPkgState().isUpdatedSystemApp(), cpuAbiOverride, - extractNativeLibs); + packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp, + cpuAbiOverride); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -11510,15 +11509,15 @@ public class PackageManagerService extends IPackageManager.Stub // structure. Try to detect abi based on directory structure. String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage); - if (parsedPackage.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp() && - pkgRawPrimaryCpuAbi == null) { + if (parsedPackage.isSystem() && !isUpdatedSystemApp + && pkgRawPrimaryCpuAbi == null) { final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis( parsedPackage); abis.applyTo(parsedPackage); abis.applyTo(pkgSetting); final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = - packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting, - sAppLib32InstallDir); + packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, + isUpdatedSystemApp, sAppLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); } } else { @@ -11529,8 +11528,8 @@ public class PackageManagerService extends IPackageManager.Stub .setSecondaryCpuAbi(secondaryCpuAbiFromSettings); final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = - packageAbiHelper.getNativeLibraryPaths(parsedPackage, - pkgSetting, sAppLib32InstallDir); + packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, + isUpdatedSystemApp, sAppLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); if (DEBUG_ABI_SELECTION) { @@ -11555,7 +11554,7 @@ public class PackageManagerService extends IPackageManager.Stub // ABIs we determined during compilation, but the path will depend on the final // package path (after the rename away from the stage path). final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = - packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting, + packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp, sAppLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); } @@ -17485,7 +17484,6 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags |= SCAN_NO_DEX; try { - final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage); PackageSetting pkgSetting; synchronized (mLock) { pkgSetting = mSettings.getPackageLPr(pkgName); @@ -17500,7 +17498,7 @@ public class PackageManagerService extends IPackageManager.Stub final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage, isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred, - abiOverride, extractNativeLibs); + abiOverride); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); } catch (PackageManagerException pme) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 53a3904ed8c1..14da9aa14470 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -38,7 +38,6 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser.SigningDetails; import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; -import android.content.pm.ParceledListSlice; import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; @@ -184,20 +183,6 @@ public class StagingManager { mApexManager.markBootCompleted(); } - ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) { - final List<PackageInstaller.SessionInfo> result = new ArrayList<>(); - synchronized (mStagedSessions) { - for (int i = 0; i < mStagedSessions.size(); i++) { - final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); - if (stagedSession.isDestroyed()) { - continue; - } - result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid)); - } - } - return new ParceledListSlice<>(result); - } - /** * Validates the signature used to sign the container of the new apex package * diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java index 88938b204f6c..c8e36481c738 100644 --- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java +++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java @@ -206,6 +206,16 @@ public class AppDataRollbackHelper { } /** + * Deletes snapshots of the credential encrypted apex data directories for the specified user, + * for the given rollback id. This method will be a no-op if the user is not unlocked. + */ + public void destroyApexCeSnapshots(int userId, int rollbackId) { + if (!isUserCredentialLocked(userId)) { + mApexManager.destroyCeSnapshots(userId, rollbackId); + } + } + + /** * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode * of the CE user data snapshot. diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index c3477804fb64..2db6043f1c0a 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -42,6 +42,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.ext.SdkExtensions; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseIntArray; @@ -61,9 +62,9 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; - /** * Information about a rollback available for a set of atomically installed packages. * @@ -699,11 +700,13 @@ class Rollback { void delete(AppDataRollbackHelper dataHelper) { assertInWorkerThread(); boolean containsApex = false; + Set<Integer> apexUsers = new ArraySet<>(); for (PackageRollbackInfo pkgInfo : info.getPackages()) { + List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers(); if (pkgInfo.isApex()) { containsApex = true; + apexUsers.addAll(snapshottedUsers); } else { - List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers(); for (int i = 0; i < snapshottedUsers.size(); i++) { // Destroy app data snapshot. int userId = snapshottedUsers.get(i); @@ -714,6 +717,9 @@ class Rollback { } if (containsApex) { dataHelper.destroyApexDeSnapshots(info.getRollbackId()); + for (int user : apexUsers) { + dataHelper.destroyApexCeSnapshots(user, info.getRollbackId()); + } } RollbackStore.deleteRollback(this); diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 37df5481d3c4..6dc1d6921dbb 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -53,16 +53,14 @@ public final class StorageSessionController { private final Context mContext; @GuardedBy("mLock") private final SparseArray<StorageUserConnection> mConnections = new SparseArray<>(); - private final boolean mIsFuseEnabled; private volatile ComponentName mExternalStorageServiceComponent; private volatile String mExternalStorageServicePackageName; private volatile int mExternalStorageServiceAppId; private volatile boolean mIsResetting; - public StorageSessionController(Context context, boolean isFuseEnabled) { + public StorageSessionController(Context context) { mContext = Objects.requireNonNull(context); - mIsFuseEnabled = isFuseEnabled; } /** @@ -361,6 +359,6 @@ public final class StorageSessionController { } private boolean shouldHandle(@Nullable VolumeInfo vol) { - return mIsFuseEnabled && !mIsResetting && (vol == null || isEmulatedOrPublic(vol)); + return !mIsResetting && (vol == null || isEmulatedOrPublic(vol)); } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 32f8da8d4a77..3406bd99c883 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -28,6 +28,7 @@ import android.database.ContentObserver; import android.os.Binder; import android.os.Handler; import android.provider.Settings; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; @@ -139,7 +140,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - mTimeDetectorStrategy.dump(pw, args); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + mTimeDetectorStrategy.dump(ipw, args); + ipw.flush(); } private void enforceSuggestTelephonyTimePermission() { diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index 9a39d2418994..e943978cfc14 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -17,25 +17,25 @@ package com.android.server.timedetector; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.os.TimestampedValue; +import android.util.IndentingPrintWriter; -import java.io.PrintWriter; +import com.android.server.timezonedetector.Dumpable; /** * The interface for the class that implements the time detection algorithm used by the * {@link TimeDetectorService}. * * <p>Most calls will be handled by a single thread but that is not true for all calls. For example - * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must - * handle thread safety. + * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so + * implementations must handle thread safety. * * @hide */ -public interface TimeDetectorStrategy { +public interface TimeDetectorStrategy extends Dumpable { /** * The interface used by the strategy to interact with the surrounding service. @@ -94,9 +94,6 @@ public interface TimeDetectorStrategy { /** Handle the auto-time setting being toggled on or off. */ void handleAutoTimeDetectionChanged(); - /** Dump debug information. */ - void dump(@NonNull PrintWriter pw, @Nullable String[] args); - // Utility methods below are to be moved to a better home when one becomes more obvious. /** diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index ceb548551635..fe0e82e66093 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -24,16 +24,15 @@ import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.os.TimestampedValue; +import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.timezonedetector.ArrayMapWithHistory; import com.android.server.timezonedetector.ReferenceWithHistory; -import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -203,8 +202,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @Override - public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) { - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + public synchronized void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) { ipw.println("TimeDetectorStrategy:"); ipw.increaseIndent(); // level 1 @@ -232,7 +230,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { ipw.decreaseIndent(); // level 2 ipw.decreaseIndent(); // level 1 - ipw.flush(); } @GuardedBy("this") diff --git a/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java index 3274f0e1112f..d6fdddf4ee43 100644 --- a/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java +++ b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java @@ -20,10 +20,10 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; /** * A partial decorator for {@link ArrayMap} that records historic values for each mapping for diff --git a/services/core/java/com/android/server/timezonedetector/Dumpable.java b/services/core/java/com/android/server/timezonedetector/Dumpable.java index 58a5a0b517f8..5603c38bd0ae 100644 --- a/services/core/java/com/android/server/timezonedetector/Dumpable.java +++ b/services/core/java/com/android/server/timezonedetector/Dumpable.java @@ -15,24 +15,27 @@ */ package com.android.server.timezonedetector; -import java.io.PrintWriter; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.IndentingPrintWriter; /** An interface for components that can write their internal state to dumpsys logs. */ public interface Dumpable { /** Dump internal state. */ - void dump(PrintWriter pw, String[] args); + void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args); /** * An interface that can be used expose when one component allows another to be registered so * that it is dumped at the same time. */ - interface Dumpee { + interface Container { /** * Registers the supplied {@link Dumpable}. When the implementation is dumped - * {@link Dumpable#dump(PrintWriter, String[])} should be called on the {@code dumpable}. + * {@link Dumpable#dump(IndentingPrintWriter, String[])} should be called on the + * {@code dumpable}. */ - void addDumpable(Dumpable dumpable); + void addDumpable(@NonNull Dumpable dumpable); } } diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java index 165419a7a38b..b63df05ad7a1 100644 --- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java +++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java @@ -19,8 +19,7 @@ package com.android.server.timezonedetector; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; - -import com.android.internal.util.IndentingPrintWriter; +import android.util.IndentingPrintWriter; import java.util.ArrayDeque; diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index d833586dc2dd..3d9ec6475a8b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -24,7 +24,7 @@ import android.annotation.NonNull; * * @hide */ -public interface TimeZoneDetectorInternal extends Dumpable.Dumpee { +public interface TimeZoneDetectorInternal extends Dumpable.Container { /** * Suggests the current time zone, determined using geolocation, to the detector. The diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java index d8db1ba0eb43..4464f7d136e3 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java @@ -49,7 +49,7 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter } @Override - public void addDumpable(Dumpable dumpable) { + public void addDumpable(@NonNull Dumpable dumpable) { mTimeZoneDetectorStrategy.addDumpable(dumpable); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index bb4f56d76943..d9415ce81636 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -36,6 +36,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.provider.Settings; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -118,7 +119,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, new ContentObserver(handler) { public void onChange(boolean selfChange) { - service.handleAutoTimeZoneDetectionChanged(); + service.handleAutoTimeZoneConfigChanged(); } }); return service; @@ -272,12 +273,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - mTimeZoneDetectorStrategy.dump(pw, args); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + mTimeZoneDetectorStrategy.dump(ipw, args); + ipw.flush(); } /** Internal method for handling the auto time zone configuration being changed. */ @VisibleForTesting - public void handleAutoTimeZoneDetectionChanged() { + public void handleAutoTimeZoneConfigChanged() { mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneConfigChanged); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 497ff3efc4b8..f947a6554412 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -21,8 +21,7 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; - -import java.io.PrintWriter; +import android.util.IndentingPrintWriter; /** * The interface for the class that implements the time detection algorithm used by the @@ -32,12 +31,12 @@ import java.io.PrintWriter; * and what to set it to. * * <p>Most calls will be handled by a single thread but that is not true for all calls. For example - * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must - * handle thread safety. + * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so + * implementations mustvhandle thread safety. * * @hide */ -public interface TimeZoneDetectorStrategy extends Dumpable.Dumpee { +public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { /** A listener for strategy events. */ interface StrategyListener { @@ -91,9 +90,4 @@ public interface TimeZoneDetectorStrategy extends Dumpable.Dumpee { * Called when there has been a change to the automatic time zone detection configuration. */ void handleAutoTimeZoneConfigChanged(); - - /** - * Dumps internal state such as field values. - */ - void dump(PrintWriter pw, String[] args); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index ab9a773d3b8c..9c36c3921e3e 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -32,14 +32,13 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; import android.content.Context; +import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -517,7 +516,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } @Override - public synchronized void addDumpable(Dumpable dumpable) { + public synchronized void addDumpable(@NonNull Dumpable dumpable) { mDumpables.add(dumpable); } @@ -525,8 +524,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * Dumps internal state such as field values. */ @Override - public synchronized void dump(PrintWriter pw, String[] args) { - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + public synchronized void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) { ipw.println("TimeZoneDetectorStrategy:"); ipw.increaseIndent(); // level 1 @@ -549,7 +547,6 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat for (Dumpable dumpable : mDumpables) { dumpable.dump(ipw, args); } - ipw.flush(); } /** diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 5f6323369d0a..f5eed30a19bf 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -114,7 +114,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final boolean DEBUG = false; private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed - private static final int MAX_PERSISTED_URI_GRANTS = 128; + private static final int MAX_PERSISTED_URI_GRANTS = 512; private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true; private final Object mLock = new Object(); diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index f5d68031b493..ecba3f9c27c4 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1357,7 +1357,12 @@ final class AccessibilityController { addedWindows.clear(); // Gets the top focused display Id and window token for supporting multi-display. - topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId(); + // If this top focused display is an embedded one, using its parent display as the + // top focused display. + final DisplayContent topFocusedDisplayContent = + mService.mRoot.getTopFocusedDisplayContent(); + topFocusedDisplayId = isEmbeddedDisplay(topFocusedDisplayContent) ? mDisplayId + : topFocusedDisplayContent.getDisplayId(); topFocusedWindowToken = topFocusedWindowState.mClient.asBinder(); } mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2d50012ed580..a87fe615e4df 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4605,8 +4605,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } - final boolean behindFullscreenActivity = stack.checkBehindFullscreenActivity( - this, null /* handleBehindFullscreenActivity */); + final boolean behindFullscreenActivity = !stack.shouldBeVisible(null /* starting */) + || stack.getOccludingActivityAbove(this) != null; return shouldBeVisible(behindFullscreenActivity, false /* ignoringKeyguard */); } @@ -7516,22 +7516,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * @return {@code true} if this is the resumed activity on its current display, {@code false} + * @return {@code true} if this is the focused activity on its current display, {@code false} * otherwise. */ - boolean isResumedActivityOnDisplay() { + boolean isFocusedActivityOnDisplay() { final DisplayContent display = getDisplay(); if (display == null) { return false; } - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity(); - if (resumedActivity != null) { - return resumedActivity == this; - } - } - return false; + return display.forAllTaskDisplayAreas(taskDisplayArea -> + taskDisplayArea.getFocusedActivity() == this); } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 3a24abd02064..ea9aab71bcb0 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -328,81 +328,6 @@ class ActivityStack extends Task { } } - private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper = - new CheckBehindFullscreenActivityHelper(); - private class CheckBehindFullscreenActivityHelper { - private boolean mAboveTop; - private boolean mBehindFullscreenActivity; - private ActivityRecord mToCheck; - private Consumer<ActivityRecord> mHandleBehindFullscreenActivity; - private boolean mHandlingOccluded; - - private void reset(ActivityRecord toCheck, - Consumer<ActivityRecord> handleBehindFullscreenActivity) { - mToCheck = toCheck; - mHandleBehindFullscreenActivity = handleBehindFullscreenActivity; - mAboveTop = true; - mBehindFullscreenActivity = false; - - if (!shouldBeVisible(null)) { - // The stack is not visible, so no activity in it should be displaying a starting - // window. Mark all activities below top and behind fullscreen. - mAboveTop = false; - mBehindFullscreenActivity = true; - } - - mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null; - } - - boolean process(ActivityRecord toCheck, - Consumer<ActivityRecord> handleBehindFullscreenActivity) { - reset(toCheck, handleBehindFullscreenActivity); - - if (!mHandlingOccluded && mBehindFullscreenActivity) { - return true; - } - - final ActivityRecord topActivity = topRunningActivity(); - final PooledFunction f = PooledLambda.obtainFunction( - CheckBehindFullscreenActivityHelper::processActivity, this, - PooledLambda.__(ActivityRecord.class), topActivity); - forAllActivities(f); - f.recycle(); - - return mBehindFullscreenActivity; - } - - /** Returns {@code true} to stop the outer loop and indicate the result is computed. */ - private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) { - if (mAboveTop) { - if (r == topActivity) { - if (r == mToCheck) { - // It is the top activity in a visible stack. - mBehindFullscreenActivity = false; - return true; - } - mAboveTop = false; - } - mBehindFullscreenActivity |= r.occludesParent(); - return false; - } - - if (mHandlingOccluded) { - // Iterating through all occluded activities. - if (mBehindFullscreenActivity) { - mHandleBehindFullscreenActivity.accept(r); - } - } else if (r == mToCheck) { - return true; - } else if (mBehindFullscreenActivity) { - // It is occluded before {@param toCheck} is found. - return true; - } - mBehindFullscreenActivity |= r.occludesParent(); - return false; - } - } - // TODO: Can we just loop through WindowProcessController#mActivities instead of doing this? private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp = new RemoveHistoryRecordsForApp(); @@ -1434,25 +1359,6 @@ class ActivityStack extends Task { } } - /** @see ActivityRecord#cancelInitializing() */ - void cancelInitializingActivities() { - // We don't want to clear starting window for activities that aren't behind fullscreen - // activities as we need to display their starting window until they are done initializing. - checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing); - } - - /** - * If an activity {@param toCheck} is given, this method returns {@code true} if the activity - * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling - * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded - * activities to the function. - */ - boolean checkBehindFullscreenActivity(ActivityRecord toCheck, - Consumer<ActivityRecord> handleBehindFullscreenActivity) { - return mCheckBehindFullscreenActivityHelper.process( - toCheck, handleBehindFullscreenActivity); - } - /** * Ensure that the top activity in the stack is resumed. * @@ -2414,7 +2320,7 @@ class ActivityStack extends Task { forAllActivities(ActivityRecord::removeLaunchTickRunnable); } - private void updateTransitLocked(int transit, ActivityOptions options) { + private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) { if (options != null) { ActivityRecord r = topRunningActivity(); if (r != null && !r.isState(RESUMED)) { @@ -2423,7 +2329,8 @@ class ActivityStack extends Task { ActivityOptions.abort(options); } } - getDisplay().mDisplayContent.prepareAppTransition(transit, false); + getDisplay().mDisplayContent.prepareAppTransition(transit, false, + 0 /* flags */, forceOverride); } final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options, @@ -2443,8 +2350,17 @@ class ActivityStack extends Task { // nothing to do! if (noAnimation) { ActivityOptions.abort(options); + } else if (isSingleTaskInstance()) { + // When a task is moved front on the display which can only contain one task, start + // a special transition. + // {@link AppTransitionController#handleAppTransitionReady} later picks up the + // transition, and schedules + // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered + // after contents are drawn on the display. + updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options, + true /* forceOverride */); } else { - updateTransitLocked(TRANSIT_TASK_TO_FRONT, options); + updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */); } return; } @@ -2490,9 +2406,13 @@ class ActivityStack extends Task { mStackSupervisor.mNoAnimActivities.add(r); } ActivityOptions.abort(options); + } else if (isSingleTaskInstance()) { + updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options, + true /* forceOverride */); } else { - updateTransitLocked(TRANSIT_TASK_TO_FRONT, options); + updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */); } + // If a new task is moved to the front, then mark the existing top activity as // supporting @@ -2646,16 +2566,6 @@ class ActivityStack extends Task { task.setBounds(task.isResizeable() ? bounds : null); } - /** - * Returns the top-most activity that occludes the given one, or @{code null} if none. - */ - @Nullable - private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { - ActivityRecord top = getActivity((ar) -> ar.occludesParent(), - true /* traverseTopToBottom */, activity); - return top != activity ? top : null; - } - boolean willActivityBeVisible(IBinder token) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r == null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index bf85db2e9700..4e1d789bebd8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -218,7 +218,6 @@ import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; import android.sysprop.DisplayProperties; import android.telecom.TelecomManager; -import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -1956,7 +1955,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.immersive = immersive; // update associated state if we're frontmost - if (r.isResumedActivityOnDisplay()) { + if (r.isFocusedActivityOnDisplay()) { if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r); applyUpdateLockStateLocked(r); } @@ -4007,8 +4006,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { ActivityRecord record = ActivityRecord.isInStackLocked(token); if (record == null) { - throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not " - + "found for: " + token); + return; } record.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations, smallestSizeConfigurations); @@ -4322,7 +4320,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.requestedVrComponent = (enabled) ? packageName : null; // Update associated state if this activity is currently focused - if (r.isResumedActivityOnDisplay()) { + if (r.isFocusedActivityOnDisplay()) { applyUpdateVrModeLocked(r); } return 0; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 98522082e1c6..201473e764c7 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -30,6 +30,7 @@ import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; +import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; @@ -40,7 +41,9 @@ import com.android.server.policy.WindowManagerPolicy; import com.android.server.protolog.common.ProtoLog; import java.util.Comparator; +import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -139,11 +142,108 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { return DISPLAY_AREA; } + @Override + final DisplayArea asDisplayArea() { + return this; + } + + @Override void forAllDisplayAreas(Consumer<DisplayArea> callback) { super.forAllDisplayAreas(callback); callback.accept(this); } + @Override + boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback, + boolean traverseTopToBottom) { + // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. + if (mType != DisplayArea.Type.ANY) { + return false; + } + + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + while (i >= 0 && i < childCount) { + T child = mChildren.get(i); + // Only traverse if the child is a DisplayArea. + if (child.asDisplayArea() != null && child.asDisplayArea() + .forAllTaskDisplayAreas(callback, traverseTopToBottom)) { + return true; + } + i += traverseTopToBottom ? -1 : 1; + } + return false; + } + + @Override + void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) { + // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. + if (mType != DisplayArea.Type.ANY) { + return; + } + + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + while (i >= 0 && i < childCount) { + T child = mChildren.get(i); + // Only traverse if the child is a DisplayArea. + if (child.asDisplayArea() != null) { + child.asDisplayArea().forAllTaskDisplayAreas(callback, traverseTopToBottom); + } + i += traverseTopToBottom ? -1 : 1; + } + } + + @Nullable + @Override + <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, + @Nullable R initValue, boolean traverseTopToBottom) { + // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. + if (mType != DisplayArea.Type.ANY) { + return initValue; + } + + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + R result = initValue; + while (i >= 0 && i < childCount) { + T child = mChildren.get(i); + // Only traverse if the child is a DisplayArea. + if (child.asDisplayArea() != null) { + result = (R) child.asDisplayArea() + .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom); + } + i += traverseTopToBottom ? -1 : 1; + } + return result; + } + + @Nullable + @Override + <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, + boolean traverseTopToBottom) { + // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. + if (mType != DisplayArea.Type.ANY) { + return null; + } + + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + while (i >= 0 && i < childCount) { + T child = mChildren.get(i); + // Only traverse if the child is a DisplayArea. + if (child.asDisplayArea() != null) { + R result = (R) child.asDisplayArea() + .getItemFromTaskDisplayAreas(callback, traverseTopToBottom); + if (result != null) { + return result; + } + } + i += traverseTopToBottom ? -1 : 1; + } + return null; + } + void setOrganizer(IDisplayAreaOrganizer organizer) { if (mOrganizer == organizer) return; IDisplayAreaOrganizer lastOrganizer = mOrganizer; diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 709e45368b19..a161acaa9cc5 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -63,16 +63,6 @@ public abstract class DisplayAreaPolicy { */ public abstract List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId); - /** - * @return the number of task display areas on the display. - */ - public abstract int getTaskDisplayAreaCount(); - - /** - * @return the task display area at index. - */ - public abstract TaskDisplayArea getTaskDisplayAreaAt(int index); - /** Provider for platform-default display area policy. */ static final class DefaultProvider implements DisplayAreaPolicy.Provider { @Override diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 3de7b60e68ae..465d089e7904 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -23,20 +23,24 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import android.annotation.Nullable; +import android.os.Bundle; import android.util.ArrayMap; +import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Objects; +import java.util.Set; +import java.util.function.BiFunction; /** * A builder for instantiating a complex {@link DisplayAreaPolicy} @@ -70,22 +74,93 @@ import java.util.Objects; */ class DisplayAreaPolicyBuilder { @Nullable private HierarchyBuilder mRootHierarchyBuilder; + private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); - /** Defines the root hierarchy. */ + /** + * When a window is created, the policy will use this function to select the + * {@link RootDisplayArea} to place that window in. The selected root can be either the one of + * the {@link #mRootHierarchyBuilder} or the one of any of the + * {@link #mDisplayAreaGroupHierarchyBuilders}. + **/ + @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; + + /** Defines the root hierarchy for the whole logical display. */ DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) { - // TODO(b/157683117): Add method to add sub root and root choosing func. mRootHierarchyBuilder = rootHierarchyBuilder; return this; } + /** + * Defines a DisplayAreaGroup hierarchy. Its root will be added as a child of the root + * hierarchy. + */ + DisplayAreaPolicyBuilder addDisplayAreaGroupHierarchy( + HierarchyBuilder displayAreaGroupHierarchy) { + mDisplayAreaGroupHierarchyBuilders.add(displayAreaGroupHierarchy); + return this; + } + + /** The policy will use this function to find the root to place windows in. */ + DisplayAreaPolicyBuilder setSelectRootForWindowFunc( + BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) { + mSelectRootForWindowFunc = selectRootForWindowFunc; + return this; + } + + /** Makes sure the setting meets the requirement. */ + private void validate() { + if (mRootHierarchyBuilder == null) { + throw new IllegalStateException("Root must be set for the display area policy."); + } + + boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null; + boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder); + for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { + HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); + if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) { + throw new IllegalStateException( + "DisplayAreaGroup must contain at least one TaskDisplayArea."); + } + + containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null; + containsDefaultTda = containsDefaultTda + || containsDefaultTaskDisplayArea(hierarchyBuilder); + } + + if (!containsImeContainer) { + throw new IllegalStateException("IME container must be set."); + } + + if (!containsDefaultTda) { + throw new IllegalStateException("There must be a default TaskDisplayArea."); + } + } + + /** Checks if the given hierarchy contains the default {@link TaskDisplayArea}. */ + private static boolean containsDefaultTaskDisplayArea(HierarchyBuilder displayAreaHierarchy) { + for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) { + if (displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId + == FEATURE_DEFAULT_TASK_CONTAINER) { + return true; + } + } + return false; + } + Result build(WindowManagerService wmService) { - Objects.requireNonNull(mRootHierarchyBuilder, - "Root must be set for the display area policy."); - Objects.requireNonNull(mRootHierarchyBuilder.mImeContainer, "Ime must not be null"); - Preconditions.checkCollectionNotEmpty(mRootHierarchyBuilder.mTaskDisplayAreas, - "TaskDisplayAreas must not be empty"); - mRootHierarchyBuilder.build(); - return new Result(wmService, mRootHierarchyBuilder.mRoot); + validate(); + + // Attach DA group roots to screen hierarchy before adding windows to group hierarchies. + mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders); + List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>( + mDisplayAreaGroupHierarchyBuilders.size()); + for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { + HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); + hierarchyBuilder.build(); + displayAreaGroupRoots.add(hierarchyBuilder.mRoot); + } + return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots, + mSelectRootForWindowFunc); } /** @@ -131,6 +206,14 @@ class DisplayAreaPolicyBuilder { /** Builds the {@link DisplayArea} hierarchy below root. */ private void build() { + build(null /* displayAreaGroupHierarchyBuilders */); + } + + /** + * Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those + * {@link HierarchyBuilder} as children. + */ + private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) { final WindowManagerPolicy policy = mRoot.mWmService.mPolicy; final int maxWindowLayerCount = policy.getMaxWindowLayer(); final DisplayArea.Tokens[] displayAreaForLayer = @@ -215,7 +298,9 @@ class DisplayAreaPolicyBuilder { if (leafType == LEAF_TYPE_TASK_CONTAINERS) { // We use the passed in TaskDisplayAreas for task container type of layer. // Skip creating Tokens even if there is no TDA. - addTaskDisplayAreasToLayer(areaForLayer[layer]); + addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]); + addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer], + displayAreaGroupHierarchyBuilders); leafArea.mSkipTokens = true; } else if (leafType == LEAF_TYPE_IME_CONTAINERS) { // We use the passed in ImeContainer for ime container type of layer. @@ -234,11 +319,11 @@ class DisplayAreaPolicyBuilder { // Notify the root that we have finished attaching all the DisplayAreas. Cache all the // feature related collections there for fast access. - mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas, mTaskDisplayAreas); + mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas); } - /** Adds all {@link TaskDisplayArea} to the specified layer */ - private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea) { + /** Adds all {@link TaskDisplayArea} to the application layer. */ + private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) { final int count = mTaskDisplayAreas.size(); for (int i = 0; i < count; i++) { PendingArea leafArea = @@ -249,6 +334,24 @@ class DisplayAreaPolicyBuilder { } } + /** Adds roots of the DisplayAreaGroups to the application layer. */ + private void addDisplayAreaGroupsToApplicationLayer( + DisplayAreaPolicyBuilder.PendingArea parentPendingArea, + @Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) { + if (displayAreaGroupHierarchyBuilders == null) { + return; + } + final int count = displayAreaGroupHierarchyBuilders.size(); + for (int i = 0; i < count; i++) { + DisplayAreaPolicyBuilder.PendingArea + leafArea = new DisplayAreaPolicyBuilder.PendingArea( + null /* feature */, APPLICATION_LAYER, parentPendingArea); + leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot; + leafArea.mMaxLayer = APPLICATION_LAYER; + parentPendingArea.mChildren.add(leafArea); + } + } + private static int typeOfLayer(WindowManagerPolicy policy, int layer) { if (layer == APPLICATION_LAYER) { return LEAF_TYPE_TASK_CONTAINERS; @@ -401,9 +504,20 @@ class DisplayAreaPolicyBuilder { } static class Result extends DisplayAreaPolicy { + final List<RootDisplayArea> mDisplayAreaGroupRoots; + final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; - Result(WindowManagerService wmService, RootDisplayArea root) { + Result(WindowManagerService wmService, RootDisplayArea root, + List<RootDisplayArea> displayAreaGroupRoots, + @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea> + selectRootForWindowFunc) { super(wmService, root); + mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); + mSelectRootForWindowFunc = selectRootForWindowFunc == null + // Always return the highest level root of the logical display when the func is + // not specified. + ? (window, options) -> mRoot + : selectRootForWindowFunc; } @Override @@ -414,39 +528,38 @@ class DisplayAreaPolicyBuilder { @VisibleForTesting DisplayArea.Tokens findAreaForToken(WindowToken token) { - // TODO(b/157683117): Choose root/sub root from OEM provided func. - return mRoot.findAreaForToken(token); + return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token); } @VisibleForTesting List<Feature> getFeatures() { - // TODO(b/157683117): Also get feature from sub root. - return new ArrayList<>(mRoot.mFeatures); + Set<Feature> features = new ArraySet<>(); + features.addAll(mRoot.mFeatures); + for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) { + features.addAll(mDisplayAreaGroupRoots.get(i).mFeatures); + } + return new ArrayList<>(features); } @Override public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId) { - // TODO(b/157683117): Also get display areas from sub root. - List<Feature> features = getFeatures(); + List<DisplayArea<? extends WindowContainer>> displayAreas = new ArrayList<>(); + getDisplayAreas(mRoot, featureId, displayAreas); + for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) { + getDisplayAreas(mDisplayAreaGroupRoots.get(i), featureId, displayAreas); + } + return displayAreas; + } + + private static void getDisplayAreas(RootDisplayArea root, int featureId, + List<DisplayArea<? extends WindowContainer>> displayAreas) { + List<Feature> features = root.mFeatures; for (int i = 0; i < features.size(); i++) { Feature feature = features.get(i); if (feature.mId == featureId) { - return new ArrayList<>(mRoot.mFeatureToDisplayAreas.get(feature)); + displayAreas.addAll(root.mFeatureToDisplayAreas.get(feature)); } } - return new ArrayList<>(); - } - - @Override - public int getTaskDisplayAreaCount() { - // TODO(b/157683117): Also add TDA from sub root. - return mRoot.mTaskDisplayAreas.size(); - } - - @Override - public TaskDisplayArea getTaskDisplayAreaAt(int index) { - // TODO(b/157683117): Get TDA from root/sub root based on their z-order. - return mRoot.mTaskDisplayAreas.get(index); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4f25609c77dd..0a3b884f79d0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -71,6 +71,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; @@ -910,7 +911,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @param root {@link RootWindowContainer} */ DisplayContent(Display display, RootWindowContainer root) { - super(root.mWindowManager); + super(root.mWindowManager, "DisplayContent", FEATURE_ROOT); if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) { throw new IllegalArgumentException("Display with ID=" + display.getDisplayId() + " already exists=" @@ -2191,52 +2192,27 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Returns the topmost stack on the display that is compatible with the input windowing mode and * activity type. Null is no compatible stack on the display. */ + @Nullable ActivityStack getStack(int windowingMode, int activityType) { - for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx) - .getStack(windowingMode, activityType); - if (stack != null) { - return stack; - } - } - return null; - } - - protected int getTaskDisplayAreaCount() { - return mDisplayAreaPolicy.getTaskDisplayAreaCount(); - } - - protected TaskDisplayArea getTaskDisplayAreaAt(int index) { - return mDisplayAreaPolicy.getTaskDisplayAreaAt(index); + return getItemFromTaskDisplayAreas(taskDisplayArea -> + taskDisplayArea.getStack(windowingMode, activityType)); } + @Nullable ActivityStack getStack(int rootTaskId) { - for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx).getStack(rootTaskId); - if (stack != null) { - return stack; - } - } - return null; + return getItemFromTaskDisplayAreas(taskDisplayArea -> + taskDisplayArea.getStack(rootTaskId)); } protected int getStackCount() { - int totalStackCount = 0; - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - totalStackCount += getTaskDisplayAreaAt(i).getStackCount(); - } - return totalStackCount; + return reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> + count + taskDisplayArea.getStackCount(), 0 /* initValue */); } @VisibleForTesting + @Nullable ActivityStack getTopStack() { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - final ActivityStack stack = getTaskDisplayAreaAt(i).getTopStack(); - if (stack != null) { - return stack; - } - } - return null; + return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopStack); } /** @@ -2536,7 +2512,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents). */ TaskDisplayArea getDefaultTaskDisplayArea() { - return mDisplayAreaPolicy.getTaskDisplayAreaAt(0); + return getItemFromTaskDisplayAreas(taskDisplayArea -> taskDisplayArea, + false /* traverseTopToBottom */); } void positionDisplayAt(int position, boolean includingParents) { @@ -2755,9 +2732,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void prepareFreezingTaskBounds() { - for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - getTaskDisplayAreaAt(tdaNdx).prepareFreezingTaskBounds(); - } + forAllTaskDisplayAreas(TaskDisplayArea::prepareFreezingTaskBounds); } void rotateBounds(int oldRotation, int newRotation, Rect bounds) { @@ -2952,9 +2927,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp pw.println(); pw.println(prefix + "Task display areas in top down Z order:"); - for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - getTaskDisplayAreaAt(tdaNdx).dump(pw, prefix + " ", dumpAll); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.dump(pw, prefix + " ", dumpAll); + }); pw.println(); if (!mExitingTokens.isEmpty()) { @@ -3519,12 +3494,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - private void updateImeControlTarget() { + void updateImeControlTarget() { mInputMethodControlTarget = computeImeControlTarget(); mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget); - final WindowState win = mInputMethodControlTarget != null - ? mInputMethodControlTarget.getWindow() : null; + final WindowState win = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget); final IBinder token = win != null ? win.mClient.asBinder() : null; // Note: not allowed to call into IMMS with the WM lock held, hence the post. mWmService.mH.post(() -> @@ -3548,6 +3522,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) { return mRemoteInsetsControlTarget; } else { + // Now, a special case -- if the last target's window is in the process of exiting, but + // not removed, keep on the last target to avoid IME flicker. + final WindowState cur = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget); + if (cur != null && !cur.mRemoved && cur.isDisplayedLw() && cur.isClosing() + && !cur.isActivityTypeHome()) { + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "Not changing control while current window" + + " is closing and not removed"); + } + return cur; + } // Otherwise, we just use the ime target as received from IME. return mInputMethodInputTarget; } @@ -4126,9 +4111,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } // Initialize state of exiting applications. - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - getTaskDisplayAreaAt(i).setExitingTokensHasVisible(hasVisible); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.setExitingTokensHasVisible(hasVisible); + }); } void removeExistingTokensIfPossible() { @@ -4140,9 +4125,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } // Time to remove any exiting applications? - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - getTaskDisplayAreaAt(i).removeExistingAppTokensIfPossible(); - } + forAllTaskDisplayAreas(TaskDisplayArea::removeExistingAppTokensIfPossible); } @Override @@ -4466,9 +4449,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void assignStackOrdering() { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - getTaskDisplayAreaAt(i).assignStackOrdering(getPendingTransaction()); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.assignStackOrdering(getPendingTransaction()); + }); } /** @@ -5010,13 +4993,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Nullable ActivityStack getFocusedStack() { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - final ActivityStack stack = getTaskDisplayAreaAt(i).getFocusedStack(); - if (stack != null) { - return stack; - } - } - return null; + return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedStack); } /** @@ -5024,15 +5001,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ void removeStacksInWindowingModes(int... windowingModes) { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - getTaskDisplayAreaAt(i).removeStacksInWindowingModes(windowingModes); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.removeStacksInWindowingModes(windowingModes); + }); } void removeStacksWithActivityTypes(int... activityTypes) { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - getTaskDisplayAreaAt(i).removeStacksWithActivityTypes(activityTypes); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.removeStacksWithActivityTypes(activityTypes); + }); } ActivityRecord topRunningActivity() { @@ -5048,15 +5025,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * can be shown on top of the keyguard will be considered. * @return The top running activity. {@code null} if none is available. */ + @Nullable ActivityRecord topRunningActivity(boolean considerKeyguardState) { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - final ActivityRecord activity = getTaskDisplayAreaAt(i) - .topRunningActivity(considerKeyguardState); - if (activity != null) { - return activity; - } - } - return null; + return getItemFromTaskDisplayAreas(taskDisplayArea -> + taskDisplayArea.topRunningActivity(considerKeyguardState)); } boolean updateDisplayOverrideConfigurationLocked() { @@ -5217,18 +5189,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void remove() { mRemoving = true; - ActivityStack lastReparentedStack = null; + ActivityStack lastReparentedStack; mRootWindowContainer.mStackSupervisor.beginDeferResume(); try { - int numTaskContainers = getTaskDisplayAreaCount(); - for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) { - final ActivityStack lastReparentedStackFromArea = getTaskDisplayAreaAt(tdaNdx) - .remove(); + lastReparentedStack = reduceOnAllTaskDisplayAreas((taskDisplayArea, stack) -> { + final ActivityStack lastReparentedStackFromArea = taskDisplayArea.remove(); if (lastReparentedStackFromArea != null) { - lastReparentedStack = lastReparentedStackFromArea; + return lastReparentedStackFromArea; } - } + return stack; + }, null /* initValue */, false /* traverseTopToBottom */); } finally { mRootWindowContainer.mStackSupervisor.endDeferResume(); } @@ -5255,26 +5226,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } // Check if all task display areas have only the empty home stacks left. - boolean onlyEmptyHomeStacksLeft = true; - for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = getTaskDisplayAreaAt(tdaNdx); + boolean hasNonEmptyHomeStack = forAllTaskDisplayAreas(taskDisplayArea -> { if (taskDisplayArea.getStackCount() != 1) { - onlyEmptyHomeStacksLeft = false; - break; + return true; } final ActivityStack stack = taskDisplayArea.getStackAt(0); - if (!stack.isActivityTypeHome() || stack.hasChild()) { - onlyEmptyHomeStacksLeft = false; - break; - } - } - if (onlyEmptyHomeStacksLeft) { + return !stack.isActivityTypeHome() || stack.hasChild(); + }); + if (!hasNonEmptyHomeStack) { // Release this display if only empty home stack(s) are left. This display will be // released along with the stack(s) removal. - for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final ActivityStack s = getTaskDisplayAreaAt(tdaNdx).getStackAt(0); - s.removeIfPossible(); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.getStackAt(0).removeIfPossible(); + }); } else if (getTopStack() == null) { removeIfPossible(); mRootWindowContainer.mStackSupervisor @@ -5339,10 +5303,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } mInEnsureActivitiesVisible = true; try { - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges, + forAllTaskDisplayAreas(taskDisplayArea -> { + taskDisplayArea.ensureActivitiesVisible(starting, configChanges, preserveWindows, notifyClients); - } + }); } finally { mInEnsureActivitiesVisible = false; } @@ -5357,8 +5321,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void setDisplayToSingleTaskInstance() { - final int taskDisplayAreaCount = getTaskDisplayAreaCount(); - if (taskDisplayAreaCount > 1) { + int tdaCount = reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> ++count, + 0 /* initValue */); + if (tdaCount > 1) { throw new IllegalArgumentException( "Display already has multiple task display areas. display=" + this); } @@ -5581,6 +5546,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mRemoteInsetsController = controller; } + /** + * Notifies the remote insets controller that the top focused window has changed. + * + * @param packageName The name of the package that is open in the top focused window. + */ + void topFocusedWindowChanged(String packageName) { + try { + mRemoteInsetsController.topFocusedWindowChanged(packageName); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver package in top focused window change", e); + } + } + void notifyInsetsChanged() { try { mRemoteInsetsController.insetsChanged( diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 96d10a10aa75..d2f5d2d58006 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -386,11 +386,6 @@ public class DisplayPolicy { private int mForcingShowNavBarLayer; private boolean mForceShowSystemBars; - /** - * Force the display of system bars regardless of other settings. - */ - private boolean mForceShowSystemBarsFromExternal; - private boolean mShowingDream; private boolean mLastShowingDream; private boolean mDreamingLockscreen; @@ -480,7 +475,6 @@ public class DisplayPolicy { final Resources r = mContext.getResources(); mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer); mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer); - mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars); mAccessibilityManager = (AccessibilityManager) mContext.getSystemService( Context.ACCESSIBILITY_SERVICE); @@ -698,17 +692,6 @@ public class DisplayPolicy { return mDockMode; } - /** - * @see WindowManagerService.setForceShowSystemBars - */ - void setForceShowSystemBars(boolean forceShowSystemBars) { - mForceShowSystemBarsFromExternal = forceShowSystemBars; - } - - boolean getForceShowSystemBars() { - return mForceShowSystemBarsFromExternal; - } - public boolean hasNavigationBar() { return mHasNavigationBar; } @@ -3539,8 +3522,7 @@ public class DisplayPolicy { // We need to force system bars when the docked stack is visible, when the freeform stack // is focused but also when we are resizing for the transitions when docked stack // visibility changes. - mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing - || mForceShowSystemBarsFromExternal; + mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing; final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing(); // apply translucent bar vis flags @@ -3915,8 +3897,8 @@ public class DisplayPolicy { } pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen); pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); - pw.print(prefix); pw.print("mForceShowSystemBarsFromExternal="); - pw.print(mForceShowSystemBarsFromExternal); + pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars"); + pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars()); pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn); mStatusBarController.dump(pw, prefix); mNavigationBarController.dump(pw, prefix); diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 8298763c1392..99ee5e121b7a 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -18,13 +18,14 @@ package com.android.server.wm; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME; -import android.graphics.PixelFormat; import android.view.InsetsSource; import android.view.WindowInsets; import com.android.internal.annotations.VisibleForTesting; import com.android.server.protolog.common.ProtoLog; +import java.io.PrintWriter; + /** * Controller for IME inset source on the server. It's called provider as it provides the * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. @@ -132,8 +133,17 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { || (mImeTargetFromIme != null && dcTarget.getParentWindow() == mImeTargetFromIme && dcTarget.mSubLayer > mImeTargetFromIme.mSubLayer) || mImeTargetFromIme == mDisplayContent.getImeFallback() - // If IME target is transparent but control target matches requesting window. - || (controlTarget == mImeTargetFromIme - && PixelFormat.formatHasAlpha(dcTarget.mAttrs.format)); + || (!mImeTargetFromIme.isClosing() && controlTarget == mImeTargetFromIme); + } + + @Override + public void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + if (mImeTargetFromIme != null) { + pw.print(prefix); + pw.print("showImePostLayout pending for mImeTargetFromIme="); + pw.print(mImeTargetFromIme); + pw.println(); + } } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 3e88566449fe..0216db471843 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.os.Process.myPid; -import static android.os.Process.myUid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; @@ -480,11 +478,18 @@ final class InputMonitor { mService.getRecentsAnimationController(); final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); - if (inputWindowHandle == null || w.mRemoved) { + final int type = w.mAttrs.type; + final boolean isVisible = w.isVisibleLw(); + if (inputChannel == null || inputWindowHandle == null || w.mRemoved + || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { + // Assign an InputInfo with type to the overlay window which can't receive input + // event. This is used to omit Surfaces from occlusion detection. + populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible); mInputTransaction.setInputWindowInfo( w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), mInvalidInputWindow); + return; } // Skip this window because it cannot possibly receive input. return; @@ -492,24 +497,8 @@ final class InputMonitor { final int flags = w.mAttrs.flags; final int privateFlags = w.mAttrs.privateFlags; - final int type = w.mAttrs.type; - final boolean isVisible = w.isVisibleLw(); - - // Assign an InputInfo with type to the overlay window which can't receive input event. - // This is used to omit Surfaces from occlusion detection. - if (inputChannel == null - || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { - if (!w.mWinAnimator.hasSurface()) { - return; - } - populateOverlayInputInfo(inputWindowHandle, w.getName(), type, isVisible); - mInputTransaction.setInputWindowInfo( - w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), - inputWindowHandle); - return; - } - final boolean hasFocus = w.isFocused(); + if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) { @@ -582,8 +571,6 @@ final class InputMonitor { inputWindowHandle.visible = isVisible; inputWindowHandle.canReceiveKeys = false; inputWindowHandle.hasFocus = false; - inputWindowHandle.ownerPid = myPid(); - inputWindowHandle.ownerUid = myUid(); inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; inputWindowHandle.scaleFactor = 1; inputWindowHandle.layoutParamsFlags = diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index c50f296504fc..3ffc26a7a8ad 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -62,4 +62,8 @@ interface InsetsControlTarget { return false; } + /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */ + static WindowState asWindowOrNull(InsetsControlTarget target) { + return target != null ? target.getWindow() : null; + } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 254356d673e3..b7287e718bd6 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -46,7 +46,9 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; +import android.view.WindowManager; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.DisplayThread; @@ -97,12 +99,30 @@ class InsetsPolicy { private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); private boolean mAnimatingShown; + /** + * Let remote insets controller control system bars regardless of other settings. + */ + private boolean mRemoteInsetsControllerControlsSystemBars; private final float[] mTmpFloat9 = new float[9]; InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { mStateController = stateController; mDisplayContent = displayContent; mPolicy = displayContent.getDisplayPolicy(); + mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean( + R.bool.config_remoteInsetsControllerControlsSystemBars); + } + + boolean getRemoteInsetsControllerControlsSystemBars() { + return mRemoteInsetsControllerControlsSystemBars; + } + + /** + * Used only for testing. + */ + @VisibleForTesting + void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) { + mRemoteInsetsControllerControlsSystemBars = controlsSystemBars; } /** Updates the target which can control system bars. */ @@ -256,6 +276,11 @@ class InsetsPolicy { // Notification shade has control anyways, no reason to force anything. return focusedWin; } + if (remoteInsetsControllerControlsSystemBars(focusedWin)) { + mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( + focusedWin.mAttrs.packageName); + return mDisplayContent.mRemoteInsetsControlTarget; + } if (forceShowsSystemBarsForWindowingMode) { // Status bar is forcibly shown for the windowing mode which is a steady state. // We don't want the client to control the status bar, and we will dispatch the real @@ -285,6 +310,11 @@ class InsetsPolicy { // Notification shade has control anyways, no reason to force anything. return focusedWin; } + if (remoteInsetsControllerControlsSystemBars(focusedWin)) { + mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( + focusedWin.mAttrs.packageName); + return mDisplayContent.mRemoteInsetsControlTarget; + } if (forceShowsSystemBarsForWindowingMode) { // Navigation bar is forcibly shown for the windowing mode which is a steady state. // We don't want the client to control the navigation bar, and we will dispatch the real @@ -300,6 +330,28 @@ class InsetsPolicy { return focusedWin; } + /** + * Determines whether the remote insets controller should take control of system bars for all + * windows. + */ + boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) { + if (focusedWin == null) { + return false; + } + if (!mRemoteInsetsControllerControlsSystemBars) { + return false; + } + if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) { + // No remote insets control target to take control of insets. + return false; + } + // If necessary, auto can control application windows when + // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases + // where we want to dictate system bar inset state for applications. + return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + } + private boolean forceShowsStatusBarTransiently() { final WindowState win = mPolicy.getStatusBar(); return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0; @@ -321,10 +373,7 @@ class InsetsPolicy { // We need to force system bars when the docked stack is visible, when the freeform stack // is visible but also when we are resizing for the transitions when docked stack // visibility changes. - return isDockedStackVisible - || isFreeformStackVisible - || isResizing - || mPolicy.getForceShowSystemBars(); + return isDockedStackVisible || isFreeformStackVisible || isResizing; } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 63083faaddb1..9c978fd0c867 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -44,6 +45,7 @@ import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.WindowManager; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.protolog.common.ProtoLog; import java.io.PrintWriter; @@ -74,7 +76,21 @@ class InsetsStateController { w.notifyInsetsChanged(); } }; - private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { }; + private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { + @Override + public void notifyInsetsControlChanged() { + InsetsSourceControl[] controls = getControlsForDispatch(this); + if (controls == null) { + return; + } + for (InsetsSourceControl control : controls) { + if (control.getType() == ITYPE_IME) { + mDisplayContent.mWmService.mH.post(() -> + InputMethodManagerInternal.get().removeImeSurface()); + } + } + } + }; InsetsStateController(DisplayContent displayContent) { mDisplayContent = displayContent; @@ -171,6 +187,7 @@ class InsetsStateController { state = new InsetsState(state); state.removeSource(ITYPE_STATUS_BAR); state.removeSource(ITYPE_NAVIGATION_BAR); + state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); } if (aboveIme) { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 9c535e4a41d7..76f236534b69 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -40,6 +40,7 @@ import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING; import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID; import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED; +import android.annotation.Nullable; import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; @@ -514,9 +515,9 @@ class KeyguardController { * Only the top non-pinned activity of the focusable stack on each display can control its * occlusion state. */ + @Nullable private ActivityStack getStackForControllingOccluding(DisplayContent display) { - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); + return display.getItemFromTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); if (stack != null && stack.isFocusableAndVisible() @@ -524,8 +525,8 @@ class KeyguardController { return stack; } } - } - return null; + return null; + }); } void dumpStatus(PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index c7f78342c829..c255a18190f7 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -359,17 +359,20 @@ class RemoteAnimationController implements DeathRecipient { RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds, Rect endBounds, Rect startBounds) { mWindowContainer = windowContainer; - mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds); if (startBounds != null) { mStartBounds = new Rect(startBounds); + mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, + mStartBounds); mTmpRect.set(startBounds); mTmpRect.offsetTo(0, 0); if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds, - mTmpRect); + mTmpRect, new Rect()); } } else { + mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, + new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom)); mStartBounds = null; } } @@ -407,13 +410,15 @@ class RemoteAnimationController implements DeathRecipient { final Point mPosition = new Point(); final Rect mLocalBounds; final Rect mStackBounds = new Rect(); + final Rect mStartBounds = new Rect(); RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, - Rect localBounds, Rect stackBounds) { + Rect localBounds, Rect stackBounds, Rect startBounds) { mRecord = record; mPosition.set(position.x, position.y); mLocalBounds = localBounds; mStackBounds.set(stackBounds); + mStartBounds.set(startBounds); } @Override @@ -427,13 +432,12 @@ class RemoteAnimationController implements DeathRecipient { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); // Restore position and stack crop until client has a chance to modify it. - if (mRecord.mStartBounds != null) { - t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top); - t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(), - mRecord.mStartBounds.height()); + if (mStartBounds.isEmpty()) { + t.setPosition(animationLeash, 0, 0); + t.setWindowCrop(animationLeash, -1, -1); } else { - t.setPosition(animationLeash, mPosition.x, mPosition.y); - t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height()); + t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top); + t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height()); } mCapturedLeash = animationLeash; mCapturedFinishCallback = finishCallback; diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index 9d408540c838..faed7fa99fdd 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; -import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; @@ -44,19 +43,11 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */ private DisplayArea.Tokens[] mAreaForLayer; - /** - * List of {@link TaskDisplayArea} that are attached to this {@link DisplayArea} hierarchy. The - * order is the same as their z-order. - * - * TODO(b/157683117): Instead of caching the TDAs, always traverse the hierarchy to get them. - */ - ArrayList<TaskDisplayArea> mTaskDisplayAreas; - /** Whether the hierarchy has been built. */ private boolean mHasBuiltHierarchy; - RootDisplayArea(WindowManagerService wms) { - super(wms, Type.ANY, "RootDisplayArea", FEATURE_ROOT); + RootDisplayArea(WindowManagerService wms, String name, int featureId) { + super(wms, Type.ANY, name, featureId); } /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */ @@ -73,15 +64,13 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { /** Callback after {@link DisplayArea} hierarchy has been built. */ void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer, - Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas, - ArrayList<TaskDisplayArea> taskDisplayAreas) { + Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas) { if (mHasBuiltHierarchy) { throw new IllegalStateException("Root should only build the hierarchy once"); } mHasBuiltHierarchy = true; mFeatures = Collections.unmodifiableList(features); mAreaForLayer = areaForLayer; - mTaskDisplayAreas = taskDisplayAreas; mFeatureToDisplayAreas = featureToDisplayAreas; } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6e2df84e2e96..40f8fab510ba 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1106,9 +1106,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean displayHasContent = false; ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, - "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w" - + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d", - w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout); + "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w" + + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d", + w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout); if (w.mHasSurface && onScreen) { if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) { mUserActivityTimeout = w.mAttrs.userActivityTimeout; @@ -1457,16 +1457,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } void startHomeOnEmptyDisplays(String reason) { - for (int i = getChildCount() - 1; i >= 0; i--) { - final DisplayContent display = getChildAt(i); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - if (taskDisplayArea.topRunningActivity() == null) { - startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea, - false /* allowInstrumenting */, false /* fromHomeKey */); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + if (taskDisplayArea.topRunningActivity() == null) { + startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea, + false /* allowInstrumenting */, false /* fromHomeKey */); } - } + }); } boolean startHomeOnDisplay(int userId, String reason, int displayId) { @@ -1483,13 +1479,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } final DisplayContent display = getDisplayContent(displayId); - boolean result = false; - for (int tcNdx = display.getTaskDisplayAreaCount() - 1; tcNdx >= 0; --tcNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tcNdx); - result |= startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea, - allowInstrumenting, fromHomeKey); - } - return result; + return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> + result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea, + allowInstrumenting, fromHomeKey), + false /* initValue */); } /** @@ -1826,31 +1819,27 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ArrayList<IBinder> topActivityTokens = new ArrayList<>(); final ActivityStack topFocusedStack = getTopDisplayFocusedStack(); // Traverse all displays. - for (int dNdx = getChildCount() - 1; dNdx >= 0; dNdx--) { - final DisplayContent display = getChildAt(dNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = - display.getTaskDisplayAreaAt(tdaNdx); - // Traverse all stacks on a display area. - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - // Get top activity from a visible stack and add it to the list. - if (stack.shouldBeVisible(null /* starting */)) { - final ActivityRecord top = stack.getTopNonFinishingActivity(); - if (top != null) { - if (stack == topFocusedStack) { - topActivityTokens.add(0, top.appToken); - } else { - topActivityTokens.add(top.appToken); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + // Traverse all stacks on a display area. + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + // Get top activity from a visible stack and add it to the list. + if (stack.shouldBeVisible(null /* starting */)) { + final ActivityRecord top = stack.getTopNonFinishingActivity(); + if (top != null) { + if (stack == topFocusedStack) { + topActivityTokens.add(0, top.appToken); + } else { + topActivityTokens.add(top.appToken); } } } } - } + }); return topActivityTokens; } + @Nullable ActivityStack getTopDisplayFocusedStack() { for (int i = getChildCount() - 1; i >= 0; --i) { final ActivityStack focusedStack = getChildAt(i).getFocusedStack(); @@ -1861,6 +1850,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return null; } + @Nullable ActivityRecord getTopResumedActivity() { final ActivityStack focusedStack = getTopDisplayFocusedStack(); if (focusedStack == null) { @@ -1872,18 +1862,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // The top focused stack might not have a resumed activity yet - look on all displays in // focus order. - for (int i = getChildCount() - 1; i >= 0; --i) { - final DisplayContent display = getChildAt(i); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - final ActivityRecord resumedActivityOnTaskContainer = taskDisplayArea - .getFocusedActivity(); - if (resumedActivityOnTaskContainer != null) { - return resumedActivityOnTaskContainer; - } - } - } - return null; + return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity); } boolean isTopDisplayFocusedStack(ActivityStack stack) { @@ -1897,25 +1876,21 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // First, found out what is currently the foreground app, so that we don't blow away the // previous app if this activity is being hosted by the process that is actually still the // foreground. - WindowProcessController fgApp = null; - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - if (isTopDisplayFocusedStack(stack)) { - final ActivityRecord resumedActivity = stack.getResumedActivity(); - if (resumedActivity != null) { - fgApp = resumedActivity.app; - } else if (stack.mPausingActivity != null) { - fgApp = stack.mPausingActivity.app; - } - break; + WindowProcessController fgApp = reduceOnAllTaskDisplayAreas((taskDisplayArea, app) -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + if (isTopDisplayFocusedStack(stack)) { + final ActivityRecord resumedActivity = stack.getResumedActivity(); + if (resumedActivity != null) { + app = resumedActivity.app; + } else if (stack.mPausingActivity != null) { + app = stack.mPausingActivity.app; } + break; } } - } + return app; + }, null /* initValue */); // Now set this one as the previous process, only if that really makes sense to. if (r.hasProcess() && fgApp != null && r.app != fgApp @@ -2024,16 +1999,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mCurrentUser = userId; mStackSupervisor.mStartingUsers.add(uss); - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - stack.switchUser(userId); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + stack.switchUser(userId); } - } + }); final int restoreStackId = mUserStackInFront.get(userId); ActivityStack stack = getStack(restoreStackId); @@ -2221,6 +2192,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } + @Nullable ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); mTmpFindTaskResult.clear(); @@ -2234,20 +2206,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - if (taskDisplayArea == preferredTaskDisplayArea) { - continue; - } + final ActivityRecord task = getItemFromTaskDisplayAreas(taskDisplayArea -> { + if (taskDisplayArea == preferredTaskDisplayArea) { + return null; + } - taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */, - mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; - } + taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */, + mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; } + return null; + }); + if (task != null) { + return task; } if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found"); @@ -2261,24 +2233,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished. */ int finishTopCrashedActivities(WindowProcessController app, String reason) { - Task finishedTask = null; ActivityStack focusedStack = getTopDisplayFocusedStack(); - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - // It is possible that request to finish activity might also remove its task and - // stack, so we need to be careful with indexes in the loop and check child count - // every time. - for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx); - final Task t = stack.finishTopCrashedActivityLocked(app, reason); - if (stack == focusedStack || finishedTask == null) { - finishedTask = t; - } + Task finishedTask = reduceOnAllTaskDisplayAreas((taskDisplayArea, task) -> { + // It is possible that request to finish activity might also remove its task and + // stack, so we need to be careful with indexes in the loop and check child count + // every time. + for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx); + final Task t = stack.finishTopCrashedActivityLocked(app, reason); + if (stack == focusedStack || task == null) { + task = t; } } - } + return task; + }, null /* initValue */); return finishedTask != null ? finishedTask.mTaskId : INVALID_TASK_ID; } @@ -2300,33 +2268,36 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - boolean resumedOnDisplay = false; final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - final ActivityRecord topRunningActivity = stack.topRunningActivity(); - if (!stack.isFocusableAndVisible() || topRunningActivity == null) { - continue; - } - if (stack == targetStack) { - // Simply update the result for targetStack because the targetStack had - // already resumed in above. We don't want to resume it again, especially in - // some cases, it would cause a second launch failure if app process was - // dead. - resumedOnDisplay |= result; - continue; - } - if (taskDisplayArea.isTopStack(stack) && topRunningActivity.isState(RESUMED)) { - // Kick off any lingering app transitions form the MoveTaskToFront - // operation, but only consider the top task and stack on that display. - stack.executeAppTransition(targetOptions); - } else { - resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target); - } - } - } + final boolean curResult = result; + boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas( + (taskDisplayArea, resumed) -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + final ActivityRecord topRunningActivity = stack.topRunningActivity(); + if (!stack.isFocusableAndVisible() || topRunningActivity == null) { + continue; + } + if (stack == targetStack) { + // Simply update the result for targetStack because the targetStack + // had already resumed in above. We don't want to resume it again, + // especially in some cases, it would cause a second launch failure + // if app process was dead. + resumed |= curResult; + continue; + } + if (taskDisplayArea.isTopStack(stack) + && topRunningActivity.isState(RESUMED)) { + // Kick off any lingering app transitions form the MoveTaskToFront + // operation, but only consider the top task and stack on that + // display. + stack.executeAppTransition(targetOptions); + } else { + resumed |= topRunningActivity.makeActiveIfNeeded(target); + } + } + return resumed; + }, false /* initValue */); if (!resumedOnDisplay) { // In cases when there are no valid activities (e.g. device just booted or launcher // crashed) it's possible that nothing was resumed on a display. Requesting resume @@ -2360,8 +2331,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // Set the sleeping state of the stacks on the display. - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); + display.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); if (displayShouldSleep) { @@ -2375,7 +2345,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // triggered after contents are drawn on the display. if (display.isSingleTaskInstance()) { display.mDisplayContent.prepareAppTransition( - TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false); + TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false, + 0 /* flags */, true /* forceOverride*/); } stack.awakeFromSleepingLocked(); if (display.isSingleTaskInstance()) { @@ -2399,7 +2370,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> false /* preserveWindows */); } } - } + }); } } @@ -2500,29 +2471,24 @@ class RootWindowContainer extends WindowContainer<DisplayContent> ArrayList<ActivityManager.StackInfo> getAllStackInfos(int displayId) { ArrayList<ActivityManager.StackInfo> list = new ArrayList<>(); if (displayId == INVALID_DISPLAY) { - for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - list.add(getStackInfo(stack)); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + list.add(getStackInfo(stack)); } - } + }); return list; } final DisplayContent display = getDisplayContent(displayId); if (display == null) { return list; } - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); + display.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); list.add(getStackInfo(stack)); } - } + }); return list; } @@ -2723,28 +2689,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Tries to put all activity stacks to sleep. Returns true if all stacks were // successfully put to sleep. boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) { - boolean allSleep = true; - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - // Stacks and activities could be removed while putting activities to sleep if - // the app process was gone. This prevents us getting exception by accessing an - // invalid stack index. - if (sNdx >= taskDisplayArea.getStackCount()) { - continue; - } - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - if (allowDelay) { - allSleep &= stack.goToSleepIfPossible(shuttingDown); - } else { - stack.goToSleep(); - } + return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + // Stacks and activities could be removed while putting activities to sleep if + // the app process was gone. This prevents us getting exception by accessing an + // invalid stack index. + if (sNdx >= taskDisplayArea.getStackCount()) { + continue; + } + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + if (allowDelay) { + result &= stack.goToSleepIfPossible(shuttingDown); + } else { + stack.goToSleep(); } } - } - return allSleep; + return result; + }, true /* initValue */); } void handleAppCrash(WindowProcessController app) { @@ -3112,18 +3073,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } boolean handleAppDied(WindowProcessController app) { - boolean hasVisibleActivities = false; - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - hasVisibleActivities |= stack.handleAppDied(app); - } + return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + result |= stack.handleAppDied(app); } - } - return hasVisibleActivities; + return result; + }, false /* initValue */); } void closeSystemDialogActivities(String reason) { @@ -3240,18 +3196,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } void finishVoiceTask(IVoiceInteractionSession session) { - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - int numTaskContainers = display.getTaskDisplayAreaCount(); - for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - final int numStacks = display.getStackCount(); - for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx); - stack.finishVoiceTask(session); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + final int numStacks = taskDisplayArea.getStackCount(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx); + stack.finishVoiceTask(session); } - } + }); } /** @@ -3310,48 +3261,50 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } boolean allResumedActivitiesVisible() { - boolean foundResumed = false; - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - final ActivityRecord r = stack.getResumedActivity(); - if (r != null) { - if (!r.nowVisible) { - return false; + boolean[] foundResumed = {false}; + final boolean foundInvisibleResumedActivity = forAllTaskDisplayAreas( + taskDisplayArea -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + final ActivityRecord r = stack.getResumedActivity(); + if (r != null) { + if (!r.nowVisible) { + return true; + } + foundResumed[0] = true; } - foundResumed = true; } - } - } + return false; + }); + if (foundInvisibleResumedActivity) { + return false; } - return foundResumed; + return foundResumed[0]; } boolean allPausedActivitiesComplete() { - boolean pausing = true; - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - final ActivityRecord r = stack.mPausingActivity; - if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) { - if (DEBUG_STATES) { - Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r - + " state=" + r.getState()); - pausing = false; - } else { - return false; + boolean[] pausing = {true}; + final boolean hasActivityNotCompleted = forAllTaskDisplayAreas( + taskDisplayArea -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + final ActivityRecord r = stack.mPausingActivity; + if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) { + if (DEBUG_STATES) { + Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r + + " state=" + r.getState()); + pausing[0] = false; + } else { + return true; + } } } - } - } + return false; + }); + if (hasActivityNotCompleted) { + return false; } - return pausing; + return pausing[0]; } /** @@ -3381,8 +3334,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * <li>The top activity explicitly belongs to {@param userId}.</li> * <li>The top activity returns a result to an activity belonging to {@param userId}.</li> * </ul> - * - * @return {@code true} if the top activity looks like it belongs to {@param userId}. */ private void taskTopActivityIsUser(Task task, @UserIdInt int userId) { // To handle the case that work app is in the task but just is not the top one. @@ -3400,15 +3351,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } void cancelInitializingActivities() { - for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - taskDisplayArea.getStackAt(sNdx).cancelInitializingActivities(); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + // We don't want to clear starting window for activities that aren't occluded + // as we need to display their starting window until they are done initializing. + taskDisplayArea.getStackAt(sNdx).forAllOccludedActivities( + ActivityRecord::cancelInitializing); } - } + }); } Task anyTaskForId(int id) { @@ -3511,24 +3461,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } else { // Set power mode when the activity's process is different than the current top resumed // activity on all display areas, or if there are no resumed activities in the system. - boolean noResumedActivities = true; - boolean allFocusedProcessesDiffer = true; - for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) { - final DisplayContent dc = getChildAt(displayNdx); - for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx); - final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity(); - final WindowProcessController resumedActivityProcess = - resumedActivity == null ? null : resumedActivity.app; - - noResumedActivities &= resumedActivityProcess == null; - if (resumedActivityProcess != null) { - allFocusedProcessesDiffer &= !resumedActivityProcess.equals( - targetActivity.app); - } + boolean[] noResumedActivities = {true}; + boolean[] allFocusedProcessesDiffer = {true}; + forAllTaskDisplayAreas(taskDisplayArea -> { + final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity(); + final WindowProcessController resumedActivityProcess = + resumedActivity == null ? null : resumedActivity.app; + + noResumedActivities[0] &= resumedActivityProcess == null; + if (resumedActivityProcess != null) { + allFocusedProcessesDiffer[0] &= + !resumedActivityProcess.equals(targetActivity.app); } - } - sendPowerModeLaunch = noResumedActivities || allFocusedProcessesDiffer; + }); + sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0]; } if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) { @@ -3569,19 +3515,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } else { ArrayList<ActivityRecord> activities = new ArrayList<>(); - int numDisplays = getChildCount(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { - activities.addAll(stack.getDumpActivitiesLocked(name)); - } + forAllTaskDisplayAreas(taskDisplayArea -> { + for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); + if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { + activities.addAll(stack.getDumpActivitiesLocked(name)); } } - } + }); return activities; } } @@ -3614,46 +3555,43 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, String dumpPackage) { - boolean printed = false; - boolean needSep = false; + boolean[] printed = {false}; + boolean[] needSep = {false}; for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { DisplayContent displayContent = getChildAt(displayNdx); - if (printed) { + if (printed[0]) { pw.println(); } pw.print("Display #"); pw.print(displayContent.mDisplayId); pw.println(" (activities from top to bottom):"); - for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx); + displayContent.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); - if (needSep) { + if (needSep[0]) { pw.println(); } - needSep = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false); - printed |= needSep; + needSep[0] = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false); + printed[0] |= needSep[0]; } - } - for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx); - printed |= printThisActivity(pw, taskDisplayArea.getFocusedActivity(), - dumpPackage, needSep, " Resumed: ", () -> { - pw.println(" Resumed activities in task display areas" - + " (from top to bottom):"); - }); - } + }); + displayContent.forAllTaskDisplayAreas(taskDisplayArea -> { + printed[0] |= printThisActivity(pw, taskDisplayArea.getFocusedActivity(), + dumpPackage, needSep[0], " Resumed: ", () -> + pw.println(" Resumed activities in task display areas" + + " (from top to bottom):")); + }); } - printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ", + printed[0] |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ", "Fin", false, !dumpAll, false, dumpPackage, true, - () -> { pw.println(" Activities waiting to finish:"); }, null); - printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ", + () -> pw.println(" Activities waiting to finish:"), null); + printed[0] |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ", "Stop", false, !dumpAll, false, dumpPackage, true, - () -> { pw.println(" Activities waiting to stop:"); }, null); + () -> pw.println(" Activities waiting to stop:"), null); - return printed; + return printed[0]; } private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index bcd71c9ba74e..970520aff81f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3175,6 +3175,33 @@ class Task extends WindowContainer<WindowContainer> { return false; } + /** Returns the top-most activity that occludes the given one, or {@code null} if none. */ + @Nullable + ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { + final ActivityRecord top = getActivity(ActivityRecord::occludesParent, + true /* traverseTopToBottom */, activity); + return top != activity ? top : null; + } + + /** Iterates through all occluded activities. */ + void forAllOccludedActivities(Consumer<ActivityRecord> handleOccludedActivity) { + if (!shouldBeVisible(null /* starting */)) { + // The stack is invisible so all activities are occluded. + forAllActivities(handleOccludedActivity); + return; + } + final ActivityRecord topOccluding = getOccludingActivityAbove(null); + if (topOccluding == null) { + // No activities are occluded. + return; + } + // Invoke the callback on the activities behind the top occluding activity. + forAllActivities(r -> { + handleOccludedActivity.accept(r); + return false; + }, topOccluding, false /* includeBoundary */, true /* traverseTopToBottom */); + } + @Override public SurfaceControl.Builder makeAnimationLeash() { return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 24cf751015c2..8bab106e2f54 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -66,6 +66,9 @@ import com.android.server.protolog.common.ProtoLog; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; /** * {@link DisplayArea} that represents a section of a screen that contains app window containers. @@ -375,6 +378,31 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded(); } + @Override + boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback, + boolean traverseTopToBottom) { + return callback.apply(this); + } + + @Override + void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) { + callback.accept(this); + } + + @Nullable + @Override + <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, + @Nullable R initValue, boolean traverseTopToBottom) { + return accumulator.apply(this, initValue); + } + + @Nullable + @Override + <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, + boolean traverseTopToBottom) { + return callback.apply(this); + } + /** * Assigns a priority number to stack types. This priority defines an order between the types * of stacks that are added to the task display area. diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 11c20b6d9133..00ddf82d2ba3 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -741,9 +741,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { private void adjustBoundsToAvoidConflictInDisplay(@NonNull DisplayContent display, @NonNull Rect inOutBounds) { final List<Rect> taskBoundsToCheck = new ArrayList<>(); - int numTaskContainers = display.getTaskDisplayAreaCount(); - for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); + display.forAllTaskDisplayAreas(taskDisplayArea -> { int numStacks = taskDisplayArea.getStackCount(); for (int sNdx = 0; sNdx < numStacks; ++sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); @@ -755,7 +753,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { taskBoundsToCheck.add(stack.getChildAt(j).getBounds()); } } - } + }, false /* traverseTopToBottom */); adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index f70bf18cdea5..04d134c3649d 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -603,8 +603,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { throw new IllegalArgumentException("Display " + displayId + " doesn't exist"); } ArrayList<RunningTaskInfo> out = new ArrayList<>(); - for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx); + dc.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final Task task = taskDisplayArea.getStackAt(sNdx); if (activityTypes != null @@ -613,7 +612,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } out.add(task.getTaskInfo()); } - } + }); return out; } } finally { diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 132d00ac91c1..c27d0cda22ab 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; @@ -24,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIG import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.Nullable; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -44,7 +46,13 @@ class WallpaperWindowToken extends WindowToken { WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens) { - super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens); + this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */); + } + + WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, + DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) { + super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID, + false /* roundedCornerOverlay */, false /* fromClientToken */, options); dc.mWallpaperController.addWallpaperToken(this); setWindowingMode(WINDOWING_MODE_FULLSCREEN); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 4de77724bc41..7b92c283ddf2 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -95,6 +95,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -1731,6 +1732,145 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** + * For all {@link TaskDisplayArea} at or below this container call the callback. + * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it + * returns {@code true}. + * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in + * terms of z-order, else from bottom-to-top. + * @return {@code true} if the search ended before we reached the end of the hierarchy due to + * callback returning {@code true}. + */ + boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback, + boolean traverseTopToBottom) { + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + while (i >= 0 && i < childCount) { + if (mChildren.get(i).forAllTaskDisplayAreas(callback, traverseTopToBottom)) { + return true; + } + i += traverseTopToBottom ? -1 : 1; + } + return false; + } + + /** + * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from + * top to bottom in terms of z-order. + * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it + * returns {@code true}. + * @return {@code true} if the search ended before we reached the end of the hierarchy due to + * callback returning {@code true}. + */ + boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback) { + return forAllTaskDisplayAreas(callback, true /* traverseTopToBottom */); + } + + /** + * For all {@link TaskDisplayArea} at or below this container call the callback. + * @param callback Applies on each {@link TaskDisplayArea} found. + * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in + * terms of z-order, else from bottom-to-top. + */ + void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) { + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + while (i >= 0 && i < childCount) { + mChildren.get(i).forAllTaskDisplayAreas(callback, traverseTopToBottom); + i += traverseTopToBottom ? -1 : 1; + } + } + + /** + * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from + * top to bottom in terms of z-order. + * @param callback Applies on each {@link TaskDisplayArea} found. + */ + void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback) { + forAllTaskDisplayAreas(callback, true /* traverseTopToBottom */); + } + + /** + * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the + * provided initial value and an accumulation function, and returns the reduced value. + * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result + * from the previous call. + * @param initValue The initial value to pass to the accumulating function with the first + * {@link TaskDisplayArea}. + * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in + * terms of z-order, else from bottom-to-top. + * @return the accumulative result. + */ + @Nullable + <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, + @Nullable R initValue, boolean traverseTopToBottom) { + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + R result = initValue; + while (i >= 0 && i < childCount) { + result = (R) mChildren.get(i) + .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom); + i += traverseTopToBottom ? -1 : 1; + } + return result; + } + + /** + * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the + * provided initial value and an accumulation function, and returns the reduced value. Traverses + * from top to bottom in terms of z-order. + * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result + * from the previous call. + * @param initValue The initial value to pass to the accumulating function with the first + * {@link TaskDisplayArea}. + * @return the accumulative result. + */ + @Nullable + <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, + @Nullable R initValue) { + return reduceOnAllTaskDisplayAreas(accumulator, initValue, true /* traverseTopToBottom */); + } + + /** + * Finds the first non {@code null} return value from calling the callback on all + * {@link TaskDisplayArea} at or below this container. + * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it + * returns non {@code null}. + * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in + * terms of z-order, else from bottom-to-top. + * @return the first returned object that is not {@code null}. Returns {@code null} if not + * found. + */ + @Nullable + <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, + boolean traverseTopToBottom) { + int childCount = mChildren.size(); + int i = traverseTopToBottom ? childCount - 1 : 0; + while (i >= 0 && i < childCount) { + R result = (R) mChildren.get(i) + .getItemFromTaskDisplayAreas(callback, traverseTopToBottom); + if (result != null) { + return result; + } + i += traverseTopToBottom ? -1 : 1; + } + return null; + } + + /** + * Finds the first non {@code null} return value from calling the callback on all + * {@link TaskDisplayArea} at or below this container. Traverses from top to bottom in terms of + * z-order. + * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it + * returns non {@code null}. + * @return the first returned object that is not {@code null}. Returns {@code null} if not + * found. + */ + @Nullable + <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback) { + return getItemFromTaskDisplayAreas(callback, true /* traverseTopToBottom */); + } + + /** * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than * the input container in terms of z-order. */ @@ -2656,6 +2796,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return null; } + /** Cheap way of doing cast and instanceof. */ + DisplayArea asDisplayArea() { + return null; + } + /** * @return {@code true} if window container is manage by a * {@link android.window.WindowOrganizer} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 481ad9e79182..85972bffd755 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2670,10 +2670,11 @@ public class WindowManagerService extends IWindowManager.Stub } // TODO(window-container): Clean up dead tokens if (type == TYPE_WALLPAPER) { - new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens); + new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens, + options); } else { new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, - callingUid, false /* roundedCornerOverlay */, fromClientToken); + callingUid, false /* roundedCornerOverlay */, fromClientToken, options); } } } finally { @@ -5840,27 +5841,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override - public void setForceShowSystemBars(boolean show) { - boolean isAutomotive = mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE); - if (!isAutomotive) { - throw new UnsupportedOperationException("Force showing system bars is only supported" - + "for Automotive use cases."); - } - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Caller does not hold permission " - + android.Manifest.permission.STATUS_BAR); - } - synchronized (mGlobalLock) { - final PooledConsumer c = PooledLambda.obtainConsumer( - DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show); - mRoot.forAllDisplayPolicies(c); - c.recycle(); - } - } - public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index bac921816e3f..86bc0a227ac1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2191,6 +2191,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isInputMethodTarget()) { dc.computeImeTarget(true /* updateImeTarget */); } + if (dc.mInputMethodControlTarget == this) { + dc.updateImeControlTarget(); + } final int type = mAttrs.type; if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 86aacf308068..2c1bb3ec51eb 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -40,10 +40,12 @@ import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW; import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.app.IWindowToken; import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Bundle; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; @@ -78,6 +80,13 @@ class WindowToken extends WindowContainer<WindowState> { // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; + /** + * Options that will be used to determine which {@link RootDisplayArea} this window should be + * attached to. + */ + @Nullable + final Bundle mOptions; + /** {@code true} if this holds the rounded corner overlay */ final boolean mRoundedCornerOverlay; @@ -233,9 +242,17 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, boolean roundedCornerOverlay, boolean fromClientToken) { + this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid, + roundedCornerOverlay, fromClientToken, null /* options */); + } + + WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, + DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, + boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) { super(service); token = _token; windowType = type; + mOptions = options; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; mOwnerUid = ownerUid; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 75ec22486021..7bd455ab82a1 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -194,7 +194,7 @@ protected: public: NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper); - inline sp<InputManager> getInputManager() const { return mInputManager; } + inline sp<InputManagerInterface> getInputManager() const { return mInputManager; } void dump(std::string& dump); @@ -225,7 +225,7 @@ public: /* --- InputReaderPolicyInterface implementation --- */ virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); - virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); + virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId); virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices); virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier); virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier); @@ -236,29 +236,26 @@ public: /* --- InputDispatcherPolicyInterface implementation --- */ - virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, - uint32_t policyFlags) override; - virtual void notifyConfigurationChanged(nsecs_t when); - virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle, - const sp<IBinder>& token, const std::string& reason) override; - virtual void notifyInputChannelBroken(const sp<IBinder>& token); - virtual void notifyFocusChanged(const sp<IBinder>& oldToken, - const sp<IBinder>& newToken) override; - virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override; - virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override; - virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, - uint32_t& policyFlags) override; - virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, - uint32_t& policyFlags) override; - virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, - const KeyEvent* keyEvent, - uint32_t policyFlags) override; - virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, - uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override; - virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override; - virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, - int32_t injectorUid) override; - virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override; + void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags) override; + void notifyConfigurationChanged(nsecs_t when) override; + std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<IBinder>& token, + const std::string& reason) override; + void notifyInputChannelBroken(const sp<IBinder>& token) override; + void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override; + bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override; + void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override; + void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override; + void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) override; + nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent* keyEvent, + uint32_t policyFlags) override; + bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, + uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override; + void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override; + bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override; + void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override; /* --- PointerControllerPolicyInterface implementation --- */ @@ -270,7 +267,7 @@ public: virtual int32_t getCustomPointerIconId(); private: - sp<InputManager> mInputManager; + sp<InputManagerInterface> mInputManager; jobject mServiceObj; sp<Looper> mLooper; @@ -299,7 +296,7 @@ private: sp<SpriteController> spriteController; // Pointer controller singleton, created and destroyed as needed. - wp<PointerController> pointerController; + std::weak_ptr<PointerController> pointerController; // Input devices to be disabled std::set<int32_t> disabledInputDevices; @@ -342,9 +339,9 @@ NativeInputManager::NativeInputManager(jobject contextObj, } mInteractive = true; - mInputManager = new InputManager(this, this); - defaultServiceManager()->addService(String16("inputflinger"), - mInputManager, false); + InputManager* im = new InputManager(this, this); + mInputManager = im; + defaultServiceManager()->addService(String16("inputflinger"), im); } NativeInputManager::~NativeInputManager() { @@ -544,15 +541,16 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon } // release lock } -sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) { +std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController( + int32_t /* deviceId */) { ATRACE_CALL(); AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller == nullptr) { ensureSpriteControllerLocked(); - controller = new PointerController(this, mLooper, mLocked.spriteController); + controller = PointerController::create(this, mLooper, mLocked.spriteController); mLocked.pointerController = controller; updateInactivityTimeoutLocked(); } @@ -694,8 +692,9 @@ static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env, return handle->getInputApplicationHandleObjLocalRef(env); } -nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle, - const sp<IBinder>& token, const std::string& reason) { +std::chrono::nanoseconds NativeInputManager::notifyAnr( + const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token, + const std::string& reason) { #if DEBUG_INPUT_DISPATCHER_POLICY ALOGD("notifyANR"); #endif @@ -718,7 +717,7 @@ nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApp } else { assert(newTimeout >= 0); } - return newTimeout; + return std::chrono::nanoseconds(newTimeout); } void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) { @@ -803,15 +802,14 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) { } void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) { - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller == nullptr) { return; } bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; - controller->setInactivityTimeout(lightsOut - ? PointerController::INACTIVITY_TIMEOUT_SHORT - : PointerController::INACTIVITY_TIMEOUT_NORMAL); + controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT + : PointerController::InactivityTimeout::NORMAL); } void NativeInputManager::setPointerSpeed(int32_t speed) { @@ -891,7 +889,7 @@ void NativeInputManager::reloadCalibration() { void NativeInputManager::setPointerIconType(int32_t iconId) { AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller != nullptr) { controller->updatePointerIcon(iconId); } @@ -899,7 +897,7 @@ void NativeInputManager::setPointerIconType(int32_t iconId) { void NativeInputManager::reloadPointerIcons() { AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller != nullptr) { controller->reloadPointerResources(); } @@ -907,7 +905,7 @@ void NativeInputManager::reloadPointerIcons() { void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) { AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller != nullptr) { controller->setCustomPointerIcon(icon); } @@ -1253,7 +1251,7 @@ int32_t NativeInputManager::getCustomPointerIconId() { } void NativeInputManager::setMotionClassifierEnabled(bool enabled) { - mInputManager->setMotionClassifierEnabled(enabled); + mInputManager->getClassifier()->setMotionClassifierEnabled(enabled); } // ---------------------------------------------------------------------------- diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java index 04a9cca7fa51..f40d3168cf98 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java @@ -41,8 +41,6 @@ import static org.mockito.MockitoAnnotations.initMocks; import android.app.AppOpsManager; import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; import android.location.util.identity.CallerIdentity; import android.platform.test.annotations.Presubmit; @@ -52,7 +50,6 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.util.ArrayList; @@ -183,11 +180,6 @@ public class SystemAppOpsHelperTest { eq(false), eq("myfeature"), nullable(String.class)); assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue(); - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).sendBroadcast(intentCaptor.capture()); - assertThat(intentCaptor.getValue().getAction()).isEqualTo( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); - doReturn(MODE_IGNORED).when( mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000), eq("mypackage"), @@ -209,11 +201,6 @@ public class SystemAppOpsHelperTest { mHelper.stopHighPowerLocationMonitoring(identity); verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature"); - - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).sendBroadcast(intentCaptor.capture()); - assertThat(intentCaptor.getValue().getAction()).isEqualTo( - LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); } @Test diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index ac2ec58fae2a..7fc6bbd70000 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -59,6 +59,7 @@ android_test { libs: [ "android.hardware.power-java", "android.hardware.tv.cec-V1.0-java", + "android.hardware.vibrator-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 6915220d3fb7..90e1cfcd305a 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -81,6 +81,9 @@ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"/> + <uses-permission android:name="android.permission.VIBRATE"/> + <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE"/> + <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java new file mode 100644 index 000000000000..c6922536f61a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -0,0 +1,607 @@ +/* + * 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 static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +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.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.intThat; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.PackageManagerInternal; +import android.hardware.vibrator.IVibrator; +import android.os.Handler; +import android.os.IBinder; +import android.os.IVibratorStateListener; +import android.os.Looper; +import android.os.PowerManager; +import android.os.PowerManagerInternal; +import android.os.PowerSaveState; +import android.os.Process; +import android.os.UserHandle; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; +import android.provider.Settings; +import android.test.mock.MockContentResolver; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.util.test.FakeSettingsProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; + +/** + * Tests for {@link VibratorService}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:VibratorServiceTest + */ +@Presubmit +public class VibratorServiceTest { + + private static final int UID = Process.ROOT_UID; + private static final String PACKAGE_NAME = "package"; + private static final VibrationAttributes ALARM_ATTRS = + new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build(); + private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS = + new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_TOUCH).build(); + private static final VibrationAttributes NOTIFICATION_ATTRS = + new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_NOTIFICATION).build(); + private static final VibrationAttributes RINGTONE_ATTRS = + new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_RINGTONE).build(); + + @Rule public MockitoRule rule = MockitoJUnit.rule(); + + @Mock private PackageManagerInternal mPackageManagerInternalMock; + @Mock private PowerManagerInternal mPowerManagerInternalMock; + @Mock private PowerSaveState mPowerSaveStateMock; + @Mock private Vibrator mVibratorMock; + @Mock private VibratorService.NativeWrapper mNativeWrapperMock; + @Mock private IVibratorStateListener mVibratorStateListenerMock; + @Mock private IBinder mVibratorStateListenerBinderMock; + + private TestLooper mTestLooper; + private ContextWrapper mContextSpy; + + @Before + public void setUp() throws Exception { + mTestLooper = new TestLooper(); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + + MockContentResolver contentResolver = new MockContentResolver(mContextSpy); + contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + + when(mContextSpy.getContentResolver()).thenReturn(contentResolver); + when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); + when(mVibratorMock.getDefaultHapticFeedbackIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibratorMock.getDefaultNotificationVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibratorMock.getDefaultRingVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock); + when(mPackageManagerInternalMock.getSystemUiServiceComponent()) + .thenReturn(new ComponentName("", "")); + when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION)) + .thenReturn(mPowerSaveStateMock); + + addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock); + addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock); + FakeSettingsProvider.clearSettingsProvider(); + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + FakeSettingsProvider.clearSettingsProvider(); + } + + private VibratorService createService() { + return new VibratorService(mContextSpy, + new VibratorService.Injector() { + @Override + VibratorService.NativeWrapper getNativeWrapper() { + return mNativeWrapperMock; + } + + @Override + Handler createHandler(Looper looper) { + return new Handler(mTestLooper.getLooper()); + } + + @Override + void addService(String name, IBinder service) { + // ignore + } + }); + } + + @Test + public void createService_initializesNativeService() { + createService(); + verify(mNativeWrapperMock).vibratorInit(); + verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void hasVibrator_withVibratorHalPresent_returnsTrue() { + when(mNativeWrapperMock.vibratorExists()).thenReturn(true); + assertTrue(createService().hasVibrator()); + } + + @Test + public void hasVibrator_withNoVibratorHalPresent_returnsFalse() { + when(mNativeWrapperMock.vibratorExists()).thenReturn(false); + assertFalse(createService().hasVibrator()); + } + + @Test + public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + assertTrue(createService().hasAmplitudeControl()); + } + + @Test + public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false); + assertFalse(createService().hasAmplitudeControl()); + } + + @Test + public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() { + when(mNativeWrapperMock.vibratorGetSupportedEffects()).thenReturn(null); + assertArrayEquals(new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN}, + createService().areEffectsSupported(new int[]{VibrationEffect.EFFECT_CLICK})); + } + + @Test + public void areEffectsSupported_withSomeEffectsSupported_returnsSupportYesAndNoForEffects() { + int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK}; + + when(mNativeWrapperMock.vibratorGetSupportedEffects()) + .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + assertArrayEquals( + new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_YES, + Vibrator.VIBRATION_EFFECT_SUPPORT_NO}, + createService().areEffectsSupported(effects)); + } + + @Test + public void arePrimitivesSupported_withoutComposeCapability_returnsAlwaysFalse() { + assertArrayEquals(new boolean[]{false, false}, + createService().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_TICK + })); + } + + @Test + public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + assertArrayEquals(new boolean[]{true, true}, + createService().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE + })); + } + + @Test + public void setAlwaysOnEffect_withCapabilityAndValidEffect_enablesAlwaysOnEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS)); + verify(mNativeWrapperMock).vibratorAlwaysOnEnable( + eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); + } + + @Test + public void setAlwaysOnEffect_withNonPrebakedEffect_ignoresEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, + VibrationEffect.createOneShot(100, 255), ALARM_ATTRS)); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong()); + } + + @Test + public void setAlwaysOnEffect_withNullEffect_disablesAlwaysOnEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + + assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS)); + verify(mNativeWrapperMock).vibratorAlwaysOnDisable(eq(1L)); + } + + @Test + public void setAlwaysOnEffect_withoutCapability_ignoresEffect() { + assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, + VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS)); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong()); + } + + @Test + public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createOneShot(100, 128)); + assertTrue(service.isVibrating()); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorOn(eq(100L)); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); + } + + @Test + public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude() { + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createOneShot(100, 128)); + assertTrue(service.isVibrating()); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorOn(eq(100L)); + verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); + } + + @Test + public void vibrate_withPrebaked_performsEffect() { + when(mNativeWrapperMock.vibratorGetSupportedEffects()) + .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), + any(VibratorService.Vibration.class), eq(false)); + } + + @Test + public void vibrate_withComposed_performsEffect() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) + .compose(); + vibrate(service, effect); + + ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = + ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + primitivesCaptor.capture(), any(VibratorService.Vibration.class)); + + // Check all primitive effect fields are passed down to the HAL. + assertEquals(1, primitivesCaptor.getValue().length); + VibrationEffect.Composition.PrimitiveEffect primitive = primitivesCaptor.getValue()[0]; + assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.id); + assertEquals(0.5f, primitive.scale, /* delta= */ 1e-2); + assertEquals(10, primitive.delay); + } + + @Test + public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime() + throws Exception { + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.createWaveform( + new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1); + vibrate(service, effect); + + verify(mNativeWrapperMock).vibratorOff(); + + Thread.sleep(5); + verify(mNativeWrapperMock).vibratorOn(eq(30L)); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); + + Thread.sleep(10); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200)); + + Thread.sleep(10); + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50)); + } + + @Test + public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); + return null; + }).when(mNativeWrapperMock).vibratorPerformComposedEffect( + any(), any(VibratorService.Vibration.class)); + + // Use vibration with delay so there is time for the callback to be triggered. + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) + .compose(); + vibrate(service, effect); + + // Vibration canceled once before perform and once by native callback. + verify(mNativeWrapperMock, times(2)).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + any(VibrationEffect.Composition.PrimitiveEffect[].class), + any(VibratorService.Vibration.class)); + } + + @Test + public void vibrate_whenBinderDies_cancelsVibration() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(1)).binderDied(); + return null; + }).when(mNativeWrapperMock).vibratorPerformComposedEffect( + any(), any(VibratorService.Vibration.class)); + + // Use vibration with delay so there is time for the callback to be triggered. + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) + .compose(); + vibrate(service, effect); + + // Vibration canceled once before perform and once by native binder death. + verify(mNativeWrapperMock, times(2)).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + any(VibrationEffect.Composition.PrimitiveEffect[].class), + any(VibratorService.Vibration.class)); + } + + @Test + public void cancelVibrate_withDeviceVibrating_callsVibratorOff() { + VibratorService service = createService(); + vibrate(service, VibrationEffect.createOneShot(100, 128)); + assertTrue(service.isVibrating()); + Mockito.clearInvocations(mNativeWrapperMock); + + service.cancelVibrate(service); + assertFalse(service.isVibrating()); + verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void cancelVibrate_withDeviceNotVibrating_ignoresCall() { + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + service.cancelVibrate(service); + assertFalse(service.isVibrating()); + verify(mNativeWrapperMock, never()).vibratorOff(); + } + + @Test + public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { + VibratorService service = createService(); + + service.registerVibratorStateListener(mVibratorStateListenerMock); + verify(mVibratorStateListenerMock).onVibrating(false); + + vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); + verify(mVibratorStateListenerMock).onVibrating(true); + + // Run the scheduled callback to finish one-shot vibration. + mTestLooper.moveTimeForward(10); + mTestLooper.dispatchAll(); + verify(mVibratorStateListenerMock, times(2)).onVibrating(false); + } + + @Test + public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception { + VibratorService service = createService(); + + service.registerVibratorStateListener(mVibratorStateListenerMock); + verify(mVibratorStateListenerMock).onVibrating(false); + + vibrate(service, VibrationEffect.createOneShot(5, VibrationEffect.DEFAULT_AMPLITUDE)); + verify(mVibratorStateListenerMock).onVibrating(true); + + service.unregisterVibratorStateListener(mVibratorStateListenerMock); + Mockito.clearInvocations(mVibratorStateListenerMock); + + vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); + verifyNoMoreInteractions(mVibratorStateListenerMock); + } + + @Test + public void scale_withPrebaked_userIntensitySettingAsEffectStrength() { + // Alarm vibration is always VIBRATION_INTENSITY_HIGH. + setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_MEDIUM); + setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_OFF); + VibratorService service = createService(); + service.systemReady(); + + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), + ALARM_ATTRS); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK), + NOTIFICATION_ATTRS); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK), + HAPTIC_FEEDBACK_ATTRS); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK), + RINGTONE_ATTRS); + + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(), anyBoolean()); + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_TICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any(), anyBoolean()); + verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any(), anyBoolean()); + verify(mNativeWrapperMock, never()).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any(), anyBoolean()); + } + + @Test + public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception { + when(mVibratorMock.getDefaultNotificationVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_OFF); + + when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + VibratorService service = createService(); + service.systemReady(); + + vibrate(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS); + vibrate(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS); + vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.createWaveform(new long[] { 10 }, new int[] { 100 }, -1), + HAPTIC_FEEDBACK_ATTRS); + + // Waveform effect runs on a separate thread. + Thread.sleep(5); + + // Alarm vibration is never scaled. + verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); + // Notification vibrations will be scaled with SCALE_VERY_HIGH. + verify(mNativeWrapperMock).vibratorSetAmplitude(intThat(amplitude -> amplitude > 150)); + // Haptic feedback vibrations will be scaled with SCALE_LOW. + verify(mNativeWrapperMock).vibratorSetAmplitude( + intThat(amplitude -> amplitude < 100 && amplitude > 50)); + // Ringtone vibration is off. + verify(mNativeWrapperMock, never()).vibratorSetAmplitude(eq(255)); + } + + @Test + public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() { + when(mVibratorMock.getDefaultNotificationVibrationIntensity()) + .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_OFF); + + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorService service = createService(); + service.systemReady(); + + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) + .compose(); + ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = + ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); + + vibrate(service, effect, ALARM_ATTRS); + vibrate(service, effect, NOTIFICATION_ATTRS); + vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS); + vibrate(service, effect, RINGTONE_ATTRS); + + // Ringtone vibration is off, so only the other 3 are propagated to native. + verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect( + primitivesCaptor.capture(), any()); + + List<VibrationEffect.Composition.PrimitiveEffect[]> values = + primitivesCaptor.getAllValues(); + + // Alarm vibration is never scaled. + assertEquals(1f, values.get(0)[0].scale, /* delta= */ 1e-2); + assertEquals(0.5f, values.get(0)[1].scale, /* delta= */ 1e-2); + + // Notification vibrations will be scaled with SCALE_VERY_HIGH. + assertEquals(1f, values.get(1)[0].scale, /* delta= */ 1e-2); + assertTrue(0.7 < values.get(1)[1].scale); + + // Haptic feedback vibrations will be scaled with SCALE_LOW. + assertTrue(0.5 < values.get(2)[0].scale); + assertTrue(0.5 > values.get(2)[1].scale); + } + + private void vibrate(VibratorService service, VibrationEffect effect) { + vibrate(service, effect, ALARM_ATTRS); + } + + private void vibrate(VibratorService service, VibrationEffect effect, + VibrationAttributes attributes) { + service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service); + } + + private void mockVibratorCapabilities(int capabilities) { + when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities); + } + + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private void setVibrationIntensityUserSetting(String settingName, int value) { + Settings.System.putIntForUser( + mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 0445bff8fd0d..5327bf778537 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -51,6 +51,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -271,6 +272,25 @@ public class AbstractAccessibilityServiceConnectionTest { } @Test + public void setServiceInfo_ChangePackageNames_updateSuccess() { + assertTrue(mServiceConnection.mPackageNames.isEmpty()); + + final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo(); + updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG, + new String[] {PACKAGE_NAME1, PACKAGE_NAME2}, + 1000); + + mServiceConnection.setServiceInfo(serviceInfo); + assertEquals(serviceInfo.packageNames.length, mServiceConnection.mPackageNames.size()); + assertTrue(mServiceConnection.mPackageNames.containsAll( + Arrays.asList(mServiceConnection.getServiceInfo().packageNames))); + + updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG, null, 1000); + mServiceConnection.setServiceInfo(serviceInfo); + assertTrue(mServiceConnection.mPackageNames.isEmpty()); + } + + @Test public void canReceiveEvents_hasEventType_returnTrue() { final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo(); updateServiceInfo(serviceInfo, diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index 09f946d4b107..e7eff00c472e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -102,13 +102,13 @@ public class ScanTests { @Before public void setupDefaultAbiBehavior() throws Exception { when(mMockPackageAbiHelper.derivePackageAbi( - any(AndroidPackage.class), anyBoolean(), nullable(String.class), anyBoolean())) + any(AndroidPackage.class), anyBoolean(), nullable(String.class))) .thenReturn(new Pair<>( new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"), new PackageAbiHelper.NativeLibraryPaths( "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2"))); - when(mMockPackageAbiHelper.getNativeLibraryPaths( - any(AndroidPackage.class), any(PackageSetting.class), any(File.class))) + when(mMockPackageAbiHelper.deriveNativeLibraryPaths( + any(AndroidPackage.class), anyBoolean(), any(File.class))) .thenReturn(new PackageAbiHelper.NativeLibraryPaths( "getRootDir", true, "getNativeDir", "getNativeDir2" )); diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 6d9b43a967c4..cd2c9230221c 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -252,6 +252,7 @@ public class RollbackUnitTest { verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(111)); verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(222)); verify(mMockDataHelper, never()).destroyApexDeSnapshots(anyInt()); + verify(mMockDataHelper, never()).destroyApexCeSnapshots(anyInt(), anyInt()); assertThat(rollback.isDeleted()).isTrue(); } @@ -273,6 +274,8 @@ public class RollbackUnitTest { verify(mMockDataHelper, never()) .destroyAppDataSnapshot(anyInt(), pkgRollbackInfoFor(PKG_2), anyInt()); verify(mMockDataHelper).destroyApexDeSnapshots(123); + verify(mMockDataHelper).destroyApexCeSnapshots(111, 123); + verify(mMockDataHelper).destroyApexCeSnapshots(222, 123); assertThat(rollback.isDeleted()).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index f0531f5166b9..9f59763cfa58 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.HandlerThread; import android.os.TimestampedValue; +import android.util.IndentingPrintWriter; import androidx.test.runner.AndroidJUnit4; @@ -46,6 +47,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.PrintWriter; +import java.io.StringWriter; @RunWith(AndroidJUnit4.class) public class TimeDetectorServiceTest { @@ -177,7 +179,8 @@ public class TimeDetectorServiceTest { when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)) .thenReturn(PackageManager.PERMISSION_GRANTED); - mTimeDetectorService.dump(null, null, null); + PrintWriter pw = new PrintWriter(new StringWriter()); + mTimeDetectorService.dump(null, pw, null); verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP)); mStubbedTimeDetectorStrategy.verifyDumpCalled(); @@ -251,7 +254,7 @@ public class TimeDetectorServiceTest { } @Override - public void dump(PrintWriter pw, String[] args) { + public void dump(IndentingPrintWriter pw, String[] args) { mDumpCalled = true; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/StubbedTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index 43a6b8f3e314..dcf319058ca2 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/StubbedTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -25,10 +25,9 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; +import android.util.IndentingPrintWriter; -import java.io.PrintWriter; - -class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { +class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { private StrategyListener mListener; @@ -110,7 +109,7 @@ class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { } @Override - public void dump(PrintWriter pw, String[] args) { + public void dump(IndentingPrintWriter pw, String[] args) { mDumpCalled = true; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java index 40ea3982df06..0e2c22756097 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java @@ -37,7 +37,7 @@ public class TimeZoneDetectorInternalImplTest { private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId"); private Context mMockContext; - private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy; + private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy; private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal; private HandlerThread mHandlerThread; @@ -53,10 +53,10 @@ public class TimeZoneDetectorInternalImplTest { mHandlerThread.start(); mTestHandler = new TestHandler(mHandlerThread.getLooper()); - mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy(); + mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy(); mTimeZoneDetectorInternal = new TimeZoneDetectorInternalImpl( - mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy); + mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy); } @After @@ -72,7 +72,7 @@ public class TimeZoneDetectorInternalImplTest { mTestHandler.assertTotalMessagesEnqueued(1); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion); + mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion); } private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() { diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index aa517ac0eea7..971d2e28b14e 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -46,13 +46,16 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.PrintWriter; +import java.io.StringWriter; + @RunWith(AndroidJUnit4.class) public class TimeZoneDetectorServiceTest { private static final int ARBITRARY_USER_ID = 9999; private Context mMockContext; - private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy; + private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy; private TimeZoneDetectorService mTimeZoneDetectorService; private HandlerThread mHandlerThread; @@ -68,10 +71,10 @@ public class TimeZoneDetectorServiceTest { mHandlerThread.start(); mTestHandler = new TestHandler(mHandlerThread.getLooper()); - mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy(); + mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy(); mTimeZoneDetectorService = new TimeZoneDetectorService( - mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy); + mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy); } @After @@ -100,7 +103,7 @@ public class TimeZoneDetectorServiceTest { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); TimeZoneCapabilities capabilities = createTimeZoneCapabilities(); - mStubbedTimeZoneDetectorStrategy.initializeCapabilities(capabilities); + mFakeTimeZoneDetectorStrategy.initializeCapabilities(capabilities); assertEquals(capabilities, mTimeZoneDetectorService.getCapabilities()); @@ -130,7 +133,7 @@ public class TimeZoneDetectorServiceTest { TimeZoneConfiguration configuration = createTimeZoneConfiguration(false /* autoDetectionEnabled */); - mStubbedTimeZoneDetectorStrategy.initializeConfiguration(configuration); + mFakeTimeZoneDetectorStrategy.initializeConfiguration(configuration); assertEquals(configuration, mTimeZoneDetectorService.getConfiguration()); @@ -161,7 +164,8 @@ public class TimeZoneDetectorServiceTest { TimeZoneConfiguration autoDetectDisabledConfiguration = createTimeZoneConfiguration(false /* autoDetectionEnabled */); - mStubbedTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration); + + mFakeTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration); IBinder mockListenerBinder = mock(IBinder.class); ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); @@ -177,7 +181,7 @@ public class TimeZoneDetectorServiceTest { // Simulate the configuration being changed and verify the mockListener was notified. TimeZoneConfiguration autoDetectEnabledConfiguration = createTimeZoneConfiguration(true /* autoDetectionEnabled */); - mStubbedTimeZoneDetectorStrategy.updateConfiguration( + mFakeTimeZoneDetectorStrategy.updateConfiguration( ARBITRARY_USER_ID, autoDetectEnabledConfiguration); verify(mockListener).onChange(autoDetectEnabledConfiguration); @@ -209,7 +213,7 @@ public class TimeZoneDetectorServiceTest { assertEquals(expectedResult, mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion)); - mStubbedTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion); + mFakeTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion); verify(mMockContext).enforceCallingOrSelfPermission( eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), @@ -261,7 +265,7 @@ public class TimeZoneDetectorServiceTest { anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion); + mFakeTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion); } @Test @@ -269,25 +273,26 @@ public class TimeZoneDetectorServiceTest { when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)) .thenReturn(PackageManager.PERMISSION_GRANTED); - mTimeZoneDetectorService.dump(null, null, null); + PrintWriter pw = new PrintWriter(new StringWriter()); + mTimeZoneDetectorService.dump(null, pw, null); verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP)); - mStubbedTimeZoneDetectorStrategy.verifyDumpCalled(); + mFakeTimeZoneDetectorStrategy.verifyDumpCalled(); } @Test public void testAutoTimeZoneDetectionChanged() throws Exception { - mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged(); + mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged(); mTestHandler.assertTotalMessagesEnqueued(1); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled(); + mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled(); - mStubbedTimeZoneDetectorStrategy.resetCallTracking(); + mFakeTimeZoneDetectorStrategy.resetCallTracking(); - mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged(); + mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged(); mTestHandler.assertTotalMessagesEnqueued(2); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled(); + mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled(); } private static TimeZoneConfiguration createTimeZoneConfiguration( diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 00a7caaa7254..68554451e43a 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -48,13 +48,13 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; +import android.util.IndentingPrintWriter; import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion; import org.junit.Before; import org.junit.Test; -import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; @@ -119,7 +119,8 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testGetCapabilities() { new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID)); } @@ -127,17 +128,19 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testGetConfiguration() { new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID); assertTrue(expectedConfiguration.isComplete()); assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID)); } @Test - public void testCapabilitiesTestInfra_owner() { + public void testCapabilitiesTestInfra_unrestricted() { Script script = new Script(); - script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); @@ -145,7 +148,8 @@ public class TimeZoneDetectorStrategyImplTest { assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone()); } - script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + script.initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); @@ -155,10 +159,11 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testCapabilitiesTestInfra_nonOwner() { + public void testCapabilitiesTestInfra_restricted() { Script script = new Script(); - script.initializeUser(USER_ID, UserCase.NON_OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.initializeUser(USER_ID, UserCase.RESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); @@ -166,7 +171,7 @@ public class TimeZoneDetectorStrategyImplTest { assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); } - script.initializeUser(USER_ID, UserCase.NON_OWNER, + script.initializeUser(USER_ID, UserCase.RESTRICTED, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); { // Check the fake test infra is doing what is expected. @@ -177,10 +182,10 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testCapabilitiesTestInfra_ownerAutoDetectNotSupported() { + public void testCapabilitiesTestInfra_autoDetectNotSupported() { Script script = new Script(); - script.initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED, + script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); { // Check the fake test infra is doing what is expected. @@ -189,7 +194,7 @@ public class TimeZoneDetectorStrategyImplTest { assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); } - script.initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED, + script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); { // Check the fake test infra is doing what is expected. @@ -200,9 +205,10 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testUpdateConfiguration_owner() { + public void testUpdateConfiguration_unrestricted() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); // Set the configuration with auto detection enabled. script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); @@ -225,9 +231,9 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testUpdateConfiguration_nonOwner() { + public void testUpdateConfiguration_restricted() { Script script = new Script() - .initializeUser(USER_ID, UserCase.NON_OWNER, + .initializeUser(USER_ID, UserCase.RESTRICTED, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); // Try to update the configuration with auto detection disabled. @@ -244,9 +250,9 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testUpdateConfiguration_ownerAutoDetectNotSupported() { + public void testUpdateConfiguration_autoDetectNotSupported() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED, + .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); // Try to update the configuration with auto detection disabled. @@ -269,7 +275,8 @@ public class TimeZoneDetectorStrategyImplTest { TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion = createEmptySlotIndex2Suggestion(); Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion) @@ -311,7 +318,8 @@ public class TimeZoneDetectorStrategyImplTest { QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH); Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); // A low quality suggestions will not be taken: The device time zone setting is left // uninitialized. @@ -376,7 +384,8 @@ public class TimeZoneDetectorStrategyImplTest { for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) { // Start with the device in a known state. - script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + script.initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); TelephonyTimeZoneSuggestion suggestion = @@ -427,7 +436,8 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testTelephonySuggestionsSingleSlotId() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) { @@ -493,7 +503,8 @@ public class TimeZoneDetectorStrategyImplTest { TELEPHONY_SCORE_NONE); Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID) // Initialize the latest suggestions as empty so we don't need to worry about nulls // below for the first loop. @@ -579,7 +590,8 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testTelephonySuggestionTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); TelephonyTestCase testCase = newTelephonyTestCase( MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH); @@ -613,9 +625,10 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_owner_simulateAutoTimeZoneEnabled() { + public void testManualSuggestion_unrestricted_simulateAutoTimeZoneEnabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is enabled so the manual suggestion should be ignored. @@ -625,9 +638,9 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_nonOwner_simulateAutoTimeZoneEnabled() { + public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.NON_OWNER, + .initializeUser(USER_ID, UserCase.RESTRICTED, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); @@ -638,9 +651,9 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_ownerAutoDetectNotSupported_simulateAutoTimeZoneEnabled() { + public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED, + .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); @@ -652,9 +665,10 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_owner_autoTimeZoneDetectionDisabled() { + public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is disabled so the manual suggestion should be used. @@ -665,13 +679,13 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_nonOwner_autoTimeZoneDetectionDisabled() { + public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.NON_OWNER, + .initializeUser(USER_ID, UserCase.RESTRICTED, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - // Only owners have the capability. + // Restricted users do not have the capability. ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); script.simulateManualTimeZoneSuggestion( USER_ID, manualSuggestion, false /* expectedResult */) @@ -679,13 +693,13 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_ownerAutoDetectNotSupported_autoTimeZoneDetectionDisabled() { + public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED, + .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - // Only owners have the capability. + // Unrestricted users have the capability. ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); script.simulateManualTimeZoneSuggestion( USER_ID, manualSuggestion, true /* expectedResult */) @@ -695,22 +709,22 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testAddDumpable() { new Script() - .initializeUser(USER_ID, UserCase.OWNER, + .initializeUser(USER_ID, UserCase.UNRESTRICTED, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); AtomicBoolean dumpCalled = new AtomicBoolean(false); class FakeDumpable implements Dumpable { @Override - public void dump(PrintWriter pw, String[] args) { + public void dump(IndentingPrintWriter pw, String[] args) { dumpCalled.set(true); } } mTimeZoneDetectorStrategy.addDumpable(new FakeDumpable()); - PrintWriter pw = new PrintWriter(new StringWriter()); + IndentingPrintWriter ipw = new IndentingPrintWriter(new StringWriter()); String[] args = {"ArgOne", "ArgTwo"}; - mTimeZoneDetectorStrategy.dump(pw, args); + mTimeZoneDetectorStrategy.dump(ipw, args); assertTrue(dumpCalled.get()); } @@ -912,12 +926,15 @@ public class TimeZoneDetectorStrategyImplTest { /** Simulated user test cases. */ enum UserCase { - /** A catch-all for users that can set time zone config. */ - OWNER, - /** A catch-all for users that can't set time zone config. */ - NON_OWNER, - /** Owner, but auto tz detection is not supported on the device. */ - OWNER_AUTO_DETECT_NOT_SUPPORTED, + /** A catch-all for users that can set auto time zone config. */ + UNRESTRICTED, + /** A catch-all for users that can't set auto time zone config. */ + RESTRICTED, + /** + * Like {@link #UNRESTRICTED}, but auto tz detection is not + * supported on the device. + */ + AUTO_DETECT_NOT_SUPPORTED, } /** @@ -927,7 +944,7 @@ public class TimeZoneDetectorStrategyImplTest { private static TimeZoneCapabilities createCapabilities( int userId, UserCase userRole, TimeZoneConfiguration configuration) { switch (userRole) { - case OWNER: { + case UNRESTRICTED: { int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled() ? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED; return new TimeZoneCapabilities.Builder(userId) @@ -935,14 +952,14 @@ public class TimeZoneDetectorStrategyImplTest { .setSuggestManualTimeZone(suggestManualTimeZoneCapability) .build(); } - case NON_OWNER: { + case RESTRICTED: { return new TimeZoneCapabilities.Builder(userId) .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED) .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED) .build(); } - case OWNER_AUTO_DETECT_NOT_SUPPORTED: { + case AUTO_DETECT_NOT_SUPPORTED: { return new TimeZoneCapabilities.Builder(userId) .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 1b42a0466cf7..373eed921580 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1331,42 +1331,40 @@ public class ActivityStackTests extends ActivityTestsBase { } @Test - public void testCheckBehindFullscreenActivity() { + public void testIterateOccludedActivity() { final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>(); - final Consumer<ActivityRecord> handleBehindFullscreenActivity = occludedActivities::add; + final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add; final ActivityRecord bottomActivity = new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); final ActivityRecord topActivity = new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + // Top activity occludes bottom activity. doReturn(true).when(mStack).shouldBeVisible(any()); - assertTrue(mStack.checkBehindFullscreenActivity(bottomActivity, - null /* handleBehindFullscreenActivity */)); - assertFalse(mStack.checkBehindFullscreenActivity(topActivity, - null /* handleBehindFullscreenActivity */)); + assertTrue(topActivity.shouldBeVisible()); + assertFalse(bottomActivity.shouldBeVisible()); - // Top activity occludes bottom activity. - mStack.checkBehindFullscreenActivity(null /* toCheck */, handleBehindFullscreenActivity); + mStack.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).containsExactly(bottomActivity); + // Top activity doesn't occlude parent, so the bottom activity is not occluded. doReturn(false).when(topActivity).occludesParent(); - assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity, - null /* handleBehindFullscreenActivity */)); - assertFalse(mStack.checkBehindFullscreenActivity(topActivity, - null /* handleBehindFullscreenActivity */)); + assertTrue(bottomActivity.shouldBeVisible()); occludedActivities.clear(); - // Top activity doesn't occlude parent, so the bottom activity is not occluded. - mStack.checkBehindFullscreenActivity(null /* toCheck */, handleBehindFullscreenActivity); + mStack.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).isEmpty(); + // A finishing activity should not occlude other activities behind. final ActivityRecord finishingActivity = new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); finishingActivity.finishing = true; doCallRealMethod().when(finishingActivity).occludesParent(); - assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity, - null /* handleBehindFullscreenActivity */)); - assertFalse(mStack.checkBehindFullscreenActivity(topActivity, - null /* handleBehindFullscreenActivity */)); + assertTrue(topActivity.shouldBeVisible()); + assertTrue(bottomActivity.shouldBeVisible()); + + occludedActivities.clear(); + mStack.forAllOccludedActivities(handleOccludedActivity); + assertThat(occludedActivities).isEmpty(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 4a19684e5554..e3b1d6306a81 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -200,7 +200,8 @@ public class ActivityStarterTests extends ActivityTestsBase { ai.packageName = "com.android.test.package"; final WindowProcessController wpc = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP) - ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener); + ? null + : new WindowProcessController(service, ai, null, 0, -1, null, listener); doReturn(wpc).when(service).getProcessController(any()); final Intent intent = new Intent(); @@ -211,7 +212,7 @@ public class ActivityStarterTests extends ActivityTestsBase { IVoiceInteractionSession voiceSession = containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION) - ? mock(IVoiceInteractionSession.class) : null; + ? mock(IVoiceInteractionSession.class) : null; // Create source token final ActivityBuilder builder = new ActivityBuilder(service).setTask( @@ -489,13 +490,12 @@ public class ActivityStarterTests extends ActivityTestsBase { } private void assertNoTasks(DisplayContent display) { - for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx); + display.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); assertFalse(stack.hasChild()); } - } + }); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 1fefb0c481d1..9d0cd26bc040 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -591,15 +591,14 @@ class ActivityTestsBase extends SystemServiceTestsBase { mService.mTaskOrganizerController.setLaunchRoot(mDisplayId, mSecondary.mRemoteToken.toWindowContainerToken()); DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId); - for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx); + dc.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final ActivityStack stack = taskDisplayArea.getStackAt(sNdx); if (!WindowConfiguration.isSplitScreenWindowingMode(stack.getWindowingMode())) { stack.reparent(mSecondary, POSITION_BOTTOM); } } - } + }); } @Override public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 4ba08d7cd005..a8fc66da9bd0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -16,15 +16,19 @@ package com.android.server.wm; +import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; +import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; @@ -33,13 +37,18 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; import static java.util.stream.Collectors.toList; import android.content.res.Resources; +import android.os.Bundle; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import com.google.android.collect.Lists; + import org.hamcrest.CustomTypeSafeMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -74,6 +83,10 @@ public class DisplayAreaPolicyBuilderTest { private DisplayContent mDisplayContent; private TaskDisplayArea mDefaultTaskDisplayArea; private List<TaskDisplayArea> mTaskDisplayAreaList; + private RootDisplayArea mGroupRoot1; + private RootDisplayArea mGroupRoot2; + private TaskDisplayArea mTda1; + private TaskDisplayArea mTda2; @Before public void setup() { @@ -85,6 +98,10 @@ public class DisplayAreaPolicyBuilderTest { FEATURE_DEFAULT_TASK_CONTAINER); mTaskDisplayAreaList = new ArrayList<>(); mTaskDisplayAreaList.add(mDefaultTaskDisplayArea); + mGroupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", FEATURE_VENDOR_FIRST + 1); + mGroupRoot2 = new SurfacelessDisplayAreaRoot(mWms, "group2", FEATURE_VENDOR_FIRST + 2); + mTda1 = new TaskDisplayArea(mDisplayContent, mWms, "tda1", FEATURE_VENDOR_FIRST + 3); + mTda2 = new TaskDisplayArea(mDisplayContent, mWms, "tda2", FEATURE_VENDOR_FIRST + 4); } @Test @@ -191,6 +208,271 @@ public class DisplayAreaPolicyBuilderTest { } } + @Test + public void testBuilder_singleRoot_validateSettings() { + final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); + + // Root must be set. + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // IME must be set. + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // Default TaskDisplayArea must be set. + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_FIRST + 1)))); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // No exception + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + builder.build(mWms); + } + + @Test + public void testBuilder_displayAreaGroup_validateSettings() { + final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); + + // IME must be set to one of the roots. + builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder1.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); + + // Default TaskDisplayArea must be set. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_FIRST + 1)))); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + + // Each DisplayAreaGroup must have at least one TaskDisplayArea. + final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); + builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2)); + + assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); + + // No exception + final DisplayAreaPolicyBuilder builder4 = new DisplayAreaPolicyBuilder(); + builder4.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); + builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList( + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_FIRST + 1)))); + + builder4.build(mWms); + } + + @Test + public void testBuilder_displayAreaGroup_attachDisplayAreas() { + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2))) + .build(mWms); + + assertThat(mDefaultTaskDisplayArea.isDescendantOf(mRoot)).isTrue(); + assertThat(mGroupRoot1.isDescendantOf(mRoot)).isTrue(); + assertThat(mGroupRoot2.isDescendantOf(mRoot)).isTrue(); + assertThat(mImeContainer.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(mTda1.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(mTda2.isDescendantOf(mGroupRoot2)).isTrue(); + assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot1)).isTrue(); + assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot2)).isTrue(); + } + + @Test + public void testBuilder_displayAreaGroup_createFeatureOnGroup() { + final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1", + FEATURE_VENDOR_FIRST + 5) + .all() + .except(TYPE_STATUS_BAR) + .build(); + final Feature feature2 = new Feature.Builder(mWms.mPolicy, "feature2", + FEATURE_VENDOR_FIRST + 6) + .upTo(TYPE_STATUS_BAR) + .and(TYPE_NAVIGATION_BAR) + .build(); + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)) + .addFeature(feature1)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2)) + .addFeature(feature2)) + .build(mWms); + + List<DisplayArea<? extends WindowContainer>> feature1DAs = + policy.getDisplayAreas(feature1.getId()); + List<DisplayArea<? extends WindowContainer>> feature2DAs = + policy.getDisplayAreas(feature2.getId()); + for (DisplayArea<? extends WindowContainer> da : feature1DAs) { + assertThat(da.isDescendantOf(mGroupRoot1)).isTrue(); + } + for (DisplayArea<? extends WindowContainer> da : feature2DAs) { + assertThat(da.isDescendantOf(mGroupRoot2)).isTrue(); + } + } + + @Test + public void testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc() { + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))) + .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2))) + .build(mWms); + + final WindowToken token = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + policy.addWindow(token); + + // By default, window are always added to the root. + assertThat(token.isDescendantOf(mRoot)).isTrue(); + assertThat(token.isDescendantOf(mGroupRoot1)).isFalse(); + assertThat(token.isDescendantOf(mGroupRoot2)).isFalse(); + } + + @Test + public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType() { + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 = + new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)); + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 = + new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2)); + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList)) + .addDisplayAreaGroupHierarchy(hierarchy1) + .addDisplayAreaGroupHierarchy(hierarchy2) + .setSelectRootForWindowFunc((token, options) -> { + if (token.windowType == TYPE_STATUS_BAR) { + return mGroupRoot1; + } + return mGroupRoot2; + }) + .build(mWms); + + final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), + TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + policy.addWindow(token1); + policy.addWindow(token2); + + assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); + } + + @Test + public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions() { + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 = + new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setTaskDisplayAreas(mTaskDisplayAreaList); + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 = + new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)); + final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 = + new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda2)); + final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .setRootHierarchy(hierarchy0) + .addDisplayAreaGroupHierarchy(hierarchy1) + .addDisplayAreaGroupHierarchy(hierarchy2) + .setSelectRootForWindowFunc((token, options) -> { + if (options == null) { + return mRoot; + } + if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot1.mFeatureId) { + return mGroupRoot1; + } + if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot2.mFeatureId) { + return mGroupRoot2; + } + return mRoot; + }) + .build(mWms); + + final Bundle options1 = new Bundle(); + options1.putInt("HIERARCHY_ROOT_ID", mGroupRoot1.mFeatureId); + final Bundle options2 = new Bundle(); + options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId); + final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, options1); + final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + false /* fromClientToken */, options2); + + policy.addWindow(token0); + policy.addWindow(token1); + policy.addWindow(token2); + + assertThat(token0.isDescendantOf(mRoot)).isTrue(); + assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue(); + assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); + } + private static Resources resourcesWithProvider(String provider) { Resources mock = mock(Resources.class); when(mock.getString( @@ -260,6 +542,10 @@ public class DisplayAreaPolicyBuilderTest { }; } + private boolean isSibling(WindowContainer da1, WindowContainer da2) { + return da1.getParent() != null && da1.getParent() == da2.getParent(); + } + private WindowToken tokenOfType(int type) { WindowToken m = mock(WindowToken.class); when(m.getWindowLayerFromType()).thenReturn( @@ -290,10 +576,14 @@ public class DisplayAreaPolicyBuilderTest { } } - private static class SurfacelessDisplayAreaRoot extends RootDisplayArea { + static class SurfacelessDisplayAreaRoot extends RootDisplayArea { SurfacelessDisplayAreaRoot(WindowManagerService wms) { - super(wms); + this(wms, "SurfacelessDisplayAreaRoot", FEATURE_ROOT); + } + + SurfacelessDisplayAreaRoot(WindowManagerService wms, String name, int featureId) { + super(wms, name, featureId); } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 880c486c15af..c8ed87db9d3e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -19,47 +19,74 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayArea.Type.ANY; import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS; import static com.android.server.wm.DisplayArea.Type.checkChild; import static com.android.server.wm.DisplayArea.Type.checkSiblings; import static com.android.server.wm.DisplayArea.Type.typeOf; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.testing.Assert.assertThrows; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import android.os.Binder; import android.platform.test.annotations.Presubmit; -import android.view.SurfaceControl; +import com.google.android.collect.Lists; + +import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Tests for the {@link DisplayArea} container. + * + * Build/Install/Run: + * atest WmTests:DisplayAreaTest + */ @Presubmit public class DisplayAreaTest { @Rule public SystemServicesTestRule mWmsRule = new SystemServicesTestRule(); + private WindowManagerService mWms; + + @Before + public void setup() { + mWms = mWmsRule.getWindowManagerService(); + } + @Test public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() { - WindowManagerService wms = mWmsRule.getWindowManagerService(); - DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent"); - DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child"); + DisplayArea<WindowContainer> parent = new DisplayArea<>(mWms, BELOW_TASKS, "Parent"); + DisplayArea<WindowContainer> child = new DisplayArea<>(mWms, ANY, "Child"); assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0)); } @Test public void testType_typeOf() { - WindowManagerService wms = mWmsRule.getWindowManagerService(); - - assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test"))); - assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test"))); - assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test"))); + assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(mWms, ABOVE_TASKS, "test"))); + assertEquals(ANY, typeOf(new DisplayArea<>(mWms, ANY, "test"))); + assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(mWms, BELOW_TASKS, "test"))); assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY))); assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION))); @@ -97,21 +124,264 @@ public class DisplayAreaTest { assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY)); } - private WindowToken createWindowToken(int type) { - return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(), - type, false /* persist */, null /* displayContent */, - false /* canManageTokens */); + @Test + public void testAsDisplayArea() { + final WindowContainer windowContainer = new WindowContainer(mWms); + final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(mWms, ANY, "DA"); + final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA", FEATURE_DEFAULT_TASK_CONTAINER); + + assertThat(windowContainer.asDisplayArea()).isNull(); + assertThat(displayArea.asDisplayArea()).isEqualTo(displayArea); + assertThat(taskDisplayArea.asDisplayArea()).isEqualTo(taskDisplayArea); } - private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> { + @Test + public void testForAllTaskDisplayAreas_onlyTraversesDisplayAreaOfTypeAny() { + final RootDisplayArea root = + new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms); + final Function<TaskDisplayArea, Boolean> callback0 = tda -> false; + final Consumer<TaskDisplayArea> callback1 = tda -> { }; + final BiFunction<TaskDisplayArea, Integer, Integer> callback2 = (tda, result) -> result; + final Function<TaskDisplayArea, TaskDisplayArea> callback3 = tda -> null; + + // Don't traverse the child if the current DA has type BELOW_TASKS + final DisplayArea<WindowContainer> da1 = new DisplayArea<>(mWms, BELOW_TASKS, "DA1"); + final DisplayArea<WindowContainer> da2 = new DisplayArea<>(mWms, BELOW_TASKS, "DA2"); + root.addChild(da1, POSITION_BOTTOM); + da1.addChild(da2, POSITION_TOP); + spyOn(da2); + + da1.forAllTaskDisplayAreas(callback0); + da1.forAllTaskDisplayAreas(callback1); + da1.reduceOnAllTaskDisplayAreas(callback2, 0); + da1.getItemFromTaskDisplayAreas(callback3); + + verifyZeroInteractions(da2); + + // Traverse the child if the current DA has type ANY + final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWms, ANY, "DA3"); + final DisplayArea<WindowContainer> da4 = new DisplayArea<>(mWms, ANY, "DA4"); + root.addChild(da3, POSITION_TOP); + da3.addChild(da4, POSITION_TOP); + spyOn(da4); + + da3.forAllTaskDisplayAreas(callback0); + da3.forAllTaskDisplayAreas(callback1); + da3.reduceOnAllTaskDisplayAreas(callback2, 0); + da3.getItemFromTaskDisplayAreas(callback3); + + verify(da4).forAllTaskDisplayAreas(callback0, true /* traverseTopToBottom */); + verify(da4).forAllTaskDisplayAreas(callback1, true /* traverseTopToBottom */); + verify(da4).reduceOnAllTaskDisplayAreas(callback2, 0 /* initValue */, + true /* traverseTopToBottom */); + verify(da4).getItemFromTaskDisplayAreas( + callback3, true /* traverseTopToBottom */); + + // Don't traverse the child if the current DA has type ABOVE_TASKS + final DisplayArea<WindowContainer> da5 = new DisplayArea<>(mWms, ABOVE_TASKS, "DA5"); + final DisplayArea<WindowContainer> da6 = new DisplayArea<>(mWms, ABOVE_TASKS, "DA6"); + root.addChild(da5, POSITION_TOP); + da5.addChild(da6, POSITION_TOP); + spyOn(da6); + + da5.forAllTaskDisplayAreas(callback0); + da5.forAllTaskDisplayAreas(callback1); + da5.reduceOnAllTaskDisplayAreas(callback2, 0); + da5.getItemFromTaskDisplayAreas(callback3); + + verifyZeroInteractions(da6); + } + + @Test + public void testForAllTaskDisplayAreas_appliesOnTaskDisplayAreaInOrder() { + final RootDisplayArea root = + new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms); + final DisplayArea<DisplayArea> da1 = + new DisplayArea<>(mWms, ANY, "DA1"); + final DisplayArea<DisplayArea> da2 = + new DisplayArea<>(mWms, ANY, "DA2"); + final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); + final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA2", FEATURE_VENDOR_FIRST); + final TaskDisplayArea tda3 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA3", FEATURE_VENDOR_FIRST + 1); + root.addChild(da1, POSITION_TOP); + root.addChild(da2, POSITION_TOP); + da1.addChild(tda1, POSITION_TOP); + da2.addChild(tda2, POSITION_TOP); + da2.addChild(tda3, POSITION_TOP); + + /* The hierarchy looks like this + Root + - DA1 + - TDA1 ------ bottom + - DA2 + - TDA2 + - TDA3 ------ top + */ + + // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>) + List<TaskDisplayArea> actualOrder = new ArrayList<>(); + root.forAllTaskDisplayAreas(tda -> { + actualOrder.add(tda); + }); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); + + // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>, boolean) + actualOrder.clear(); + root.forAllTaskDisplayAreas(tda -> { + actualOrder.add(tda); + }, false /* traverseTopToBottom */); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); + + // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>) + actualOrder.clear(); + root.forAllTaskDisplayAreas(tda -> { + actualOrder.add(tda); + return false; + }); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); - SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) { - super(wms, type, name); - } + // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>, boolean) + actualOrder.clear(); + root.forAllTaskDisplayAreas(tda -> { + actualOrder.add(tda); + return false; + }, false /* traverseTopToBottom */); - @Override - SurfaceControl.Builder makeChildSurface(WindowContainer child) { - return new MockSurfaceControlBuilder(); - } + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); + + // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R) + actualOrder.clear(); + root.reduceOnAllTaskDisplayAreas((tda, result) -> { + actualOrder.add(tda); + return result; + }, 0 /* initValue */); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); + + // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R, boolean) + actualOrder.clear(); + root.reduceOnAllTaskDisplayAreas((tda, result) -> { + actualOrder.add(tda); + return result; + }, 0 /* initValue */, false /* traverseTopToBottom */); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); + + // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback) + actualOrder.clear(); + root.getItemFromTaskDisplayAreas(tda -> { + actualOrder.add(tda); + return null; + }); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); + + // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean) + actualOrder.clear(); + root.getItemFromTaskDisplayAreas(tda -> { + actualOrder.add(tda); + return null; + }, false /* traverseTopToBottom */); + + assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); + } + + @Test + public void testForAllTaskDisplayAreas_returnsWhenCallbackReturnTrue() { + final RootDisplayArea root = + new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms); + final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); + final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA2", FEATURE_VENDOR_FIRST); + root.addChild(tda1, POSITION_TOP); + root.addChild(tda2, POSITION_TOP); + + /* The hierarchy looks like this + Root + - TDA1 ------ bottom + - TDA2 ------ top + */ + + root.forAllTaskDisplayAreas(tda -> { + assertThat(tda).isEqualTo(tda2); + return true; + }); + + root.forAllTaskDisplayAreas(tda -> { + assertThat(tda).isEqualTo(tda1); + return true; + }, false /* traverseTopToBottom */); + } + + @Test + public void testReduceOnAllTaskDisplayAreas_returnsTheAccumulativeResult() { + final RootDisplayArea root = + new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms); + final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); + final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA2", FEATURE_VENDOR_FIRST); + root.addChild(tda1, POSITION_TOP); + root.addChild(tda2, POSITION_TOP); + + /* The hierarchy looks like this + Root + - TDA1 ------ bottom + - TDA2 ------ top + */ + + String accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) -> + result + tda.getName(), "" /* initValue */); + assertThat(accumulativeName).isEqualTo("TDA2TDA1"); + + accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) -> + result + tda.getName(), "" /* initValue */, false /* traverseTopToBottom */); + assertThat(accumulativeName).isEqualTo("TDA1TDA2"); + } + + @Test + public void testGetItemFromTaskDisplayAreas_returnsWhenCallbackReturnNotNull() { + final RootDisplayArea root = + new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms); + final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); + final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, + mWms, "TDA2", FEATURE_VENDOR_FIRST); + root.addChild(tda1, POSITION_TOP); + root.addChild(tda2, POSITION_TOP); + + /* The hierarchy looks like this + Root + - TDA1 ------ bottom + - TDA2 ------ top + */ + + TaskDisplayArea result = root.getItemFromTaskDisplayAreas(tda -> { + assertThat(tda).isEqualTo(tda2); + return tda; + }); + + assertThat(result).isEqualTo(tda2); + + result = root.getItemFromTaskDisplayAreas(tda -> { + assertThat(tda).isEqualTo(tda1); + return tda; + }, false /* traverseTopToBottom */); + + assertThat(result).isEqualTo(tda1); + } + + private WindowToken createWindowToken(int type) { + return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(), + type, false /* persist */, null /* displayContent */, + false /* canManageTokens */); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f4f199ca52a1..018b3027bdff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -96,13 +96,10 @@ import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; import android.view.Gravity; -import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; import android.view.ISystemGestureExclusionListener; import android.view.IWindowManager; -import android.view.InsetsSourceControl; -import android.view.InsetsState; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl.Transaction; @@ -950,6 +947,26 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testComputeImeControlTarget_exitingApp() throws Exception { + final DisplayContent dc = createNewDisplay(); + + WindowState exitingWin = createWindow(null, TYPE_BASE_APPLICATION, "exiting app"); + makeWindowVisible(exitingWin); + exitingWin.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; + exitingWin.mAnimatingExit = true; + + dc.mInputMethodControlTarget = exitingWin; + dc.mInputMethodTarget = dc.mInputMethodInputTarget = + createWindow(null, TYPE_BASE_APPLICATION, "starting app"); + + assertEquals(exitingWin, dc.computeImeControlTarget()); + + exitingWin.removeImmediately(); + + assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget()); + } + + @Test public void testComputeImeControlTarget_splitscreen() throws Exception { final DisplayContent dc = createNewDisplay(); dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); @@ -970,28 +987,6 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget()); } - private IDisplayWindowInsetsController createDisplayWindowInsetsController() { - return new IDisplayWindowInsetsController.Stub() { - - @Override - public void insetsChanged(InsetsState insetsState) throws RemoteException { - } - - @Override - public void insetsControlChanged(InsetsState insetsState, - InsetsSourceControl[] insetsSourceControls) throws RemoteException { - } - - @Override - public void showInsets(int i, boolean b) throws RemoteException { - } - - @Override - public void hideInsets(int i, boolean b) throws RemoteException { - } - }; - } - @Test public void testUpdateSystemGestureExclusion() throws Exception { final DisplayContent dc = createNewDisplay(); @@ -1487,7 +1482,6 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testEnsureActivitiesVisibleNotRecursive() { final TaskDisplayArea mockTda = mock(TaskDisplayArea.class); - doReturn(mockTda).when(mDisplayContent).getTaskDisplayAreaAt(anyInt()); final boolean[] called = { false }; doAnswer(invocation -> { // The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index b69a1f3f7766..da7c41a74528 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -828,28 +828,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test - public void forceShowSystemBars_clearsSystemUIFlags() { - doCallRealMethod().when(mDisplayPolicy).updateSystemUiVisibilityLw(); - mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN; - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - mWindow.mAttrs.flags = - FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - mWindow.mSystemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN; - mDisplayPolicy.setForceShowSystemBars(true); - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - // triggers updateSystemUiVisibilityLw which will reset the flags as needed - int finishPostLayoutPolicyLw = mDisplayPolicy.focusChangedLw(mWindow, mWindow); - - assertEquals(WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT, finishPostLayoutPolicyLw); - assertEquals(0, mDisplayPolicy.mLastSystemUiFlags); - assertEquals(0, mWindow.mAttrs.systemUiVisibility); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - } - - @Test public void testScreenDecorWindows() { final WindowState decorWindow = spy( createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow")); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index c794e1a3b328..87bc7f1bf781 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -172,8 +172,9 @@ public class InsetsPolicyTest extends WindowTestsBase { } @Test - public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() { - mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true); + public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() { + mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); + mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true); addWindow(TYPE_STATUS_BAR, "statusBar"); addWindow(TYPE_NAVIGATION_BAR, "navBar"); diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index add2054d5638..13f04d23ccd3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -46,7 +46,6 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.testutils.OffsettableClock; @@ -244,7 +243,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } @Test - public void testChange() throws Exception { + public void testChangeToSmallerSize() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mChangingContainers.add(win.mActivityRecord); try { @@ -279,8 +278,61 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { assertEquals(false, app.isTranslucent); verify(mMockTransaction).setPosition( mMockLeash, app.startBounds.left, app.startBounds.top); - verify(mMockTransaction).setWindowCrop(mMockLeash, 200, 200); + verify(mMockTransaction).setWindowCrop( + mMockLeash, app.startBounds.width(), app.startBounds.height()); verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); + verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1); + + finishedCaptor.getValue().onAnimationFinished(); + verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), + eq(record.mAdapter)); + verify(mThumbnailFinishedCallback).onAnimationFinished( + eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter)); + } finally { + mDisplayContent.mChangingContainers.clear(); + } + } + + @Test + public void testChangeTolargerSize() throws Exception { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mDisplayContent.mChangingContainers.add(win.mActivityRecord); + try { + final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( + win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200), + new Rect(50, 100, 150, 150)); + assertNotNull(record.mThumbnailAdapter); + ((AnimationAdapter) record.mAdapter) + .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, + mFinishedCallback); + ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, + mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); + mController.goodToGo(); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + finishedCaptor.capture()); + assertEquals(1, appsCaptor.getValue().length); + final RemoteAnimationTarget app = appsCaptor.getValue()[0]; + assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode); + assertEquals(new Point(0, 0), app.position); + assertEquals(new Rect(0, 0, 200, 200), app.sourceContainerBounds); + assertEquals(new Rect(50, 100, 150, 150), app.startBounds); + assertEquals(mMockLeash, app.leash); + assertEquals(mMockThumbnailLeash, app.startLeash); + assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect); + assertEquals(false, app.isTranslucent); + verify(mMockTransaction).setPosition( + mMockLeash, app.startBounds.left, app.startBounds.top); + verify(mMockTransaction).setWindowCrop( + mMockLeash, app.startBounds.width(), app.startBounds.height()); + verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); + verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 51db099676b0..d9ec0b138552 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -292,13 +292,11 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount()); final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent(); - doReturn(2).when(dc).getTaskDisplayAreaCount(); final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(dc, mRootWindowContainer.mWmService, "SecondaryTaskDisplayArea", FEATURE_VENDOR_FIRST); // Add second display area right above the default one defaultTaskDisplayArea.getParent().addChild(secondTaskDisplayArea, defaultTaskDisplayArea.getParent().mChildren.indexOf(defaultTaskDisplayArea) + 1); - doReturn(secondTaskDisplayArea).when(dc).getTaskDisplayAreaAt(1); final ActivityStack secondStack = secondTaskDisplayArea.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); new ActivityBuilder(mService).setCreateTask(true).setStack(secondStack) diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 35d1b17d5822..181de8e2a1a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -148,7 +148,7 @@ public class RootWindowContainerTests extends WindowTestsBase { @Test public void testAllPausedActivitiesComplete() { DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); - TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(0); + TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea(); ActivityStack stack = taskDisplayArea.getStackAt(0); ActivityRecord activity = createActivityRecord(displayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index d2a2732d60ab..213c1f52aa37 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -43,10 +43,9 @@ class TestDisplayContent extends DisplayContent { // hard-code to FULLSCREEN for tests. setWindowingMode(WINDOWING_MODE_FULLSCREEN); spyOn(this); - for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { - spyOn(getTaskDisplayAreaAt(i)); - } - + forAllTaskDisplayAreas(taskDisplayArea -> { + spyOn(taskDisplayArea); + }); final DisplayRotation displayRotation = getDisplayRotation(); spyOn(displayRotation); doAnswer(invocation -> { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 040717d2aa56..1266c50d368e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -60,24 +60,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { @Rule public ExpectedException mExpectedException = ExpectedException.none(); - @Test - public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() { - if (!isAutomotive()) { - mExpectedException.expect(UnsupportedOperationException.class); - - mWm.setForceShowSystemBars(true); - } - } - - @Test - public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() { - if (isAutomotive()) { - mExpectedException.none(); - - mWm.setForceShowSystemBars(true); - } - } - private boolean isAutomotive() { return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE); @@ -167,7 +149,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { TaskDisplayArea secondTda = new TaskDisplayArea(display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST); display.addChild(secondTda, 1); - display.mTaskDisplayAreas.add(secondTda); // Current focused window ActivityStack focusedStack = createTaskStackOnDisplay( WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 5f40b6d96b22..04d52afa3874 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -673,13 +673,12 @@ public class WindowOrganizerTests extends WindowTestsBase { private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { ArrayList<Task> out = new ArrayList<>(); - for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) { - final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx); + dc.forAllTaskDisplayAreas(taskDisplayArea -> { for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { final Task t = taskDisplayArea.getStackAt(sNdx); if (t.mCreatedByOrganizer) out.add(t); } - } + }); return out; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index a1e5b80eb2ed..156298c86d41 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -41,11 +41,15 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.content.Intent; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.view.Display; import android.view.DisplayInfo; +import android.view.IDisplayWindowInsetsController; import android.view.IWindow; +import android.view.InsetsSourceControl; +import android.view.InsetsState; import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.WindowManager; @@ -123,7 +127,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY, "mChildAppWindowBelow"); - mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false); + mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false); // Adding a display will cause freezing the display. Make sure to wait until it's // unfrozen to not run into race conditions with the tests. @@ -344,6 +348,32 @@ class WindowTestsBase extends SystemServiceTestsBase { return createNewDisplay(displayInfo, false /* supportIme */); } + IDisplayWindowInsetsController createDisplayWindowInsetsController() { + return new IDisplayWindowInsetsController.Stub() { + + @Override + public void insetsChanged(InsetsState insetsState) throws RemoteException { + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] insetsSourceControls) throws RemoteException { + } + + @Override + public void showInsets(int i, boolean b) throws RemoteException { + } + + @Override + public void hideInsets(int i, boolean b) throws RemoteException { + } + + @Override + public void topFocusedWindowChanged(String packageName) { + } + }; + } + /** Sets the default minimum task size to 1 so that tests can use small task sizes */ void removeGlobalMinSizeRestriction() { mWm.mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 23a097eb0c7c..5264e9aaba27 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -20,16 +20,21 @@ import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import android.content.res.Configuration; +import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; @@ -38,6 +43,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.function.BiFunction; + /** * Tests for the {@link WindowToken} class. * @@ -205,4 +212,27 @@ public class WindowTokenTests extends WindowTestsBase { false /* fromClientToken */); assertNotNull(nonClientToken.mSurfaceControl); } + + @Test + public void testWindowAttachedWithOptions() { + BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc = + ((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy) + .mSelectRootForWindowFunc; + spyOn(selectFunc); + + final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); + + verify(selectFunc).apply(token1, null); + + final Bundle options = new Bundle(); + final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + false /* fromClientToken */, options /* options */); + + verify(selectFunc).apply(token2, options); + } } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 57345f1c0e21..6cf0eecc6352 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1367,6 +1367,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser try { writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE, FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim()); + } catch (FileNotFoundException exNotFound) { + Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: " + + "kernel state:" + STATE_PATH); } catch (Exception e) { Slog.e(TAG, "Could not read kernel state", e); } @@ -1375,6 +1378,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser writeStringIfNotNull(dump, "kernel_function_list", UsbHandlerProto.KERNEL_FUNCTION_LIST, FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim()); + } catch (FileNotFoundException exNotFound) { + Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: " + + "kernel function list:" + FUNCTIONS_PATH); } catch (Exception e) { Slog.e(TAG, "Could not read kernel function list", e); } diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index 2d0bd52f84ee..4e4abd0f63b0 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -16,7 +16,9 @@ package android.telephony; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.telephony.cdma.CdmaCellLocation; @@ -31,11 +33,25 @@ import com.android.internal.telephony.PhoneConstants; public abstract class CellLocation { /** - * Request an update of the current location. If the location has changed, - * a broadcast will be sent to everyone registered with {@link - * PhoneStateListener#LISTEN_CELL_LOCATION}. + * This method will not do anything. + * + * Whenever location changes, a callback will automatically be be sent to + * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}. + * + * <p>This method is a no-op for callers targeting SDK level 31 or greater. + * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * <p>This method is a no-op for callers that target SDK level 28 or below and lack + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * + * Callers wishing to request a single location update should use + * {@link TelephonyManager#requestCellInfoUpdate}. */ public static void requestLocationUpdate() { + // Since this object doesn't have a context, this is the best we can do. + final Context appContext = ActivityThread.currentApplication(); + if (appContext == null) return; // should never happen + try { ITelephony phone = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer @@ -43,7 +59,7 @@ public abstract class CellLocation { .getTelephonyServiceRegisterer() .get()); if (phone != null) { - phone.updateServiceLocation(); + phone.updateServiceLocationWithPackageName(appContext.getOpPackageName()); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 347dcc81ce4e..717a9b155cbf 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -255,28 +255,6 @@ public class SmsMessage { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging and for RIL - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - // received SMS in 3GPP format - SmsMessageBase wrappedMessage = - com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu); - - if (wrappedMessage != null) { - return new SmsMessage(wrappedMessage); - } else { - Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null"); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3104c20b0063..d96e024d4c60 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2341,58 +2341,6 @@ public class TelephonyManager { } /** - * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates() { - enableLocationUpdates(getSubId()); - } - - /** - * Enables location update notifications for a subscription. - * {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @param subId for which the location updates are enabled - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.enableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** - * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void disableLocationUpdates() { - disableLocationUpdates(getSubId()); - } - - /** @hide */ - public void disableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.disableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** * Returns the neighboring cell information of the device. * * @return List of NeighboringCellInfo or null if info unavailable. @@ -9290,17 +9238,14 @@ public class TelephonyManager { return RADIO_POWER_UNAVAILABLE; } - /** @hide */ + /** + * This method should not be used due to privacy and stability concerns. + * + * @hide + */ @SystemApi - @SuppressLint("Doclava125") public void updateServiceLocation() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.updateServiceLocation(); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e); - } + Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()"); } /** @hide */ diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 4276e713561b..e2de5c82940f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -222,42 +222,29 @@ interface ITelephony { boolean setRadioPower(boolean turnOn); /** - * Request to update location information in service state + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void updateServiceLocation(); /** - * Request to update location information for a subscrition in service state - * @param subId user preferred subId. + * Version of updateServiceLocation that records the caller and validates permissions. */ - void updateServiceLocationForSubscriber(int subId); + void updateServiceLocationWithPackageName(String callingPkg); /** - * Enable location update notifications. + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void enableLocationUpdates(); /** - * Enable location update notifications. - * @param subId user preferred subId. - */ - void enableLocationUpdatesForSubscriber(int subId); - - /** - * Disable location update notifications. + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void disableLocationUpdates(); /** - * Disable location update notifications. - * @param subId user preferred subId. - */ - void disableLocationUpdatesForSubscriber(int subId); - - /** * Allow mobile data connections. */ @UnsupportedAppUsage diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index d41a6c889afb..d216162cc257 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -171,8 +171,8 @@ public class TelephonyIntents { * if not specified </dd> * </dl> * - * <p class="note"> - * Requires the READ_PHONE_STATE permission. + * <p class="note">This is a sticky broadcast, and therefore requires no permissions to listen + * to. Do not add any additional information to this broadcast. * * <p class="note">This is a protected intent that can only be sent * by the system. diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index ccb14749109d..e4205aaae5b6 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -139,38 +139,6 @@ public class SmsMessage extends SmsMessageBase { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - try { - SmsMessage msg = new SmsMessage(); - msg.parsePdu(pdu); - return msg; - } catch (RuntimeException ex) { - Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); - return null; - } - } - - /** @hide */ - public static SmsMessage newFromCDS(byte[] pdu) { - try { - SmsMessage msg = new SmsMessage(); - msg.parsePdu(pdu); - return msg; - } catch (RuntimeException ex) { - Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. diff --git a/test-base/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java index 6b79314a4385..9f7a2fa44dc2 100644 --- a/test-base/src/android/test/InstrumentationTestCase.java +++ b/test-base/src/android/test/InstrumentationTestCase.java @@ -34,9 +34,9 @@ import junit.framework.TestCase; * A test case that has access to {@link Instrumentation}. * * @deprecated Use - * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html"> + * <a href="{@docRoot}reference/androidx/test/platform/app/InstrumentationRegistry.html"> * InstrumentationRegistry</a> instead. New tests should be written using the - * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. + * <a href="{@docRoot}training/testing/index.html">AndroidX Test Library</a>. */ @Deprecated public class InstrumentationTestCase extends TestCase { diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 2be4ae6bb214..a23df920b396 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -29,7 +29,12 @@ java_test_host { name: "StagedRollbackTest", srcs: ["StagedRollbackTest/src/**/*.java"], libs: ["tradefed"], - static_libs: ["testng", "compatibility-tradefed", "RollbackTestLib"], + static_libs: [ + "compatibility-tradefed", + "frameworks-base-hostutils", + "RollbackTestLib", + "testng", + ], test_suites: ["general-tests"], test_config: "StagedRollbackTest.xml", data: [":com.android.apex.apkrollback.test_v1"], @@ -39,7 +44,7 @@ java_test_host { name: "NetworkStagedRollbackTest", srcs: ["NetworkStagedRollbackTest/src/**/*.java"], libs: ["tradefed"], - static_libs: ["RollbackTestLib"], + static_libs: ["RollbackTestLib", "frameworks-base-hostutils"], test_suites: ["general-tests"], test_config: "NetworkStagedRollbackTest.xml", } diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 55bbe22c4d86..c2fd0c39221e 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -453,6 +453,32 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { after.forEach(dir -> assertDirectoryIsEmpty(dir)); } + @Test + public void testExpireApexRollback() throws Exception { + List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); + pushTestApex(); + + // Push files to apex data directory + String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1; + String oldFilePath2 = + apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; + assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); + assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + + // Install new version of the APEX with rollback enabled + runPhase("testRollbackApexDataDirectories_Phase1"); + getDevice().reboot(); + + List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); + // Only check directories newly created during the test + after.removeAll(before); + // Expire all rollbacks and check CE snapshot directories are deleted + runPhase("testCleanUp"); + for (String dir : after) { + assertNull(getDevice().getFileEntry(dir)); + } + } + private void pushTestApex() throws Exception { CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index c3fdd695c2b7..65d15a7dccaa 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -24,7 +24,7 @@ java_test_host { name: "StagedInstallInternalTest", srcs: ["src/**/*.java"], libs: ["tradefed"], - static_libs: ["testng", "compatibility-tradefed"], + static_libs: ["testng", "compatibility-tradefed", "frameworks-base-hostutils"], test_suites: ["general-tests"], test_config: "StagedInstallInternalTest.xml", } diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 9b432f7d0ca5..e6ba8015e5b5 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -19,14 +19,17 @@ package com.android.tests.stagedinstallinternal.host; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import com.android.ddmlib.Log; +import com.android.tests.rollback.host.AbandonSessionsRule; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import com.android.tradefed.util.ProcessInfo; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,7 +38,9 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String TAG = StagedInstallInternalTest.class.getSimpleName(); private static final long SYSTEM_SERVER_TIMEOUT_MS = 60 * 1000; - private boolean mWasRoot = false; + + @Rule + public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); /** * Runs the given phase of a test by calling into the device. @@ -62,21 +67,11 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Before public void setUp() throws Exception { - mWasRoot = getDevice().isAdbRoot(); - if (!mWasRoot) { - getDevice().enableAdbRoot(); - } cleanUp(); - // Abandon all staged sessions - getDevice().executeShellCommand("pm install-abandon $(pm get-stagedsessions --only-ready " - + "--only-parent --only-sessionid)"); } @After public void tearDown() throws Exception { - if (!mWasRoot) { - getDevice().disableAdbRoot(); - } cleanUp(); } @@ -89,23 +84,24 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private void restartSystemServer() throws Exception { // Restart the system server - long oldStartTime = getDevice().getProcessByName("system_server").getStartTime(); + ProcessInfo oldPs = getDevice().getProcessByName("system_server"); + + getDevice().enableAdbRoot(); // Need root to restart system server assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system"); + getDevice().disableAdbRoot(); // Wait for new system server process to start long start = System.currentTimeMillis(); - long newStartTime = oldStartTime; while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) { ProcessInfo newPs = getDevice().getProcessByName("system_server"); if (newPs != null) { - newStartTime = newPs.getStartTime(); - if (newStartTime != oldStartTime) { - break; + if (newPs.getPid() != oldPs.getPid()) { + getDevice().waitForDeviceAvailable(); + return; } } Thread.sleep(500); } - assertThat(newStartTime).isNotEqualTo(oldStartTime); - getDevice().waitForDeviceAvailable(); + fail("Timed out in restarting system server"); } } diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index 9e3aeefd204c..5a29c2c96ba7 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -114,6 +114,7 @@ public class PermissionMonitorTest { @Mock private INetd mNetdService; @Mock private PackageManagerInternal mMockPmi; @Mock private UserManager mUserManager; + @Mock private PermissionMonitor.Dependencies mDeps; private PermissionMonitor mPermissionMonitor; @@ -128,7 +129,7 @@ public class PermissionMonitorTest { new UserInfo(MOCK_USER2, "", 0), })); - mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService)); + mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mMockPmi); @@ -283,14 +284,14 @@ public class PermissionMonitorTest { @Test public void testHasRestrictedNetworkPermissionSystemUid() { - doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt(); + doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); assertTrue(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL)); assertTrue(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt(); + doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); assertFalse(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL)); diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/AbandonSessionsRule.java b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java index b08621314ee0..b08621314ee0 100644 --- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/AbandonSessionsRule.java +++ b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index a230de46dcf3..4c741c49cfdb 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -25,6 +25,7 @@ namespace android { namespace stats_log_api_gen { +using google::protobuf::OneofDescriptor; using google::protobuf::EnumDescriptor; using google::protobuf::FieldDescriptor; using google::protobuf::FileDescriptor; @@ -396,16 +397,14 @@ int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t> collate_enums(*field->enum_type(), &atField); } - // Generate signature for pushed atoms - if (atomDecl->code < PULL_ATOM_START_ID) { - if (javaType == JAVA_TYPE_ENUM) { - // All enums are treated as ints when it comes to function signatures. - signature->push_back(JAVA_TYPE_INT); - } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { - signature->push_back(JAVA_TYPE_BYTE_ARRAY); - } else { - signature->push_back(javaType); - } + // Generate signature for atom. + if (javaType == JAVA_TYPE_ENUM) { + // All enums are treated as ints when it comes to function signatures. + signature->push_back(JAVA_TYPE_INT); + } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { + signature->push_back(JAVA_TYPE_BYTE_ARRAY); + } else { + signature->push_back(javaType); } atomDecl->fields.push_back(atField); @@ -518,8 +517,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* shared_ptr<AtomDecl> atomDecl = make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name()); - if (atomDecl->code < PULL_ATOM_START_ID && - atomField->options().GetExtension(os::statsd::truncate_timestamp)) { + if (atomField->options().GetExtension(os::statsd::truncate_timestamp)) { addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER, ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL, AnnotationValue(true)); @@ -537,7 +535,24 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* continue; } - FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature]; + const OneofDescriptor* oneofAtom = atomField->containing_oneof(); + if (oneofAtom == nullptr) { + print_error(atomField, "Atom is not declared in a `oneof` field: %s\n", + atomField->name().c_str()); + errorCount++; + continue; + } + else if ((oneofAtom->name() != ONEOF_PUSHED_ATOM_NAME) && + (oneofAtom->name() != ONEOF_PULLED_ATOM_NAME)) { + print_error(atomField, "Atom is neither a pushed nor pulled atom: %s\n", + atomField->name().c_str()); + errorCount++; + continue; + } + + FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = oneofAtom->name() == + ONEOF_PUSHED_ATOM_NAME ? atoms->signatureInfoMap[signature] : + atoms->pulledAtomsSignatureInfoMap[signature]; populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet); atoms->decls.insert(atomDecl); @@ -556,6 +571,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* } if (dbg) { + // Signatures for pushed atoms. printf("signatures = [\n"); for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin(); it != atoms->signatureInfoMap.end(); it++) { @@ -566,6 +582,17 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* } printf("\n"); } + + // Signatures for pull atoms. + for (SignatureInfoMap::const_iterator it = atoms->pulledAtomsSignatureInfoMap.begin(); + it != atoms->pulledAtomsSignatureInfoMap.end(); it++) { + printf(" "); + for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end(); + jt++) { + printf(" %d", (int)*jt); + } + printf("\n"); + } printf("]\n"); } diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 10b34ecf5f54..e637ed945a08 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -29,6 +29,7 @@ namespace android { namespace stats_log_api_gen { +using google::protobuf::OneofDescriptor; using google::protobuf::Descriptor; using google::protobuf::FieldDescriptor; using std::map; @@ -41,6 +42,14 @@ const int PULL_ATOM_START_ID = 10000; const int FIRST_UID_IN_CHAIN_ID = 0; +/** + * The types of oneof atoms. + * + * `OneofDescriptor::name()` returns the name of the oneof. + */ +const string ONEOF_PUSHED_ATOM_NAME = "pushed"; +const string ONEOF_PULLED_ATOM_NAME = "pulled"; + enum AnnotationId : uint8_t { ANNOTATION_ID_IS_UID = 1, ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2, @@ -184,6 +193,7 @@ using SignatureInfoMap = map<vector<java_type_t>, FieldNumberToAtomDeclSet>; struct Atoms { SignatureInfoMap signatureInfoMap; + SignatureInfoMap pulledAtomsSignatureInfoMap; AtomDeclSet decls; AtomDeclSet non_chained_decls; SignatureInfoMap nonChainedSignatureInfoMap; diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index f4c937c3f599..ffbe9f800736 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -96,29 +96,160 @@ static void write_annotations(FILE* out, int argIndex, } } +static void write_method_signature(FILE* out, const vector<java_type_t>& signature, + const AtomDecl& attributionDecl) { + int argIndex = 1; + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { + if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { + for (auto chainField : attributionDecl.fields) { + fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), + chainField.name.c_str()); + } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", android.util.SparseArray<Object> valueMap"); + } else { + fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); + } + argIndex++; + } +} + +static int write_method_body(FILE* out, const vector<java_type_t>& signature, + const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet, + const AtomDecl& attributionDecl, const string& indent) { + // Start StatsEvent.Builder. + fprintf(out, + "%s final StatsEvent.Builder builder = " + "StatsEvent.newBuilder();\n", + indent.c_str()); + + // Write atom code. + fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str()); + write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet); + + // Write the args. + int argIndex = 1; + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { + switch (*arg) { + case JAVA_TYPE_BOOLEAN: + fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: + fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex); + break; + case JAVA_TYPE_FLOAT: + fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_LONG: + fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex); + break; + case JAVA_TYPE_STRING: + fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_BYTE_ARRAY: + fprintf(out, + "%s builder.writeByteArray(null == arg%d ? new byte[0] : " + "arg%d);\n", + indent.c_str(), argIndex, argIndex); + break; + case JAVA_TYPE_ATTRIBUTION_CHAIN: { + const char* uidName = attributionDecl.fields.front().name.c_str(); + const char* tagName = attributionDecl.fields.back().name.c_str(); + + fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); + fprintf(out, "%s null == %s ? new int[0] : %s,\n", + indent.c_str(), uidName, uidName); + fprintf(out, "%s null == %s ? new String[0] : %s);\n", + indent.c_str(), tagName, tagName); + break; + } + case JAVA_TYPE_KEY_VALUE_PAIR: + fprintf(out, "\n"); + fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str()); + fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); + fprintf(out, "%s android.util.SparseIntArray intMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseLongArray longMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", + indent.c_str()); + fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); + fprintf(out, "%s final int key = valueMap.keyAt(i);\n", + indent.c_str()); + fprintf(out, "%s final Object value = valueMap.valueAt(i);\n", + indent.c_str()); + fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str()); + fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); + fprintf(out, + "%s intMap = new " + "android.util.SparseIntArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s intMap.put(key, (Integer) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Long) {\n", + indent.c_str()); + fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); + fprintf(out, + "%s longMap = new " + "android.util.SparseLongArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s longMap.put(key, (Long) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof String) {\n", + indent.c_str()); + fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); + fprintf(out, + "%s stringMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s stringMap.put(key, (String) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Float) {\n", + indent.c_str()); + fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); + fprintf(out, + "%s floatMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s floatMap.put(key, (Float) value);\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, + "%s builder.writeKeyValuePairs(" + "intMap, longMap, stringMap, floatMap);\n", + indent.c_str()); + break; + default: + // Unsupported types: OBJECT, DOUBLE. + fprintf(stderr, "Encountered unsupported type."); + return 1; + } + write_annotations(out, argIndex, fieldNumberToAtomDeclSet); + argIndex++; + } + return 0; +} + static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap, const AtomDecl& attributionDecl, const bool supportQ) { for (auto signatureInfoMapIt = signatureInfoMap.begin(); signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { // Print method signature. fprintf(out, " public static void write(int code"); - const vector<java_type_t>& signature = signatureInfoMapIt->first; - const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second; - int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); - arg++) { - if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { - for (auto chainField : attributionDecl.fields) { - fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), - chainField.name.c_str()); - } - } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", android.util.SparseArray<Object> valueMap"); - } else { - fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); - } - argIndex++; - } + write_method_signature(out, signatureInfoMapIt->first, attributionDecl); fprintf(out, ") {\n"); // Print method body. @@ -128,130 +259,13 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa indent = " "; } - // Start StatsEvent.Builder. - fprintf(out, - "%s final StatsEvent.Builder builder = " - "StatsEvent.newBuilder();\n", - indent.c_str()); - - // Write atom code. - fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str()); - write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet); - - // Write the args. - argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); - arg++) { - switch (*arg) { - case JAVA_TYPE_BOOLEAN: - fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(), - argIndex); - break; - case JAVA_TYPE_INT: - case JAVA_TYPE_ENUM: - fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_FLOAT: - fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(), - argIndex); - break; - case JAVA_TYPE_LONG: - fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_STRING: - fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), - argIndex); - break; - case JAVA_TYPE_BYTE_ARRAY: - fprintf(out, - "%s builder.writeByteArray(null == arg%d ? new byte[0] : " - "arg%d);\n", - indent.c_str(), argIndex, argIndex); - break; - case JAVA_TYPE_ATTRIBUTION_CHAIN: { - const char* uidName = attributionDecl.fields.front().name.c_str(); - const char* tagName = attributionDecl.fields.back().name.c_str(); - - fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); - fprintf(out, "%s null == %s ? new int[0] : %s,\n", - indent.c_str(), uidName, uidName); - fprintf(out, "%s null == %s ? new String[0] : %s);\n", - indent.c_str(), tagName, tagName); - break; - } - case JAVA_TYPE_KEY_VALUE_PAIR: - fprintf(out, "\n"); - fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str()); - fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); - fprintf(out, "%s android.util.SparseIntArray intMap = null;\n", - indent.c_str()); - fprintf(out, "%s android.util.SparseLongArray longMap = null;\n", - indent.c_str()); - fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n", - indent.c_str()); - fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", - indent.c_str()); - fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); - fprintf(out, "%s final int key = valueMap.keyAt(i);\n", - indent.c_str()); - fprintf(out, "%s final Object value = valueMap.valueAt(i);\n", - indent.c_str()); - fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str()); - fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); - fprintf(out, - "%s intMap = new " - "android.util.SparseIntArray();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s intMap.put(key, (Integer) value);\n", - indent.c_str()); - fprintf(out, "%s } else if (value instanceof Long) {\n", - indent.c_str()); - fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); - fprintf(out, - "%s longMap = new " - "android.util.SparseLongArray();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s longMap.put(key, (Long) value);\n", - indent.c_str()); - fprintf(out, "%s } else if (value instanceof String) {\n", - indent.c_str()); - fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); - fprintf(out, - "%s stringMap = new " - "android.util.SparseArray<>();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s stringMap.put(key, (String) value);\n", - indent.c_str()); - fprintf(out, "%s } else if (value instanceof Float) {\n", - indent.c_str()); - fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); - fprintf(out, - "%s floatMap = new " - "android.util.SparseArray<>();\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s floatMap.put(key, (Float) value);\n", - indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, - "%s builder.writeKeyValuePairs(" - "intMap, longMap, stringMap, floatMap);\n", - indent.c_str()); - break; - default: - // Unsupported types: OBJECT, DOUBLE. - fprintf(stderr, "Encountered unsupported type."); - return 1; - } - write_annotations(out, argIndex, fieldNumberToAtomDeclSet); - argIndex++; + int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second, + attributionDecl, indent); + if (ret != 0) { + return ret; } - fprintf(out, "\n"); + fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str()); fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str()); @@ -259,9 +273,9 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa if (supportQ) { fprintf(out, " } else {\n"); fprintf(out, " QLogger.write(code"); - argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + int argIndex = 1; + for (vector<java_type_t>::const_iterator arg = signatureInfoMapIt->first.begin(); + arg != signatureInfoMapIt->first.end(); arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { const char* uidName = attributionDecl.fields.front().name.c_str(); const char* tagName = attributionDecl.fields.back().name.c_str(); @@ -285,6 +299,32 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa return 0; } +static int write_java_build_stats_event_methods(FILE* out, const SignatureInfoMap& signatureInfoMap, + const AtomDecl& attributionDecl) { + for (auto signatureInfoMapIt = signatureInfoMap.begin(); + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + // Print method signature. + fprintf(out, " public static StatsEvent buildStatsEvent(int code"); + write_method_signature(out, signatureInfoMapIt->first, attributionDecl); + fprintf(out, ") {\n"); + + // Print method body. + string indent(""); + int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second, + attributionDecl, indent); + if (ret != 0) { + return ret; + } + fprintf(out, "\n"); + + fprintf(out, "%s return builder.build();\n", indent.c_str()); + + fprintf(out, " }\n"); // method + fprintf(out, "\n"); + } + return 0; +} + int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, const string& javaClass, const string& javaPackage, const bool supportQ, const bool supportWorkSource) { @@ -319,6 +359,8 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attribut fprintf(out, " // Write methods\n"); errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ); errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap); + errors += write_java_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap, + attributionDecl); if (supportWorkSource) { errors += write_java_work_source_methods(out, atoms.signatureInfoMap); } diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index aaa488e44fee..e658b62b8daa 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -58,7 +58,7 @@ message AllTypesAtom { } message Event { - oneof event { + oneof pushed { OutOfOrderAtom out_of_order_atom = 2; IntAtom int_atom = 1; AnotherIntAtom another_int_atom = 3; @@ -74,7 +74,7 @@ message BadTypesAtom { } message BadTypesEvent { - oneof event { + oneof pushed { BadTypesAtom bad_types_atom = 1; } } @@ -84,7 +84,7 @@ message BadSkippedFieldSingleAtom { } message BadSkippedFieldSingle { - oneof event { + oneof pushed { BadSkippedFieldSingleAtom bad = 1; } } @@ -96,7 +96,7 @@ message BadSkippedFieldMultipleAtom { } message BadSkippedFieldMultiple { - oneof event { + oneof pushed { BadSkippedFieldMultipleAtom bad = 1; } } @@ -107,11 +107,11 @@ message BadAttributionNodePositionAtom { } message BadAttributionNodePosition { - oneof event { BadAttributionNodePositionAtom bad = 1; } + oneof pushed { BadAttributionNodePositionAtom bad = 1; } } message GoodEventWithBinaryFieldAtom { - oneof event { GoodBinaryFieldAtom field1 = 1; } + oneof pushed { GoodBinaryFieldAtom field1 = 1; } } message ComplexField { @@ -124,7 +124,7 @@ message GoodBinaryFieldAtom { } message BadEventWithBinaryFieldAtom { - oneof event { BadBinaryFieldAtom field1 = 1; } + oneof pushed { BadBinaryFieldAtom field1 = 1; } } message BadBinaryFieldAtom { @@ -133,7 +133,7 @@ message BadBinaryFieldAtom { } message BadStateAtoms { - oneof event { + oneof pushed { BadStateAtom1 bad1 = 1; BadStateAtom2 bad2 = 2; BadStateAtom3 bad3 = 3; @@ -141,7 +141,7 @@ message BadStateAtoms { } message GoodStateAtoms { - oneof event { + oneof pushed { GoodStateAtom1 good1 = 1; GoodStateAtom2 good2 = 2; } @@ -204,7 +204,7 @@ message NoModuleAtom { } message ModuleAtoms { - oneof event { + oneof pushed { ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"]; ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"]; ModuleOneAndTwoAtom module_one_and_two_atom = 3 [ @@ -213,3 +213,24 @@ message ModuleAtoms { NoModuleAtom no_module_atom = 4; } } + +message NotAPushNorPullAtom { + oneof event { + IntAtom int_atom = 1; + } +} + +message AtomNotInAOneof { + optional IntAtom int_atom = 1; +} + +message PushedAndPulledAtoms { + oneof pushed { + IntAtom int_atom_1 = 1; + } + + oneof pulled { + OutOfOrderAtom out_of_order_atom = 11; + AnotherIntAtom another_int_atom = 10; + } +} diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index dbae58889333..5fd728a29c07 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -365,5 +365,69 @@ TEST(CollationTest, RecognizeModule1Atom) { EXPECT_TRUE(annotation->value.boolValue); } +/** + * Test that an atom is not a pushed nor pulled atom. + */ +TEST(CollationTest, InvalidAtomType) { + Atoms atoms; + int errorCount = collate_atoms(NotAPushNorPullAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms); + + EXPECT_EQ(1, errorCount); +} + +/** + * Test that an atom was not declared in a `oneof` field. + */ +TEST(CollationTest, AtomNotDeclaredInAOneof) { + Atoms atoms; + int errorCount = collate_atoms(AtomNotInAOneof::descriptor(), DEFAULT_MODULE_NAME, &atoms); + + EXPECT_EQ(1, errorCount); +} + +/** + * Test a correct collation with pushed and pulled atoms. + */ +TEST(CollationTest, CollatePushedAndPulledAtoms) { + Atoms atoms; + int errorCount = collate_atoms(PushedAndPulledAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms); + + EXPECT_EQ(0, errorCount); + EXPECT_EQ(1ul, atoms.signatureInfoMap.size()); + EXPECT_EQ(2ul, atoms.pulledAtomsSignatureInfoMap.size()); + + // IntAtom + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT); + + // AnotherIntAtom + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT); + + // OutOfOrderAtom + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT); + + EXPECT_EQ(3ul, atoms.decls.size()); + + AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); + EXPECT_EQ(1, (*atomIt)->code); + EXPECT_EQ("int_atom_1", (*atomIt)->name); + EXPECT_EQ("IntAtom", (*atomIt)->message); + EXPECT_NO_ENUM_FIELD((*atomIt)); + atomIt++; + + EXPECT_EQ(10, (*atomIt)->code); + EXPECT_EQ("another_int_atom", (*atomIt)->name); + EXPECT_EQ("AnotherIntAtom", (*atomIt)->message); + EXPECT_NO_ENUM_FIELD((*atomIt)); + atomIt++; + + EXPECT_EQ(11, (*atomIt)->code); + EXPECT_EQ("out_of_order_atom", (*atomIt)->name); + EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message); + EXPECT_NO_ENUM_FIELD((*atomIt)); + atomIt++; + + EXPECT_EQ(atoms.decls.end(), atomIt); +} + } // namespace stats_log_api_gen } // namespace android |