diff options
284 files changed, 8684 insertions, 3337 deletions
diff --git a/Android.bp b/Android.bp index 9b8e01829c6f..aa94d2236084 100644 --- a/Android.bp +++ b/Android.bp @@ -22,9 +22,9 @@ java_defaults { ], errorprone: { javacflags: [ - "-Xep:AndroidFrameworkBinderIdentity:ERROR", + // "-Xep:AndroidFrameworkBinderIdentity:ERROR", "-Xep:AndroidFrameworkCompatChange:ERROR", - "-Xep:AndroidFrameworkUid:ERROR", + // "-Xep:AndroidFrameworkUid:ERROR", // NOTE: only enable to generate local patchfiles // "-XepPatchChecks:refaster:frameworks/base/errorprone/refaster/EfficientXml.java.refaster", // "-XepPatchLocation:/tmp/refaster/", @@ -44,6 +44,7 @@ java_defaults { "-Xep:AndroidFrameworkEfficientCollections:OFF", "-Xep:AndroidFrameworkEfficientParcelable:OFF", "-Xep:AndroidFrameworkEfficientStrings:OFF", + "-Xep:AndroidFrameworkEfficientXml:OFF", ], }, } @@ -626,6 +627,7 @@ java_defaults { "av-types-aidl-java", "mediatranscoding_aidl_interface-java", "soundtrigger_middleware-aidl-java", + "modules-utils-os", ], } @@ -1267,7 +1269,6 @@ aidl_mapping { filegroup { name: "framework-telephony-common-shared-srcs", srcs: [ - "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/os/RegistrantList.java", "core/java/android/os/Registrant.java", "core/java/android/util/IndentingPrintWriter.java", @@ -1350,7 +1351,6 @@ filegroup { name: "framework-wifi-service-shared-srcs", srcs: [ "core/java/android/net/InterfaceConfiguration.java", - "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/util/BackupUtils.java", "core/java/android/util/Rational.java", "core/java/com/android/internal/util/FastXmlSerializer.java", diff --git a/StubLibraries.bp b/StubLibraries.bp index 49a42d7525a5..380839e5c06b 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -70,6 +70,7 @@ stubs_defaults { "android.hardware.cas-V1.2-java", "android.hardware.health-V1.0-java-constants", "android.hardware.radio-V1.5-java", + "android.hardware.radio-V1.6-java", "android.hardware.thermal-V1.0-java-constants", "android.hardware.thermal-V2.0-java", "android.hardware.tv.input-V1.0-java-constants", @@ -252,21 +253,7 @@ java_library_static { "framework-wifi.stubs", "private-stub-annotations-jar", ], - defaults: [ - "android_defaults_stubs_current", - "android_stubs_dists_default", - ], - dist: { - dir: "apistubs/android/system", - }, - dists: [ - { - // Legacy dist path - targets: ["sdk", "win_sdk"], - tag: ".jar", - dest: "android_system.jar", - }, - ], + defaults: ["android_defaults_stubs_current"], } java_library_static { @@ -285,7 +272,21 @@ java_library_static { "framework-wifi.stubs.system", "private-stub-annotations-jar", ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/system", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_system.jar", + }, + ], } java_library_static { diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index d37dfdeaa583..f77f6c642f6a 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -79,7 +79,6 @@ import android.os.Process; import android.os.RemoteCallback; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -106,6 +105,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.blob.BlobMetadata.Committer; +import com.android.server.pm.UserManagerInternal; import com.android.server.usage.StorageStatsManagerInternal; import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index ca002ec992e2..fe96952d83f9 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -63,7 +63,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.os.WorkSource; import android.provider.DeviceConfig; import android.text.format.DateUtils; @@ -104,6 +103,7 @@ import com.android.server.job.controllers.StorageController; import com.android.server.job.controllers.TimeController; import com.android.server.job.restrictions.JobRestriction; import com.android.server.job.restrictions.ThermalStatusRestriction; +import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import com.android.server.utils.quota.Categorizer; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index 1e7206287566..cc202130ab07 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java @@ -20,10 +20,11 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.os.BasicShellCommandHandler; import android.os.Binder; import android.os.UserHandle; +import com.android.modules.utils.BasicShellCommandHandler; + import java.io.PrintWriter; public final class JobSchedulerShellCommand extends BasicShellCommandHandler { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 0f60eaebd59f..a9037701fa01 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -60,6 +60,7 @@ import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto"; import "frameworks/base/core/proto/android/stats/tls/enums.proto"; +import "frameworks/base/core/proto/android/stats/tv/tif_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; @@ -510,6 +511,7 @@ message Atom { MediaPlaybackTrackChanged media_playback_track_changed = 324; WifiScanReported wifi_scan_reported = 325 [(module) = "wifi"]; WifiPnoScanReported wifi_pno_scan_reported = 326 [(module) = "wifi"]; + TifTuneStateChanged tif_tune_changed = 327 [(module) = "framework"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -3766,6 +3768,7 @@ message AppStartOccurred { WARM = 1; HOT = 2; COLD = 3; + RELAUNCH = 4; } // The transition type. optional TransitionType type = 3; @@ -3827,6 +3830,7 @@ message AppStartCanceled { WARM = 1; HOT = 2; COLD = 3; + RELAUNCH = 4; } // The transition type. optional TransitionType type = 3; @@ -10352,6 +10356,39 @@ message CellBroadcastMessageError { } /** + * Logs when a TV Input Service Session changes tune state + * This is atom ID 327. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java + */ +message TifTuneStateChanged { + + // Tv Input Service uid, TV Player uid + repeated AttributionNode attribution_node = 1 [ + (state_field_option).primary_field_first_uid = true + ]; + optional android.stats.tv.TifTuneState state = 2 [ + (state_field_option).exclusive_state = true, + (state_field_option).default_state_value = 0, + (state_field_option).nested = false + ]; + // This a globally unique 128 bit random number created by TvInputManagerService when + // android.media.tv.TvInputManager#createSession is called. + // It is has no device or user association. + // See android.media.tv.TvInputService.onCreateSession(java.lang.String, java.lang.String) + // WARNING: Any changes to this field should be carefully reviewed for privacy. + // Inspect the code at + // framework/base/cmds/statsd/src/atoms.proto + // TifTuneState + // frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java + // logTuneStateChanged + // BinderService.createSession + // SessionState.sessionId + optional string tif_session_id = 3 [(state_field_option).primary_field = true]; +} + +/** * Logs when a tune occurs through device's Frontend. * This is atom ID 276. * diff --git a/config/preloaded-classes b/config/preloaded-classes index 822b40be811a..bbdb5e9be99e 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -5575,7 +5575,6 @@ android.os.AsyncTask android.os.BadParcelableException android.os.BaseBundle$NoImagePreloadHolder android.os.BaseBundle -android.os.BasicShellCommandHandler android.os.BatteryManager android.os.BatteryManagerInternal android.os.BatteryProperty$1 @@ -11756,6 +11755,7 @@ com.android.net.module.annotation.VisibleForTesting$Visibility com.android.net.module.annotation.VisibleForTesting com.android.net.module.annotation.WorkerThread com.android.net.module.util.InetAddressUtils +com.android.modules.utils.BasicShellCommandHandler com.android.okhttp.Address com.android.okhttp.AndroidShimResponseCache com.android.okhttp.Authenticator diff --git a/core/api/current.txt b/core/api/current.txt index f665512070f5..821717a195a1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10158,6 +10158,7 @@ package android.content { method public abstract void grantUriPermission(String, android.net.Uri, int); method public abstract boolean isDeviceProtectedStorage(); method public boolean isRestricted(); + method public static boolean isUiContext(@NonNull android.content.Context); method public abstract boolean moveDatabaseFrom(android.content.Context, String); method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String); method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]); @@ -35793,6 +35794,7 @@ package android.os { method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectImplicitDirectBoot(); + method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects(); @@ -46595,6 +46597,8 @@ package android.telephony { field public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504; // 0x1f8 field public static final int RESULT_REMOTE_EXCEPTION = 31; // 0x1f field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18 + field public static final int RESULT_RIL_ACCESS_BARRED = 122; // 0x7a + field public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; // 0x7b field public static final int RESULT_RIL_CANCELLED = 119; // 0x77 field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71 @@ -46613,6 +46617,7 @@ package android.telephony { field public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100; // 0x64 field public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114; // 0x72 field public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106; // 0x6a + field public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; // 0x79 field public static final int RESULT_RIL_SIM_ABSENT = 120; // 0x78 field public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101; // 0x65 field public static final int RESULT_RIL_SYSTEM_ERR = 108; // 0x6c @@ -52924,6 +52929,7 @@ package android.view { method public boolean performHapticFeedback(int, int); method public boolean performLongClick(); method public boolean performLongClick(float, float); + method @Nullable public android.view.OnReceiveContentListener.Payload performReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload); method public void playSoundEffect(int); method public boolean post(Runnable); method public boolean postDelayed(Runnable, long); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6cf93fc1a403..a56b39675c36 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2066,6 +2066,10 @@ package android.content.pm { field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR; } + public static class LauncherApps.ShortcutQuery { + field public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800 + } + public class PackageInstaller { method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2 @@ -5027,14 +5031,15 @@ package android.media.tv.tuner { method public void clearResourceLostListener(); method public void close(); method public int connectCiCam(int); + method public int connectFrontendToCiCam(int); method public int disconnectCiCam(); + method public int disconnectFrontendToCiCam(int); method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter); method public long getAvSyncTime(int); method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(); method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo(); method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getFrontendInfoList(); method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]); - method public int linkFrontendToCiCam(int); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler(); method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener); method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener); @@ -5048,7 +5053,6 @@ package android.media.tv.tuner { method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener); method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner); method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings); - method public int unlinkFrontendToCiCam(int); method public void updateResourcePriority(int, int); field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff field public static final int INVALID_FILTER_ID = -1; // 0xffffffff @@ -5249,7 +5253,7 @@ package android.media.tv.tuner.filter { public class Filter implements java.lang.AutoCloseable { method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); - method public int configureScramblingStatusEvent(int); + method public int configureMonitorEvent(int); method public int flush(); method public int getId(); method public long getId64Bit(); @@ -5257,6 +5261,8 @@ package android.media.tv.tuner.filter { method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter); method public int start(); method public int stop(); + field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2 + field public static final int MONITOR_EVENT_SCRAMBLING_STATUS = 1; // 0x1 field public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED = 2; // 0x2 field public static final int SCRAMBLING_STATUS_SCRAMBLED = 4; // 0x4 field public static final int SCRAMBLING_STATUS_UNKNOWN = 1; // 0x1 @@ -5303,6 +5309,10 @@ package android.media.tv.tuner.filter { ctor public FilterEvent(); } + public final class IpCidChangeEvent extends android.media.tv.tuner.filter.FilterEvent { + method public int getIpCid(); + } + public final class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder(); method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress(); @@ -11082,6 +11092,35 @@ package android.telephony.data { field public static final String TYPE_XCAP_STRING = "xcap"; } + public final class ApnThrottleStatus implements android.os.Parcelable { + method public int describeContents(); + method public int getApnType(); + method public int getRetryType(); + method public int getSlotIndex(); + method public long getThrottleExpiryTimeMillis(); + method public int getThrottleType(); + method public int getTransportType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.ApnThrottleStatus> CREATOR; + field public static final int RETRY_TYPE_HANDOVER = 3; // 0x3 + field public static final int RETRY_TYPE_NEW_CONNECTION = 2; // 0x2 + field public static final int RETRY_TYPE_NONE = 1; // 0x1 + field public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; // 0x2 + field public static final int THROTTLE_TYPE_NONE = 1; // 0x1 + } + + public static final class ApnThrottleStatus.Builder { + ctor public ApnThrottleStatus.Builder(); + method @NonNull public android.telephony.data.ApnThrottleStatus build(); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setApnType(int); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setNoThrottle(); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setRetryType(int); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int); + field public static final long NO_THROTTLE_EXPIRY_TIME = -1L; // 0xffffffffffffffffL + } + public final class DataCallResponse implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.net.LinkAddress> getAddresses(); @@ -11199,6 +11238,7 @@ package android.telephony.data { method public abstract void close(); method public void deactivateDataCall(int, int, @Nullable android.telephony.data.DataServiceCallback); method public final int getSlotIndex(); + method public final void notifyApnUnthrottled(@NonNull String); method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>); method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback); method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); @@ -11209,6 +11249,7 @@ package android.telephony.data { } public class DataServiceCallback { + method public void onApnUnthrottled(@NonNull String); method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>); method public void onDeactivateDataCallComplete(int); method public void onHandoverCancelled(int); @@ -11234,6 +11275,7 @@ package android.telephony.data { ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int); method public abstract void close(); method public final int getSlotIndex(); + method public void reportApnThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ApnThrottleStatus>); method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f695adf3e01c..fda7c203226c 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1227,7 +1227,6 @@ package android.os { } public static final class StrictMode.VmPolicy.Builder { - method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse(); method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse(); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 75f7ceca6450..e40247ba2d9a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -67,6 +67,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.provider.Settings; import android.text.BidiFormatter; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -5348,8 +5349,11 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE); big.setTextViewText(R.id.notification_material_reply_text_3, null); + final boolean snoozeEnabled = mContext.getContentResolver() != null + && (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1); big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, - R.dimen.notification_content_margin); + snoozeEnabled ? 0 : R.dimen.notification_content_margin); } private RemoteViews applyStandardTemplateWithActions(int layoutId, int viewType, diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 7fdf06f7233c..da8ac96ef40b 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -36,6 +36,7 @@ import android.util.Log; import android.window.WindowContainerToken; import java.util.ArrayList; +import java.util.Objects; /** * Stores information about a particular Task. @@ -288,6 +289,36 @@ public class TaskInfo { } /** + * Returns {@code true} if parameters that are important for task organizers have changed + * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners + * about that. + * @hide + */ + public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) { + if (that == null) { + return false; + } + return topActivityType == that.topActivityType + && isResizeable == that.isResizeable + && Objects.equals(positionInParent, that.positionInParent) + && equalsLetterboxParams(that) + && pictureInPictureParams == that.pictureInPictureParams + && getWindowingMode() == that.getWindowingMode() + && Objects.equals(taskDescription, that.taskDescription); + } + + private boolean equalsLetterboxParams(TaskInfo that) { + return Objects.equals(letterboxActivityBounds, that.letterboxActivityBounds) + && Objects.equals( + getConfiguration().windowConfiguration.getBounds(), + that.getConfiguration().windowConfiguration.getBounds()) + && Objects.equals( + getConfiguration().windowConfiguration.getMaxBounds(), + that.getConfiguration().windowConfiguration.getMaxBounds()) + && Objects.equals(parentBounds, that.parentBounds); + } + + /** * Reads the TaskInfo from a parcel. */ void readFromParcel(Parcel source) { diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java index d65be9bded4f..77891e0fd007 100644 --- a/core/java/android/app/WaitResult.java +++ b/core/java/android/app/WaitResult.java @@ -40,14 +40,21 @@ public class WaitResult implements Parcelable { */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"LAUNCH_STATE_"}, value = { + LAUNCH_STATE_UNKNOWN, LAUNCH_STATE_COLD, LAUNCH_STATE_WARM, - LAUNCH_STATE_HOT + LAUNCH_STATE_HOT, + LAUNCH_STATE_RELAUNCH }) public @interface LaunchState { } /** + * Not considered as a launch event, e.g. the activity is already on top. + */ + public static final int LAUNCH_STATE_UNKNOWN = 0; + + /** * Cold launch sequence: a new process has started. */ public static final int LAUNCH_STATE_COLD = 1; @@ -62,6 +69,13 @@ public class WaitResult implements Parcelable { */ public static final int LAUNCH_STATE_HOT = 3; + /** + * Relaunch launch sequence: process reused, but activity has to be destroyed and created. + * E.g. the current device configuration is different from the background activity that will be + * brought to foreground, and the activity doesn't declare to handle the change. + */ + public static final int LAUNCH_STATE_RELAUNCH = 4; + public static final int INVALID_DELAY = -1; public int result; public boolean timeout; @@ -124,6 +138,8 @@ public class WaitResult implements Parcelable { return "WARM"; case LAUNCH_STATE_HOT: return "HOT"; + case LAUNCH_STATE_RELAUNCH: + return "RELAUNCH"; default: return "UNKNOWN (" + type + ")"; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 37e0a44bdd95..6a3f6b4034ff 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -63,6 +63,7 @@ import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.StatFs; +import android.os.StrictMode; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -6285,4 +6286,25 @@ public abstract class Context { public boolean isUiContext() { throw new RuntimeException("Not implemented. Must override in a subclass."); } + + /** + * Returns {@code true} if the context is a UI context which can access UI components such as + * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or + * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI + * contexts throws {@link android.os.strictmode.Violation} if + * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. + * <p> + * Examples of UI contexts are + * an {@link android.app.Activity Activity}, a context created from + * {@link #createWindowContext(int, Bundle)} or + * {@link android.inputmethodservice.InputMethodService InputMethodService} + * </p> + * + * @see #getDisplay() + * @see #getSystemService(String) + * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse() + */ + public static boolean isUiContext(@NonNull Context context) { + return context.isUiContext(); + } } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index c964b4b9bb53..4b7bbbfa9d20 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -433,6 +433,16 @@ public class LauncherApps { */ public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2; + /** + * Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}. + * + * <p>The caller must have the system {@code ACCESS_SHORTCUTS} permission. + * + * @hide + */ + @SystemApi + public static final int FLAG_GET_PERSONS_DATA = 1 << 11; + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_MATCH_DYNAMIC, @@ -440,6 +450,7 @@ public class LauncherApps { FLAG_MATCH_MANIFEST, FLAG_MATCH_CACHED, FLAG_GET_KEY_FIELDS_ONLY, + FLAG_GET_PERSONS_DATA, }) @Retention(RetentionPolicy.SOURCE) public @interface QueryFlags {} diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index f806b565b127..7c2636c52be8 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -1802,21 +1802,27 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" OwnerUid: ").append(mOwnerUid); } - if (mAdministratorUids.length == 0) { - sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids)); + if (!ArrayUtils.isEmpty(mAdministratorUids)) { + sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids)); + } + + if (mRequestorUid != Process.INVALID_UID) { + sb.append(" RequestorUid: ").append(mRequestorUid); + } + + if (mRequestorPackageName != null) { + sb.append(" RequestorPkg: ").append(mRequestorPackageName); } if (null != mSSID) { sb.append(" SSID: ").append(mSSID); } + if (mPrivateDnsBroken) { - sb.append(" Private DNS is broken"); + sb.append(" PrivateDnsBroken"); } - sb.append(" RequestorUid: ").append(mRequestorUid); - sb.append(" RequestorPackageName: ").append(mRequestorPackageName); - sb.append("]"); return sb.toString(); } diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java deleted file mode 100644 index 366da3db0010..000000000000 --- a/core/java/android/os/BasicShellCommandHandler.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2019 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.os; - -import android.util.Log; - -import java.io.BufferedInputStream; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; - -/** - * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. This is meant to - * be copied into mainline modules, so this class must not use any hidden APIs. - * - * @hide - */ -public abstract class BasicShellCommandHandler { - static final String TAG = "ShellCommand"; - static final boolean DEBUG = false; - - private Binder mTarget; - private FileDescriptor mIn; - private FileDescriptor mOut; - private FileDescriptor mErr; - private String[] mArgs; - - private String mCmd; - private int mArgPos; - private String mCurArgData; - - private FileInputStream mFileIn; - private FileOutputStream mFileOut; - private FileOutputStream mFileErr; - - private PrintWriter mOutPrintWriter; - private PrintWriter mErrPrintWriter; - private InputStream mInputStream; - - public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args, int firstArgPos) { - mTarget = target; - mIn = in; - mOut = out; - mErr = err; - mArgs = args; - mCmd = null; - mArgPos = firstArgPos; - mCurArgData = null; - mFileIn = null; - mFileOut = null; - mFileErr = null; - mOutPrintWriter = null; - mErrPrintWriter = null; - mInputStream = null; - } - - public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args) { - String cmd; - int start; - if (args != null && args.length > 0) { - cmd = args[0]; - start = 1; - } else { - cmd = null; - start = 0; - } - init(target, in, out, err, args, start); - mCmd = cmd; - - if (DEBUG) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Log.d(TAG, "Starting command " + mCmd + " on " + mTarget, here); - Log.d(TAG, "Calling uid=" + Binder.getCallingUid() - + " pid=" + Binder.getCallingPid()); - } - int res = -1; - try { - res = onCommand(mCmd); - if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget); - } catch (Throwable e) { - // Unlike usual calls, in this case if an exception gets thrown - // back to us we want to print it back in to the dump data, since - // that is where the caller expects all interesting information to - // go. - PrintWriter eout = getErrPrintWriter(); - eout.println(); - eout.println("Exception occurred while executing '" + mCmd + "':"); - e.printStackTrace(eout); - } finally { - if (DEBUG) Log.d(TAG, "Flushing output streams on " + mTarget); - if (mOutPrintWriter != null) { - mOutPrintWriter.flush(); - } - if (mErrPrintWriter != null) { - mErrPrintWriter.flush(); - } - if (DEBUG) Log.d(TAG, "Sending command result on " + mTarget); - } - if (DEBUG) Log.d(TAG, "Finished command " + mCmd + " on " + mTarget); - return res; - } - - /** - * Return the raw FileDescriptor for the output stream. - */ - public FileDescriptor getOutFileDescriptor() { - return mOut; - } - - /** - * Return direct raw access (not buffered) to the command's output data stream. - */ - public OutputStream getRawOutputStream() { - if (mFileOut == null) { - mFileOut = new FileOutputStream(mOut); - } - return mFileOut; - } - - /** - * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}. - */ - public PrintWriter getOutPrintWriter() { - if (mOutPrintWriter == null) { - mOutPrintWriter = new PrintWriter(getRawOutputStream()); - } - return mOutPrintWriter; - } - - /** - * Return the raw FileDescriptor for the error stream. - */ - public FileDescriptor getErrFileDescriptor() { - return mErr; - } - - /** - * Return direct raw access (not buffered) to the command's error output data stream. - */ - public OutputStream getRawErrorStream() { - if (mFileErr == null) { - mFileErr = new FileOutputStream(mErr); - } - return mFileErr; - } - - /** - * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}. - */ - public PrintWriter getErrPrintWriter() { - if (mErr == null) { - return getOutPrintWriter(); - } - if (mErrPrintWriter == null) { - mErrPrintWriter = new PrintWriter(getRawErrorStream()); - } - return mErrPrintWriter; - } - - /** - * Return the raw FileDescriptor for the input stream. - */ - public FileDescriptor getInFileDescriptor() { - return mIn; - } - - /** - * Return direct raw access (not buffered) to the command's input data stream. - */ - public InputStream getRawInputStream() { - if (mFileIn == null) { - mFileIn = new FileInputStream(mIn); - } - return mFileIn; - } - - /** - * Return buffered access to the command's {@link #getRawInputStream()}. - */ - public InputStream getBufferedInputStream() { - if (mInputStream == null) { - mInputStream = new BufferedInputStream(getRawInputStream()); - } - return mInputStream; - } - - /** - * Return the next option on the command line -- that is an argument that - * starts with '-'. If the next argument is not an option, null is returned. - */ - public String getNextOption() { - if (mCurArgData != null) { - String prev = mArgs[mArgPos - 1]; - throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); - } - if (mArgPos >= mArgs.length) { - return null; - } - String arg = mArgs[mArgPos]; - if (!arg.startsWith("-")) { - return null; - } - mArgPos++; - if (arg.equals("--")) { - return null; - } - if (arg.length() > 1 && arg.charAt(1) != '-') { - if (arg.length() > 2) { - mCurArgData = arg.substring(2); - return arg.substring(0, 2); - } else { - mCurArgData = null; - return arg; - } - } - mCurArgData = null; - return arg; - } - - /** - * Return the next argument on the command line, whatever it is; if there are - * no arguments left, return null. - */ - public String getNextArg() { - if (mCurArgData != null) { - String arg = mCurArgData; - mCurArgData = null; - return arg; - } else if (mArgPos < mArgs.length) { - return mArgs[mArgPos++]; - } else { - return null; - } - } - - public String peekNextArg() { - if (mCurArgData != null) { - return mCurArgData; - } else if (mArgPos < mArgs.length) { - return mArgs[mArgPos]; - } else { - return null; - } - } - - /** - * @return all the remaining arguments in the command without moving the current position. - */ - public String[] peekRemainingArgs() { - int remaining = getRemainingArgsCount(); - String[] args = new String[remaining]; - for (int pos = mArgPos; pos < mArgs.length; pos++) { - args[pos - mArgPos] = mArgs[pos]; - } - return args; - } - - /** - * Returns number of arguments that haven't been processed yet. - */ - public int getRemainingArgsCount() { - if (mArgPos >= mArgs.length) { - return 0; - } - return mArgs.length - mArgPos; - } - - /** - * Return the next argument on the command line, whatever it is; if there are - * no arguments left, throws an IllegalArgumentException to report this to the user. - */ - public String getNextArgRequired() { - String arg = getNextArg(); - if (arg == null) { - String prev = mArgs[mArgPos - 1]; - throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); - } - return arg; - } - - public int handleDefaultCommands(String cmd) { - if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) { - onHelp(); - } else { - getOutPrintWriter().println("Unknown command: " + cmd); - } - return -1; - } - - public Binder getTarget() { - return mTarget; - } - - public String[] getAllArgs() { - return mArgs; - } - - /** - * Implement parsing and execution of a command. If it isn't a command you understand, - * call {@link #handleDefaultCommands(String)} and return its result as a last resort. - * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()} - * to process additional command line arguments. Command output can be written to - * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}. - * - * <p class="caution">Note that no permission checking has been done before entering this - * function, so you need to be sure to do your own security verification for any commands you - * are executing. The easiest way to do this is to have the ShellCommand contain - * only a reference to your service's aidl interface, and do all of your command - * implementations on top of that -- that way you can rely entirely on your executing security - * code behind that interface.</p> - * - * @param cmd The first command line argument representing the name of the command to execute. - * @return Return the command result; generally 0 or positive indicates success and - * negative values indicate error. - */ - public abstract int onCommand(String cmd); - - /** - * Implement this to print help text about your command to {@link #getOutPrintWriter()}. - */ - public abstract void onHelp(); -} diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index 3358ce13ed52..a2173a6d2360 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -19,15 +19,9 @@ package android.os; import android.compat.annotation.UnsupportedAppUsage; import android.util.Slog; -import com.android.internal.util.FastPrintWriter; +import com.android.modules.utils.BasicShellCommandHandler; -import java.io.BufferedInputStream; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; /** * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index c89adadfbf2d..086180e7ead4 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -85,8 +85,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; @@ -1045,22 +1043,22 @@ public final class StrictMode { /** * Detect attempts to invoke a method on a {@link Context} that is not suited for such * operation. - * <p>An example of this is trying to obtain an instance of visual service (e.g. + * <p>An example of this is trying to obtain an instance of UI service (e.g. * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and * therefore can report incorrect metrics or resources. * @see Context#getDisplay() * @see Context#getSystemService(String) - * @hide */ - @TestApi public @NonNull Builder detectIncorrectContextUse() { return enable(DETECT_VM_INCORRECT_CONTEXT_USE); } /** * Disable detection of incorrect context use. - * TODO(b/149790106): Fix usages and remove. + * + * @see #detectIncorrectContextUse() + * * @hide */ @TestApi diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 97c9f4bbb4ba..714bcea9c01f 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -457,6 +457,20 @@ public final class DeviceConfig { */ public static final String NAMESPACE_CONFIGURATION = "configuration"; + /** + * LatencyTracker properties definitions. + * + * @hide + */ + public static final String NAMESPACE_LATENCY_TRACKER = "latency_tracker"; + + /** + * InteractionJankMonitor properties definitions. + * + * @hide + */ + public static final String NAMESPACE_INTERACTION_JANK_MONITOR = "interaction_jank_monitor"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS index 8b7d6ad851f9..97e015646df0 100644 --- a/core/java/android/provider/OWNERS +++ b/core/java/android/provider/OWNERS @@ -1,4 +1,5 @@ per-file DeviceConfig.java = svetoslavganov@google.com per-file DeviceConfig.java = hackbod@google.com +per-file DeviceConfig.java = schfan@google.com diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3e48168eabe0..884f8ccd5e54 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8412,6 +8412,14 @@ public final class Settings { "emergency_gesture_sound_enabled"; /** + * The default number to call in emergency gesture + * + * @hide + */ + public static final String EMERGENCY_GESTURE_CALL_NUMBER = + "emergency_gesture_call_number"; + + /** * Whether the camera launch gesture to double tap the power button when the screen is off * should be disabled. * @@ -14576,19 +14584,6 @@ public final class Settings { */ public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = "maximum_obscuring_opacity_for_touch"; - - /** - * LatencyTracker settings. - * - * The following strings are supported as keys: - * <pre> - * enabled (boolean) - * sampling_interval (int) - * </pre> - * - * @hide - */ - public static final String LATENCY_TRACKER = "latency_tracker"; } /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 649c8f353196..83cab0a0d3de 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5169,6 +5169,14 @@ public final class Telephony { public static final String COLUMN_IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled"; /** + * TelephonyProvider column name for determining if the user has enabled cross SIM calling + * for this subscription. + * + * @hide + */ + public static final String COLUMN_CROSS_SIM_CALLING_ENABLED = "cross_sim_calling_enabled"; + + /** * TelephonyProvider column name for whether a subscription is opportunistic, that is, * whether the network it connects to is limited in functionality or coverage. * For example, CBRS. diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java index 4919f5d8856f..923ab7a65d1b 100644 --- a/core/java/android/service/attestation/ImpressionAttestationService.java +++ b/core/java/android/service/attestation/ImpressionAttestationService.java @@ -71,6 +71,14 @@ public abstract class ImpressionAttestationService extends Service { public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.attestation.available_algorithms"; + /** + * The {@link Intent} action that must be declared as handled by a service in its manifest + * for the system to recognize it as an impression attestation providing service. + * @hide + */ + public static final String SERVICE_INTERFACE = + "android.service.attestation.ImpressionAttestationService"; + private ImpressionAttestationServiceWrapper mWrapper; private Handler mHandler; diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/uwb/AngleOfArrivalSupport.aidl new file mode 100644 index 000000000000..57666ff8bca9 --- /dev/null +++ b/core/java/android/uwb/AngleOfArrivalSupport.aidl @@ -0,0 +1,44 @@ +/* + * 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.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum AngleOfArrivalSupport { + /** + * The device does not support angle of arrival + */ + NONE, + + /** + * The device supports planar angle of arrival + */ + TWO_DIMENSIONAL, + + /** + * The device does supports three dimensional angle of arrival with hemispherical azimuth angles + */ + THREE_DIMENSIONAL_HEMISPHERICAL, + + /** + * The device does supports three dimensional angle of arrival with full azimuth angles + */ + THREE_DIMENSIONAL_SPHERICAL, +} + diff --git a/core/java/android/uwb/CloseReason.aidl b/core/java/android/uwb/CloseReason.aidl new file mode 100644 index 000000000000..bef129e2c1c7 --- /dev/null +++ b/core/java/android/uwb/CloseReason.aidl @@ -0,0 +1,58 @@ +/* + * 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.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum CloseReason { + /** + * Unknown reason + */ + UNKNOWN, + + /** + * A local API call triggered the close, such as a call to + * IUwbAdapter.stopRanging. + */ + LOCAL_API, + + /** + * The maximum number of sessions has been reached. This error may be generated + * for an active session if a higher priority session begins. + */ + MAX_SESSIONS_REACHED, + + /** + * The system state has changed resulting in the session ending (e.g. the user + * disables UWB, or the user's locale changes and an active channel is no longer + * permitted to be used). + */ + SYSTEM_POLICY, + + /** + * The remote device has requested to terminate the session + */ + REMOTE_REQUEST, + + /** + * The session was closed for a protocol specific reason + */ + PROTOCOL_SPECIFIC, +} + diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl new file mode 100644 index 000000000000..d29ed34804f1 --- /dev/null +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -0,0 +1,170 @@ +/* + * 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.uwb; + +import android.os.PersistableBundle; +import android.uwb.AngleOfArrivalSupport; +import android.uwb.IUwbAdapterStateCallbacks; +import android.uwb.IUwbRangingCallbacks; +import android.uwb.SessionHandle; + +/** + * @hide + */ +interface IUwbAdapter { + /* + * Register the callbacks used to notify the framework of events and data + * + * The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged + * function must be called immediately following registration with the current + * state of the UWB adapter. + * + * @param callbacks callback to provide range and status updates to the framework + */ + void registerAdapterStateCallbacks(in IUwbAdapterStateCallbacks adapterStateCallbacks); + + /* + * Unregister the callbacks used to notify the framework of events and data + * + * Calling this function with an unregistered callback is a no-op + * + * @param callbacks callback to unregister + */ + void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks); + + /** + * Returns true if ranging is supported, false otherwise + */ + boolean isRangingSupported(); + + /** + * Get the angle of arrival supported by this device + * + * @return the angle of arrival type supported + */ + AngleOfArrivalSupport getAngleOfArrivalSupport(); + + /** + * Generates a list of the supported 802.15.4z channels + * + * The list must be prioritized in the order of preferred channel usage. + * + * The list must only contain channels that are permitted to be used in the + * device's current location. + * + * @return an array of support channels on the device for the current location. + */ + int[] getSupportedChannels(); + + /** + * Generates a list of the supported 802.15.4z preamble codes + * + * The list must be prioritized in the order of preferred preamble usage. + * + * The list must only contain preambles that are permitted to be used in the + * device's current location. + * + * @return an array of supported preambles on the device for the current + * location. + */ + int[] getSupportedPreambleCodes(); + + /** + * Get the accuracy of the ranging timestamps + * + * @return accuracy of the ranging timestamps in nanoseconds + */ + long getTimestampResolutionNanos(); + + /** + * Get the supported number of simultaneous ranging sessions + * + * @return the supported number of simultaneous ranging sessions + */ + int getMaxSimultaneousSessions(); + + /** + * Get the maximum number of remote devices per session + * + * @return the maximum number of remote devices supported in a single session + */ + int getMaxRemoteDevicesPerSession(); + + /** + * Provides the capabilities and features of the device + * + * @return specification specific capabilities and features of the device + */ + PersistableBundle getSpecificationInfo(); + + /** + * Request to start a new ranging session + * + * This function must return before calling IUwbAdapterCallbacks + * #onRangingStarted, #onRangingClosed, or #onRangingResult. + * + * A ranging session does not need to be started before returning. + * + * IUwbAdapterCallbacks#onRangingStarted must be called within + * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called + * if the ranging session is scheduled to start successfully. + * + * IUwbAdapterCallbacks#onRangingStartFailed must be called within + * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called + * if the ranging session fails to be scheduled to start successfully. + * + * @param rangingCallbacks the callbacks used to deliver ranging information + * @param parameters the configuration to use for ranging + * @return a SessionHandle used to identify this ranging request + */ + SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks, + in PersistableBundle parameters); + + /** + * Stop and close ranging for the session associated with the given handle + * + * Calling with an invalid handle or a handle that has already been closed + * is a no-op. + * + * IUwbAdapterCallbacks#onRangingClosed must be called within + * RANGING_SESSION_CLOSE_THRESHOLD_MS of #stopRanging being called. + * + * @param sessionHandle the session handle to stop ranging for + */ + void closeRanging(in SessionHandle sessionHandle); + + /** + * The maximum allowed time to start a ranging session. + */ + const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD + + /** + * The maximum allowed time to notify the framework that a session has been + * closed. + */ + const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD + + /** + * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY + */ + const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416; + + /** + * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY + */ + const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1; +} diff --git a/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl new file mode 100644 index 000000000000..d928eabae465 --- /dev/null +++ b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl @@ -0,0 +1,32 @@ +/* + * 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.uwb; + +import android.uwb.StateChangeReason; + +/** + * @hide + */ +interface IUwbAdapterStateCallbacks { + /** + * Called whenever the adapter state changes + * + * @param isEnabled true if the adapter is enabled, false otherwise + * @param reason the reason that the state has changed + */ + void onAdapterStateChanged(boolean isEnabled, StateChangeReason reason); +}
\ No newline at end of file diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl new file mode 100644 index 000000000000..1fc3bfd818c3 --- /dev/null +++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl @@ -0,0 +1,73 @@ +/* + * 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.uwb; + +import android.os.PersistableBundle; +import android.uwb.CloseReason; +import android.uwb.RangingReport; +import android.uwb.SessionHandle; +import android.uwb.StartFailureReason; + +/** + * @hide + */ +interface IUwbRangingCallbacks { + /** + * Called when ranging has started + * + * May output parameters generated by the lower layers that must be sent to the + * remote device(s). The PersistableBundle must be constructed using the UWB + * support library. + * + * @param sessionHandle the session the callback is being invoked for + * @param rangingOutputParameters parameters generated by the lower layer that + * should be sent to the remote device. + */ + void onRangingStarted(in SessionHandle sessionHandle, + in PersistableBundle parameters); + + /** + * Called when a ranging session fails to start + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session failed to start + * @param parameters protocol specific parameters + */ + void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason, + in PersistableBundle parameters); + /** + * Called when a ranging session is closed + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session was closed + * @param parameters protocol specific parameters + */ + void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason, + in PersistableBundle parameters); + + /** + * Provides a new RangingResult to the framework + * + * The reported timestamp for a ranging measurement must be calculated as the + * time which the ranging round that generated this measurement concluded. + * + * @param sessionHandle an identifier to associate the ranging results with a + * session that is active + * @param result the ranging report + */ + void onRangingResult(in SessionHandle sessionHandle, in RangingReport result); +} diff --git a/core/java/android/uwb/MeasurementStatus.aidl b/core/java/android/uwb/MeasurementStatus.aidl new file mode 100644 index 000000000000..5fa15549e84d --- /dev/null +++ b/core/java/android/uwb/MeasurementStatus.aidl @@ -0,0 +1,39 @@ +/* + * 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.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum MeasurementStatus { + /** + * Ranging was successful + */ + SUCCESS, + + /** + * The remote device is out of range + */ + FAILURE_OUT_OF_RANGE, + + /** + * An unknown failure has occurred. + */ + FAILURE_UNKNOWN, +} + diff --git a/core/java/android/uwb/RangingReport.aidl b/core/java/android/uwb/RangingReport.aidl new file mode 100644 index 000000000000..c32747ae58da --- /dev/null +++ b/core/java/android/uwb/RangingReport.aidl @@ -0,0 +1,19 @@ +/* + * 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.uwb; + +parcelable RangingReport; diff --git a/core/java/android/uwb/SessionHandle.aidl b/core/java/android/uwb/SessionHandle.aidl new file mode 100644 index 000000000000..58a7dbb2ef9f --- /dev/null +++ b/core/java/android/uwb/SessionHandle.aidl @@ -0,0 +1,19 @@ +/* + * 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.uwb; + +parcelable SessionHandle; diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java new file mode 100644 index 000000000000..928fcbdcf1c7 --- /dev/null +++ b/core/java/android/uwb/SessionHandle.java @@ -0,0 +1,79 @@ +/* + * 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.uwb; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @hide + */ +public final class SessionHandle implements Parcelable { + private final int mId; + + public SessionHandle(int id) { + mId = id; + } + + protected SessionHandle(Parcel in) { + mId = in.readInt(); + } + + public static final Creator<SessionHandle> CREATOR = new Creator<SessionHandle>() { + @Override + public SessionHandle createFromParcel(Parcel in) { + return new SessionHandle(in); + } + + @Override + public SessionHandle[] newArray(int size) { + return new SessionHandle[size]; + } + }; + + public int getId() { + return mId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof SessionHandle) { + SessionHandle other = (SessionHandle) obj; + return mId == other.mId; + } + return false; + } + + @Override + public String toString() { + return "SessionHandle [id=" + mId + "]"; + } +} diff --git a/core/java/android/uwb/StartFailureReason.aidl b/core/java/android/uwb/StartFailureReason.aidl new file mode 100644 index 000000000000..4d9c962f529b --- /dev/null +++ b/core/java/android/uwb/StartFailureReason.aidl @@ -0,0 +1,52 @@ +/* + * 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.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum StartFailureReason { + /** + * Unknown start failure reason + */ + UNKNOWN, + + /** + * The provided parameters were invalid and ranging could not start + */ + BAD_PARAMETERS, + + /** + * The maximum number of sessions has been reached. This error may be generated + * for an active session if a higher priority session begins. + */ + MAX_SESSIONS_REACHED, + + /** + * The system state has changed resulting in the session ending (e.g. the user + * disables UWB, or the user's locale changes and an active channel is no longer + * permitted to be used). + */ + SYSTEM_POLICY, + + /** + * The session could not start because of a protocol specific reason. + */ + PROTOCOL_SPECIFIC, +} + diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl new file mode 100644 index 000000000000..46a6e2edfa22 --- /dev/null +++ b/core/java/android/uwb/StateChangeReason.aidl @@ -0,0 +1,45 @@ +/* + * 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.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum StateChangeReason { + /** + * The state changed for an unknown reason + */ + UNKNOWN, + + /** + * The adapter state changed because a session started. + */ + SESSION_STARTED, + + + /** + * The adapter state changed because all sessions were closed. + */ + ALL_SESSIONS_CLOSED, + + /** + * The adapter state changed because of a device system change. + */ + SYSTEM_POLICY, +} + diff --git a/core/java/android/uwb/UwbAddress.aidl b/core/java/android/uwb/UwbAddress.aidl new file mode 100644 index 000000000000..a202b1a1f51d --- /dev/null +++ b/core/java/android/uwb/UwbAddress.aidl @@ -0,0 +1,19 @@ +/* + * 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.uwb; + +parcelable UwbAddress; diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java index 495528989a83..db9c53863c7f 100644 --- a/core/java/android/view/OnReceiveContentListener.java +++ b/core/java/android/view/OnReceiveContentListener.java @@ -48,7 +48,7 @@ import java.util.function.Predicate; * public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; * * @Override - * public Payload onReceiveContent(TextView view, Payload payload) { + * public Payload onReceiveContent(View view, Payload payload) { * Map<Boolean, Payload> split = payload.partition(item -> item.getUri() != null); * if (split.get(true) != null) { * ClipData clip = payload.getClip(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a88ad9f24c10..30ec2b050dbe 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9008,7 +9008,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Sets the listener to be {@link #onReceiveContent used} to handle insertion of + * Sets the listener to be {@link #performReceiveContent used} to handle insertion of * content into this view. * * <p>Depending on the type of view, this listener may be invoked for different scenarios. For @@ -9039,7 +9039,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * not be null or empty if a non-null listener is passed in. * @param listener The listener to use. This can be null to reset to the default behavior. */ - @SuppressWarnings("rawtypes") public void setOnReceiveContentListener(@Nullable String[] mimeTypes, @Nullable OnReceiveContentListener listener) { if (listener != null) { @@ -9055,27 +9054,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Receives the given content. Invokes the listener configured via - * {@link #setOnReceiveContentListener}; if no listener is set, the default implementation is a - * no-op (returns the passed-in content without acting on it). + * Receives the given content. If no listener is set, invokes {@link #onReceiveContent}. If a + * listener is {@link #setOnReceiveContentListener set}, invokes the listener instead; if the + * listener returns a non-null result, invokes {@link #onReceiveContent} to handle it. * * @param payload The content to insert and related metadata. * * @return The portion of the passed-in content that was not accepted (may be all, some, or none * of the passed-in content). */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public @Nullable Payload onReceiveContent(@NonNull Payload payload) { + public @Nullable Payload performReceiveContent(@NonNull Payload payload) { final OnReceiveContentListener listener = (mListenerInfo == null) ? null : getListenerInfo().mOnReceiveContentListener; if (listener != null) { - return listener.onReceiveContent(this, payload); + final Payload remaining = listener.onReceiveContent(this, payload); + return (remaining == null) ? null : onReceiveContent(remaining); } + return onReceiveContent(payload); + } + + /** + * Implements the default behavior for receiving content for this type of view. The default + * view implementation is a no-op (returns the passed-in content without acting on it). + * + * <p>Widgets should override this method to define their default behavior for receiving + * content. Apps should {@link #setOnReceiveContentListener set a listener} to provide + * app-specific handling for receiving content. + * + * <p>See {@link #setOnReceiveContentListener} and {@link #performReceiveContent} for more info. + * + * @param payload The content to insert and related metadata. + * + * @return The portion of the passed-in content that was not handled (may be all, some, or none + * of the passed-in content). + */ + public @Nullable Payload onReceiveContent(@NonNull Payload payload) { return payload; } /** - * Returns the MIME types accepted by {@link #onReceiveContent} for this view, as + * Returns the MIME types accepted by {@link #performReceiveContent} for this view, as * configured via {@link #setOnReceiveContentListener}. By default returns null. * * <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft @@ -9092,7 +9110,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to * lowercase. * - * @return The MIME types accepted by {@link #onReceiveContent} for this view (may + * @return The MIME types accepted by {@link #performReceiveContent} for this view (may * include patterns such as "image/*"). */ public @Nullable String[] getOnReceiveContentMimeTypes() { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 72f76d1a9dc9..436acef17c4d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -410,6 +410,13 @@ public interface WindowManager extends ViewManager { int TRANSIT_FLAG_APP_CRASHED = 0x10; /** + * Transition flag: A window in a new task is being opened behind an existing one in another + * activity's task. + * @hide + */ + int TRANSIT_FLAG_OPEN_BEHIND = 0x20; + + /** * @hide */ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { @@ -417,7 +424,8 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION, TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER, TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION, - TRANSIT_FLAG_APP_CRASHED + TRANSIT_FLAG_APP_CRASHED, + TRANSIT_FLAG_OPEN_BEHIND }) @Retention(RetentionPolicy.SOURCE) @interface TransitionFlags {} diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 299c41b02b23..8fdcac7b4f9d 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2372,7 +2372,7 @@ public final class AutofillManager { return; } Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build(); - Payload result = view.onReceiveContent(payload); + Payload result = view.performReceiveContent(payload); if (result != null) { Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id + ", view=" + view + ", clip=" + clip); diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index a92d1f589e96..6a2a8d35a007 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -927,9 +927,9 @@ public class BaseInputConnection implements InputConnection { } /** - * Default implementation which invokes {@link View#onReceiveContent} on the target view if the - * view {@link View#getOnReceiveContentMimeTypes allows} content insertion; otherwise returns - * false without any side effects. + * Default implementation which invokes {@link View#performReceiveContent} on the target + * view if the view {@link View#getOnReceiveContentMimeTypes allows} content insertion; + * otherwise returns false without any side effects. */ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { ClipDescription description = inputContentInfo.getDescription(); @@ -954,6 +954,6 @@ public class BaseInputConnection implements InputConnection { .setLinkUri(inputContentInfo.getLinkUri()) .setExtras(opts) .build(); - return mTargetView.onReceiveContent(payload) == null; + return mTargetView.performReceiveContent(payload) == null; } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index da14f2cfbd5a..00ba326d2ba9 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -2872,7 +2872,7 @@ public class Editor { final OnReceiveContentListener.Payload payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP) .build(); - mTextView.onReceiveContent(payload); + mTextView.performReceiveContent(payload); if (dragDropIntoItself) { deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index fb13807495e6..98f808784803 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2152,7 +2152,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (isTextEditable()) { ClipData clip = ClipData.newPlainText("", result); Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build(); - onReceiveContent(payload); + performReceiveContent(payload); if (mEditor != null) { mEditor.refreshTextActionMode(); } @@ -11858,7 +11858,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build(); - onReceiveContent(payload); + performReceiveContent(payload); } @Override @@ -12927,7 +12927,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD) .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT) .build(); - onReceiveContent(payload); + performReceiveContent(payload); sLastCutCopyOrTextChangedTime = 0; } @@ -13728,17 +13728,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Receives the given content. Clients wishing to provide custom behavior should configure a - * listener via {@link #setOnReceiveContentListener}. + * Default {@link TextView} implementation for receiving content. Apps wishing to provide + * custom behavior should configure a listener via {@link #setOnReceiveContentListener}. * - * <p>If a listener is set, invokes the listener. If the listener returns a non-null result, - * executes the default platform handling for the portion of the content returned by the - * listener. - * - * <p>If no listener is set, executes the default platform behavior. For non-editable TextViews - * the default behavior is a no-op (returns the passed-in content without acting on it). For - * editable TextViews the default behavior coerces all content to text and inserts into the - * view. + * <p>For non-editable TextViews the default behavior is a no-op (returns the passed-in + * content without acting on it). For editable TextViews the default behavior coerces all + * content to text and inserts into the view. * * @param payload The content to insert and related metadata. * @@ -13747,11 +13742,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @Override public @Nullable Payload onReceiveContent(@NonNull Payload payload) { - Payload remaining = super.onReceiveContent(payload); - if (remaining != null && mEditor != null) { - return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, remaining); + if (mEditor != null) { + return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, payload); } - return remaining; + return payload; } private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) { diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index c0d46f632500..571ac1bbeac6 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -37,34 +37,33 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai //TODO (163431584): need also consider other refresh rates. private static final long JANK_THRESHOLD_NANOS = 1000000000 / 60; private static final long UNKNOWN_TIMESTAMP = -1; + public static final int NANOS_IN_MILLISECOND = 1_000_000; private final HardwareRendererObserver mObserver; + private final int mTraceThresholdMissedFrames; + private final int mTraceThresholdFrameTimeMillis; private final ThreadedRendererWrapper mRendererWrapper; private final FrameMetricsWrapper mMetricsWrapper; private long mBeginTime = UNKNOWN_TIMESTAMP; private long mEndTime = UNKNOWN_TIMESTAMP; - private boolean mShouldTriggerTrace; private boolean mSessionEnd; + private boolean mCancelled = false; private int mTotalFramesCount = 0; private int mMissedFramesCount = 0; private long mMaxFrameTimeNanos = 0; private Session mSession; - /** - * Constructor of FrameTracker. - * @param session a trace session. - * @param handler a handler for handling callbacks. - * @param renderer a ThreadedRendererWrapper instance. - * @param metrics a FrameMetricsWrapper instance. - */ public FrameTracker(@NonNull Session session, @NonNull Handler handler, - @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) { + @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics, + int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis) { mSession = session; mRendererWrapper = renderer; mMetricsWrapper = metrics; mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); + mTraceThresholdMissedFrames = traceThresholdMissedFrames; + mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis; } /** @@ -109,13 +108,15 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } Trace.endAsyncSection(mSession.getName(), (int) mBeginTime); mRendererWrapper.removeObserver(mObserver); - mBeginTime = UNKNOWN_TIMESTAMP; - mEndTime = UNKNOWN_TIMESTAMP; - mShouldTriggerTrace = false; + mCancelled = true; } @Override public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { + if (mCancelled) { + return; + } + // Since this callback might come a little bit late after the end() call. // We should keep tracking the begin / end timestamp. // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ. @@ -136,13 +137,14 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames", mTotalFramesCount); Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis", - (int) (mMaxFrameTimeNanos / 1_000_000)); + (int) (mMaxFrameTimeNanos / NANOS_IN_MILLISECOND)); // Trigger perfetto if necessary. - if (mShouldTriggerTrace) { - if (DEBUG) { - Log.v(TAG, "Found janky frame, triggering perfetto."); - } + boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1 + && mMissedFramesCount >= mTraceThresholdMissedFrames; + boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1 + && mMaxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND; + if (overMissedFramesThreshold || overFrameTimeThreshold) { triggerPerfetto(); } if (mSession.logToStatsd()) { @@ -168,7 +170,6 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai if (isJankyFrame) { mMissedFramesCount += 1; - mShouldTriggerTrace = true; } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index fcaa963feda2..771a72c98a7a 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -36,7 +36,10 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import android.annotation.IntDef; import android.annotation.NonNull; +import android.os.Build; +import android.os.HandlerExecutor; import android.os.HandlerThread; +import android.provider.DeviceConfig; import android.util.Log; import android.util.SparseArray; import android.view.View; @@ -47,6 +50,7 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; /** @@ -55,9 +59,21 @@ import java.util.concurrent.TimeUnit; */ public class InteractionJankMonitor { private static final String TAG = InteractionJankMonitor.class.getSimpleName(); - private static final boolean DEBUG = false; private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L); + private static final String SETTINGS_ENABLED_KEY = "enabled"; + private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; + private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY = + "trace_threshold_missed_frames"; + private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY = + "trace_threshold_frame_time_millis"; + /** Default to being enabled on debug builds. */ + private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE; + /** Default to collecting data for all CUJs. */ + private static final int DEFAULT_SAMPLING_INTERVAL = 1; + /** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */ + private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3; + private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64; // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; @@ -106,12 +122,20 @@ public class InteractionJankMonitor { private static volatile InteractionJankMonitor sInstance; + private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = + this::updateProperties; + private ThreadedRendererWrapper mRenderer; private FrameMetricsWrapper mMetrics; private SparseArray<FrameTracker> mRunningTrackers; private SparseArray<Runnable> mTimeoutActions; private HandlerThread mWorker; + private boolean mInitialized; + private boolean mEnabled = DEFAULT_ENABLED; + private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; + private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES; + private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS; /** @hide */ @IntDef({ @@ -134,10 +158,12 @@ public class InteractionJankMonitor { CUJ_NOTIFICATION_APP_START, }) @Retention(RetentionPolicy.SOURCE) - public @interface CujType {} + public @interface CujType { + } /** * Get the singleton of InteractionJankMonitor. + * * @return instance of InteractionJankMonitor */ public static InteractionJankMonitor getInstance() { @@ -154,6 +180,7 @@ public class InteractionJankMonitor { /** * This constructor should be only public to tests. + * * @param worker the worker thread for the callbacks */ @VisibleForTesting @@ -165,11 +192,11 @@ public class InteractionJankMonitor { /** * Init InteractionJankMonitor for later instrumentation. + * * @param view Any view in the view tree to get context and ThreadedRenderer. * @return boolean true if the instance has been initialized successfully. */ public boolean init(@NonNull View view) { - //TODO (163505250): This should be no-op if not in droid food rom. if (!mInitialized) { synchronized (this) { if (!mInitialized) { @@ -180,7 +207,20 @@ public class InteractionJankMonitor { mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer()); mMetrics = new FrameMetricsWrapper(); mWorker.start(); + mEnabled = DEFAULT_ENABLED; + mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; mInitialized = true; + + // Post initialization to the background in case we're running on the main + // thread. + mWorker.getThreadHandler().post( + () -> mPropertiesChangedListener.onPropertiesChanged( + DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR))); + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, + new HandlerExecutor(mWorker.getThreadHandler()), + mPropertiesChangedListener); } } } @@ -189,6 +229,7 @@ public class InteractionJankMonitor { /** * Create a {@link FrameTracker} instance. + * * @param session the session associates with this tracker * @return instance of the FrameTracker */ @@ -196,47 +237,50 @@ public class InteractionJankMonitor { public FrameTracker createFrameTracker(Session session) { synchronized (this) { if (!mInitialized) return null; - return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics); + return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics, + mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis); } } /** * Begin a trace session, must invoke {@link #init(View)} before invoking this method. + * * @param cujType the specific {@link InteractionJankMonitor.CujType}. * @return boolean true if the tracker is started successfully, false otherwise. */ public boolean begin(@CujType int cujType) { - //TODO (163505250): This should be no-op if not in droid food rom. synchronized (this) { - return begin(cujType, 0L /* timeout */); + return begin(cujType, DEFAULT_TIMEOUT_MS); } } /** * Begin a trace session, must invoke {@link #init(View)} before invoking this method. + * * @param cujType the specific {@link InteractionJankMonitor.CujType}. * @param timeout the elapsed time in ms until firing the timeout action. * @return boolean true if the tracker is started successfully, false otherwise. */ public boolean begin(@CujType int cujType, long timeout) { - //TODO (163505250): This should be no-op if not in droid food rom. synchronized (this) { if (!mInitialized) { Log.d(TAG, "Not initialized!", new Throwable()); return false; } - Session session = new Session(cujType); - FrameTracker tracker = getTracker(session); + boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + if (!mEnabled || !shouldSample) { + return false; + } + FrameTracker tracker = getTracker(cujType); // Skip subsequent calls if we already have an ongoing tracing. if (tracker != null) return false; // begin a new trace session. - tracker = createFrameTracker(session); + tracker = createFrameTracker(new Session(cujType)); mRunningTrackers.put(cujType, tracker); tracker.begin(); // Cancel the trace if we don't get an end() call in specified duration. - timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS; Runnable timeoutAction = () -> cancel(cujType); mTimeoutActions.put(cujType, timeoutAction); mWorker.getThreadHandler().postDelayed(timeoutAction, timeout); @@ -246,6 +290,7 @@ public class InteractionJankMonitor { /** * End a trace session, must invoke {@link #init(View)} before invoking this method. + * * @param cujType the specific {@link InteractionJankMonitor.CujType}. * @return boolean true if the tracker is ended successfully, false otherwise. */ @@ -263,18 +308,18 @@ public class InteractionJankMonitor { mTimeoutActions.remove(cujType); } - Session session = new Session(cujType); - FrameTracker tracker = getTracker(session); + FrameTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. if (tracker == null) return false; tracker.end(); - mRunningTrackers.remove(session.getCuj()); + mRunningTrackers.remove(cujType); return true; } } /** * Cancel the trace session, must invoke {@link #init(View)} before invoking this method. + * * @return boolean true if the tracker is cancelled successfully, false otherwise. */ public boolean cancel(@CujType int cujType) { @@ -291,48 +336,38 @@ public class InteractionJankMonitor { mTimeoutActions.remove(cujType); } - Session session = new Session(cujType); - FrameTracker tracker = getTracker(session); + FrameTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. if (tracker == null) return false; tracker.cancel(); - mRunningTrackers.remove(session.getCuj()); + mRunningTrackers.remove(cujType); return true; } } - private void destroy() { + private FrameTracker getTracker(@CujType int cuj) { synchronized (this) { - int trackers = mRunningTrackers.size(); - for (int i = 0; i < trackers; i++) { - mRunningTrackers.valueAt(i).cancel(); - } - mRunningTrackers = null; - mTimeoutActions.clear(); - mTimeoutActions = null; - mWorker.quit(); - mWorker = null; + if (!mInitialized) return null; + return mRunningTrackers.get(cuj); } } - /** - * Abandon current instance. - */ - @VisibleForTesting - public static void abandon() { - if (sInstance == null) return; - synchronized (InteractionJankMonitor.class) { - if (sInstance == null) return; - sInstance.destroy(); - sInstance = null; + private void updateProperties(DeviceConfig.Properties properties) { + synchronized (this) { + mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, + DEFAULT_SAMPLING_INTERVAL); + mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); + mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, + DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); + mTraceThresholdFrameTimeMillis = properties.getInt( + SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, + DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); } } - private FrameTracker getTracker(Session session) { - synchronized (this) { - if (!mInitialized) return null; - return mRunningTrackers.get(session.getCuj()); - } + @VisibleForTesting + public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() { + return mPropertiesChangedListener; } /** @@ -402,12 +437,14 @@ public class InteractionJankMonitor { * A class to represent a session. */ public static class Session { - private @CujType int mCujType; + @CujType + private int mCujType; public Session(@CujType int cujType) { mCujType = cujType; } + @CujType public int getCuj() { return mCujType; } diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 41be5c493d95..2237efc9e2b6 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -57,6 +57,7 @@ public class SystemNotificationChannels { public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP"; public static String SYSTEM_CHANGES = "SYSTEM_CHANGES"; public static String DO_NOT_DISTURB = "DO_NOT_DISTURB"; + public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION"; public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); @@ -191,6 +192,13 @@ public class SystemNotificationChannels { NotificationManager.IMPORTANCE_LOW); channelsList.add(dndChanges); + final NotificationChannel newFeaturePrompt = new NotificationChannel( + ACCESSIBILITY_MAGNIFICATION, + context.getString(R.string.notification_channel_accessibility_magnification), + NotificationManager.IMPORTANCE_HIGH); + newFeaturePrompt.setBlockable(true); + channelsList.add(newFeaturePrompt); + nm.createNotificationChannels(channelsList); } diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java index c110b263e781..c85b5d7aa7a6 100644 --- a/core/java/com/android/internal/os/BaseCommand.java +++ b/core/java/com/android/internal/os/BaseCommand.java @@ -18,9 +18,10 @@ package com.android.internal.os; import android.compat.annotation.UnsupportedAppUsage; -import android.os.BasicShellCommandHandler; import android.os.Build; +import com.android.modules.utils.BasicShellCommandHandler; + import java.io.PrintStream; public abstract class BaseCommand { diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 70b1ad49d8b8..aa42979ed53c 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -56,6 +56,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000; public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false; public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true; + public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false; public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500; private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_"; @@ -95,6 +96,7 @@ public class BinderCallsStats implements BinderInternal.Observer { private boolean mAddDebugEntries = false; private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID; private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE; + private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS; private CachedDeviceState.Readonly mDeviceState; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; @@ -185,8 +187,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public CallSession callStarted(Binder binder, int code, int workSourceUid) { noteNativeThreadId(); - if (!mRecordingAllTransactionsForUid - && (mDeviceState == null || mDeviceState.isCharging())) { + if (!canCollect()) { return null; } @@ -255,8 +256,7 @@ public class BinderCallsStats implements BinderInternal.Observer { synchronized (mLock) { // This was already checked in #callStart but check again while synchronized. - if (!mRecordingAllTransactionsForUid - && (mDeviceState == null || mDeviceState.isCharging())) { + if (!canCollect()) { return; } @@ -372,6 +372,22 @@ public class BinderCallsStats implements BinderInternal.Observer { mCallStatsObserver.noteBinderThreadNativeIds(getNativeTids()); } + private boolean canCollect() { + if (mRecordingAllTransactionsForUid) { + return true; + } + if (mIgnoreBatteryStatus) { + return true; + } + if (mDeviceState == null) { + return false; + } + if (mDeviceState.isCharging()) { + return false; + } + return true; + } + /** * This method is expensive to call. */ @@ -672,6 +688,18 @@ public class BinderCallsStats implements BinderInternal.Observer { } /** + * Whether to ignore battery status when collecting stats + */ + public void setIgnoreBatteryStatus(boolean ignored) { + synchronized (mLock) { + if (ignored != mIgnoreBatteryStatus) { + mIgnoreBatteryStatus = ignored; + reset(); + } + } + } + + /** * Marks the specified work source UID for total binder call tracking: detailed information * will be recorded for all calls from this source ID. * diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index 932ff572219f..2805dccffe50 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -40,6 +40,7 @@ public class LooperStats implements Looper.Observer { public static final String DEBUG_ENTRY_PREFIX = "__DEBUG_"; private static final int SESSION_POOL_SIZE = 50; private static final boolean DISABLED_SCREEN_STATE_TRACKING_VALUE = false; + public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false; @GuardedBy("mLock") private final SparseArray<Entry> mEntries = new SparseArray<>(512); @@ -56,6 +57,7 @@ public class LooperStats implements Looper.Observer { private long mStartElapsedTime = SystemClock.elapsedRealtime(); private boolean mAddDebugEntries = false; private boolean mTrackScreenInteractive = false; + private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS; public LooperStats(int samplingInterval, int entriesSizeCap) { this.mSamplingInterval = samplingInterval; @@ -139,8 +141,16 @@ public class LooperStats implements Looper.Observer { } private boolean deviceStateAllowsCollection() { - // Do not collect data if on charger or the state is not set. - return mDeviceState != null && !mDeviceState.isCharging(); + if (mIgnoreBatteryStatus) { + return true; + } + if (mDeviceState == null) { + return false; + } + if (mDeviceState.isCharging()) { + return false; + } + return true; } /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */ @@ -225,6 +235,10 @@ public class LooperStats implements Looper.Observer { mTrackScreenInteractive = enabled; } + public void setIgnoreBatteryStatus(boolean ignore) { + mIgnoreBatteryStatus = ignore; + } + @Nullable private Entry findEntry(Message msg, boolean allowCreateNew) { final boolean isInteractive = mTrackScreenInteractive diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index a87e8aaa0e64..8012540ed314 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -15,15 +15,11 @@ package com.android.internal.util; import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; import android.os.Build; import android.os.SystemClock; import android.os.Trace; -import android.os.UserHandle; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.util.EventLog; -import android.util.KeyValueListParser; import android.util.Log; import android.util.SparseLongArray; @@ -135,8 +131,16 @@ public class LatencyTracker { mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; // Post initialization to the background in case we're running on the main thread. - BackgroundThread.getHandler().post(this::registerSettingsObserver); - BackgroundThread.getHandler().post(this::readSettings); + BackgroundThread.getHandler().post(() -> this.updateProperties( + DeviceConfig.getProperties(DeviceConfig.NAMESPACE_LATENCY_TRACKER))); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LATENCY_TRACKER, + BackgroundThread.getExecutor(), this::updateProperties); + } + + private void updateProperties(DeviceConfig.Properties properties) { + mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, + DEFAULT_SAMPLING_INTERVAL); + mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); } /** @@ -171,28 +175,6 @@ public class LatencyTracker { } } - private void registerSettingsObserver() { - Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER); - mContext.getContentResolver().registerContentObserver( - settingsUri, false, new SettingsObserver(this), UserHandle.myUserId()); - } - - private void readSettings() { - KeyValueListParser parser = new KeyValueListParser(','); - String settingsValue = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.LATENCY_TRACKER); - - try { - parser.setString(settingsValue); - mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, - DEFAULT_SAMPLING_INTERVAL); - mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Incorrect settings format", e); - mEnabled = false; - } - } - public static boolean isEnabled(Context ctx) { return getInstance(ctx).isEnabled(); } @@ -236,8 +218,8 @@ public class LatencyTracker { /** * Logs an action that has started and ended. This needs to be called from the main thread. * - * @param action The action to end. One of the ACTION_* values. - * @param duration The duration of the action in ms. + * @param action The action to end. One of the ACTION_* values. + * @param duration The duration of the action in ms. */ public void logAction(int action, int duration) { boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; @@ -260,18 +242,4 @@ public class LatencyTracker { FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration); } } - - private static class SettingsObserver extends ContentObserver { - private final LatencyTracker mThisTracker; - - SettingsObserver(LatencyTracker thisTracker) { - super(BackgroundThread.getHandler()); - mThisTracker = thisTracker; - } - - @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - mThisTracker.readSettings(); - } - } } diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index 48f33a6a3d77..781895eeeaba 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -20,6 +20,8 @@ #include "android_os_HwBinder.h" +#include "android_util_Binder.h" // for binder_report_exception + #include "android_os_HwParcel.h" #include "android_os_HwRemoteBinder.h" @@ -183,15 +185,7 @@ status_t JHwBinder::onTransact( env->ExceptionDescribe(); env->ExceptionClear(); - // It is illegal to call IsInstanceOf if there is a pending exception. - // Attempting to do so results in a JniAbort which crashes the entire process. - if (env->IsInstanceOf(excep, gErrorClass)) { - /* It's an error */ - LOG(ERROR) << "Forcefully exiting"; - _exit(1); - } else { - LOG(ERROR) << "Uncaught exception!"; - } + binder_report_exception(env, excep, "Uncaught error or exception in hwbinder!"); env->DeleteLocalRef(excep); } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index a3cb4c076593..7c9b8625c8ee 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -304,8 +304,9 @@ static void report_java_lang_error(JNIEnv* env, jthrowable error, const char* ms report_java_lang_error_fatal_error(env, error, msg); } -static void report_exception(JNIEnv* env, jthrowable excep, const char* msg) -{ +namespace android { + +void binder_report_exception(JNIEnv* env, jthrowable excep, const char* msg) { env->ExceptionClear(); ScopedLocalRef<jstring> tagstr(env, env->NewStringUTF(LOG_TAG)); @@ -333,6 +334,8 @@ static void report_exception(JNIEnv* env, jthrowable excep, const char* msg) } } +} // namespace android + class JavaBBinderHolder; class JavaBBinder : public BBinder @@ -407,9 +410,9 @@ protected: if (env->ExceptionCheck()) { ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred()); - report_exception(env, excep.get(), - "*** Uncaught remote exception! " - "(Exceptions are not yet supported across processes.)"); + binder_report_exception(env, excep.get(), + "*** Uncaught remote exception! " + "(Exceptions are not yet supported across processes.)"); res = JNI_FALSE; } @@ -423,8 +426,8 @@ protected: if (env->ExceptionCheck()) { ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred()); - report_exception(env, excep.get(), - "*** Uncaught exception in onBinderStrictModePolicyChange"); + binder_report_exception(env, excep.get(), + "*** Uncaught exception in onBinderStrictModePolicyChange"); } // Need to always call through the native implementation of @@ -569,8 +572,8 @@ public: jBinderProxy.get()); if (env->ExceptionCheck()) { jthrowable excep = env->ExceptionOccurred(); - report_exception(env, excep, - "*** Uncaught exception returned from death notification!"); + binder_report_exception(env, excep, + "*** Uncaught exception returned from death notification!"); } // Serialize with our containing DeathRecipientList so that we can't @@ -1163,8 +1166,8 @@ static void android_os_BinderInternal_proxyLimitcallback(int uid) if (env->ExceptionCheck()) { ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred()); - report_exception(env, excep.get(), - "*** Uncaught exception in binderProxyLimitCallbackFromNative"); + binder_report_exception(env, excep.get(), + "*** Uncaught exception in binderProxyLimitCallbackFromNative"); } } diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h index c109d6c265c3..9098d46ee29c 100644 --- a/core/jni/android_util_Binder.h +++ b/core/jni/android_util_Binder.h @@ -35,6 +35,8 @@ extern void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy); extern void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, bool canThrowRemoteException = false, int parcelSize = 0); +// does not take ownership of the exception, aborts if this is an error +void binder_report_exception(JNIEnv* env, jthrowable excep, const char* msg); } #endif diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 6ffafc16b89c..3156f71fa113 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -78,7 +78,6 @@ #include <android-base/unique_fd.h> #include <bionic/malloc.h> #include <bionic/mte.h> -#include <bionic/mte_kernel.h> #include <cutils/fs.h> #include <cutils/memory.h> #include <cutils/multiuser.h> @@ -1650,28 +1649,6 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list, } } -#ifdef ANDROID_EXPERIMENTAL_MTE -static void SetTagCheckingLevel(int level) { -#ifdef __aarch64__ - if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) { - return; - } - - int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); - if (tagged_addr_ctrl < 0) { - ALOGE("prctl(PR_GET_TAGGED_ADDR_CTRL) failed: %s", strerror(errno)); - return; - } - - tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level; - if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) { - ALOGE("prctl(PR_SET_TAGGED_ADDR_CTRL, %d) failed: %s", tagged_addr_ctrl, - strerror(errno)); - } -#endif -} -#endif - // Utility routine to specialize a zygote child process. static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, @@ -1807,22 +1784,14 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI; break; case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC: -#ifdef ANDROID_EXPERIMENTAL_MTE - SetTagCheckingLevel(PR_MTE_TCF_ASYNC); -#endif heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC; break; case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC: -#ifdef ANDROID_EXPERIMENTAL_MTE - SetTagCheckingLevel(PR_MTE_TCF_SYNC); -#endif heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC; break; default: -#ifdef ANDROID_EXPERIMENTAL_MTE - SetTagCheckingLevel(PR_MTE_TCF_NONE); -#endif heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE; + break; } android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level)); // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime. diff --git a/core/proto/android/stats/tv/tif_enums.proto b/core/proto/android/stats/tv/tif_enums.proto new file mode 100644 index 000000000000..a9028e529186 --- /dev/null +++ b/core/proto/android/stats/tv/tif_enums.proto @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.stats.tv; +option java_multiple_files = true; + +// Enums for TV Input Framework +option java_outer_classname = "TifStatsEnums"; + +// Tune State of a TV Input Service Framework +enum TifTuneState { + TIF_TUNE_STATE_UNKNOWN = 0; + CREATED = 1; + SURFACE_ATTACHED = 2; + SURFACE_DETACHED = 3; + RELEASED = 4; + TUNE_STARTED = 5; + VIDEO_AVAILABLE = 6; + + // Keep in sync with TvInputManager + // Use the TvInputManager value + 100 + VIDEO_UNAVAILABLE_REASON_UNKNOWN = 100; + VIDEO_UNAVAILABLE_REASON_TUNING = 101; + VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 102; + VIDEO_UNAVAILABLE_REASON_BUFFERING = 103; + VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 104; + VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 105; + VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 106; + VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION=107; + VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED=108; + VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE=109; + VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED=110; + VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION=111; + VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING=112; + VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD=113; + VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE=114; + VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID=115; + VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT=116; + VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING=117; + VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN=118; +} diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8eb0853fda24..be6b6b15fe4d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -717,6 +717,10 @@ [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6665375982962336520] --> <string name="notification_channel_foreground_service">Apps consuming battery</string> + <!-- Text shown when viewing channel settings for notifications related to accessibility + magnification. [CHAR_LIMIT=NONE]--> + <string name="notification_channel_accessibility_magnification">Magnification</string> + <!-- Label for foreground service notification when one app is running. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] --> <string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is @@ -5767,4 +5771,16 @@ ul.</string> <string name="config_pdp_reject_service_not_subscribed"></string> <!-- pdp data reject dialog string for cause 55 (MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED) [CHAR LIMIT=100] --> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string> + + <!-- Window magnification prompt related string. --> + + <!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] --> + <string name="window_magnification_prompt_title">New: Window Magnifier</string> + <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] --> + <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string> + <!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] --> + <string name="turn_on_magnification_settings_action">Turn on in Settings</string> + <!-- Notification action to dismiss. [CHAR LIMIT=50] --> + <string name="dismiss_action">Dismiss</string> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 23733af4455e..752bb5b37a30 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3458,6 +3458,7 @@ <java-symbol type="string" name="notification_channel_heavy_weight_app" /> <java-symbol type="string" name="notification_channel_system_changes" /> <java-symbol type="string" name="notification_channel_do_not_disturb" /> + <java-symbol type="string" name="notification_channel_accessibility_magnification" /> <java-symbol type="string" name="config_defaultAutofillService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> @@ -4096,4 +4097,10 @@ <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> + + <!-- Window magnification prompt --> + <java-symbol type="string" name="window_magnification_prompt_title" /> + <java-symbol type="string" name="window_magnification_prompt_content" /> + <java-symbol type="string" name="turn_on_magnification_settings_action" /> + <java-symbol type="string" name="dismiss_action" /> </resources> diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index e17800f7b6fd..2402420e1bc1 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -77,7 +77,7 @@ public class FrameTrackerTest { Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); mTracker = Mockito.spy( - new FrameTracker(session, handler, mRenderer, mWrapper)); + new FrameTracker(session, handler, mRenderer, mWrapper, 1, -1)); doNothing().when(mTracker).triggerPerfetto(); } diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index a9cfd286688b..474cb1d7ed96 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -32,6 +33,7 @@ import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import android.provider.DeviceConfig; import android.view.View; import android.view.ViewAttachTestActivity; @@ -50,6 +52,7 @@ import org.mockito.ArgumentCaptor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; @@ -71,8 +74,6 @@ public class InteractionJankMonitorTest { mView = mActivity.getWindow().getDecorView(); assertThat(mView.isAttachedToWindow()).isTrue(); - InteractionJankMonitor.abandon(); - Handler handler = spy(new Handler(mActivity.getMainLooper())); doReturn(true).when(handler).sendMessageAtTime(any(), anyLong()); mWorker = spy(new HandlerThread("Interaction-jank-monitor-test")); @@ -93,7 +94,7 @@ public class InteractionJankMonitorTest { Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), new ThreadedRendererWrapper(mView.getThreadedRenderer()), - new FrameMetricsWrapper())); + new FrameMetricsWrapper(), 1, -1)); doReturn(tracker).when(monitor).createFrameTracker(any()); // Simulate a trace session and see if begin / end are invoked. @@ -104,6 +105,21 @@ public class InteractionJankMonitorTest { } @Test + public void testDisabledThroughDeviceConfig() { + InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); + monitor.init(mView); + + HashMap<String, String> propertiesValues = new HashMap<>(); + propertiesValues.put("enabled", "false"); + DeviceConfig.Properties properties = new DeviceConfig.Properties( + DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, propertiesValues); + monitor.getPropertiesChangedListener().onPropertiesChanged(properties); + + assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + } + + @Test public void testCheckInitState() { InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); @@ -134,12 +150,13 @@ public class InteractionJankMonitorTest { Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), new ThreadedRendererWrapper(mView.getThreadedRenderer()), - new FrameMetricsWrapper())); + new FrameMetricsWrapper(), 1, -1)); doReturn(tracker).when(monitor).createFrameTracker(any()); assertThat(monitor.begin(session.getCuj())).isTrue(); verify(tracker).begin(); - verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong()); + verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(), + anyLong()); Runnable runnable = captor.getValue().getCallback(); assertThat(runnable).isNotNull(); mWorker.getThreadHandler().removeCallbacks(runnable); 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 3117935fb3ed..561c549676da 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -46,7 +46,6 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Random; @@ -428,6 +427,19 @@ public class BinderCallsStatsTest { } @Test + public void testIgnoreBatteryStatusFlag() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + mDeviceState.setCharging(true); + bcs.setIgnoreBatteryStatus(true); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + assertEquals(1, bcs.getExportedCallStats().size()); + } + + @Test public void testScreenOff() { TestBinderCallsStats bcs = new TestBinderCallsStats(); bcs.setDetailedTracking(true); diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 7917a06cb9b7..fdfc7ac5587c 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -322,6 +322,23 @@ public final class LooperStatsTest { } @Test + public void testDataCollectedIfIgnoreBatteryStatusFlagSet() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + mDeviceState.setCharging(true); + looperStats.setIgnoreBatteryStatus(true); + + Object token1 = looperStats.messageDispatchStarting(); + looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000)); + Object token2 = looperStats.messageDispatchStarting(); + looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000), + new IllegalArgumentException()); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(1); + + } + + @Test public void testScreenStateCollected() { TestableLooperStats looperStats = new TestableLooperStats(1, 100); diff --git a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java new file mode 100644 index 000000000000..8b42ff7f62a7 --- /dev/null +++ b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java @@ -0,0 +1,52 @@ +/* + * 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.uwb; + +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test of {@link SessionHandle}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SessionHandleTest { + + @Test + public void testBasic() { + int handleId = 12; + SessionHandle handle = new SessionHandle(handleId); + assertEquals(handle.getId(), handleId); + } + + @Test + public void testParcel() { + Parcel parcel = Parcel.obtain(); + SessionHandle handle = new SessionHandle(10); + handle.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel); + assertEquals(handle, fromParcel); + } +} diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index fe1182ecad4f..e473c55ce010 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -56,5 +56,7 @@ <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> + <permission name="android.permission.READ_DREAM_STATE"/> + <permission name="android.permission.READ_DREAM_SUPPRESSION"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index eacb6295f96f..52da707565d7 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -241,12 +241,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, - "-1847087163": { - "message": "TRANSIT_TASK_OPEN_BEHIND, adding %s to mOpeningApps", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-1844540996": { "message": " Initial targets: %s", "level": "VERBOSE", @@ -271,6 +265,12 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "-1800899273": { + "message": "applyAnimation: anim=%s transit=%s Callers=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/AppTransition.java" + }, "-1792633344": { "message": "Register task organizer=%s uid=%d", "level": "VERBOSE", @@ -913,12 +913,6 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/WindowState.java" }, - "-1044506655": { - "message": "New transit away from wallpaper: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "-1042574499": { "message": "Attempted to add Accessibility overlay window with unknown token %s. Aborting.", "level": "WARN", @@ -985,12 +979,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-928291778": { - "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, "-927199900": { "message": "Updating global configuration to: %s", "level": "INFO", @@ -1117,12 +1105,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-793346159": { - "message": "New transit into wallpaper: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "-784959154": { "message": "Attempted to add private presentation window to a non-private display. Aborting.", "level": "WARN", @@ -1195,6 +1177,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-701167286": { + "message": "applyAnimation: transit=%s, enter=%b, wc=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "-694710814": { "message": "Pausing rotation during drag", "level": "DEBUG", @@ -1597,6 +1585,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-240296576": { + "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/AppTransitionController.java" + }, "-235225312": { "message": "Skipping config check for initializing activity: %s", "level": "VERBOSE", @@ -1657,12 +1651,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-129722369": { - "message": "New transit: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "-118786523": { "message": "Resume failed; resetting state to %s: %s", "level": "VERBOSE", @@ -3289,6 +3277,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "1810019902": { + "message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps", + "level": "DEBUG", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1822843721": { "message": "Aborted starting %s: startingData=%s", "level": "VERBOSE", @@ -3433,12 +3427,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "2016061474": { - "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, "2018454757": { "message": "WS.removeImmediately: %s Already removed...", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/drawable/TEST_MAPPING b/graphics/java/android/graphics/drawable/TEST_MAPPING new file mode 100644 index 000000000000..1018702e01c5 --- /dev/null +++ b/graphics/java/android/graphics/drawable/TEST_MAPPING @@ -0,0 +1,24 @@ +{ + "presubmit": [ + { + + "name": "CtsGraphicsTestCases", + "file_patterns": ["(/|^)Icon\\.java"], + "options" : [ + { + "include-filter": "android.graphics.drawable.cts.IconTest" + } + ] + }, + { + + "name": "FrameworksCoreTests", + "file_patterns": ["(/|^)Icon\\.java"], + "options" : [ + { + "include-filter": "android.graphics.drawable.IconTest" + } + ] + } + ] +} diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java index 9af15a5f4a16..49a48871fd30 100644 --- a/keystore/java/android/security/KeyStoreOperation.java +++ b/keystore/java/android/security/KeyStoreOperation.java @@ -17,11 +17,11 @@ package android.security; import android.annotation.NonNull; +import android.hardware.keymint.KeyParameter; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keymaster.KeymasterDefs; import android.system.keystore2.IKeystoreOperation; -import android.system.keystore2.KeyParameter; import android.system.keystore2.ResponseCode; import android.util.Log; diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index 9d3b62278ba0..7c3de8bee475 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -18,6 +18,7 @@ package android.security; import android.annotation.NonNull; import android.app.compat.CompatChanges; +import android.hardware.keymint.KeyParameter; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keystore.BackendBusyException; @@ -27,7 +28,6 @@ import android.system.keystore2.CreateOperationResponse; import android.system.keystore2.IKeystoreSecurityLevel; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyMetadata; -import android.system.keystore2.KeyParameter; import android.system.keystore2.ResponseCode; import android.util.Log; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 3ac9d68d5a9f..5867ef6eaea5 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -84,7 +84,7 @@ public class AndroidKeyStoreProvider extends Provider { // java.security.KeyStore put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi"); - put("alg.alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore"); + put("Alg.Alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore"); // java.security.KeyPairGenerator put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java index 70713a47ad6d..69c7a2589d6f 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java @@ -17,10 +17,10 @@ package android.security.keystore2; import android.annotation.NonNull; +import android.hardware.keymint.KeyParameter; import android.security.keymaster.KeymasterDefs; import android.security.keystore.ArrayUtils; import android.security.keystore.KeyProperties; -import android.system.keystore2.KeyParameter; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java index dd094b7a5fd0..2b5f6c31607b 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java @@ -18,13 +18,13 @@ package android.security.keystore2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.keymint.KeyParameter; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.ArrayUtils; import android.security.keystore.KeyProperties; import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream; -import android.system.keystore2.KeyParameter; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index b785ee5c6966..18d26922f1ae 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -19,11 +19,11 @@ package android.security.keystore2; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.keymint.KeyParameter; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyStoreCryptoOperation; -import android.system.keystore2.KeyParameter; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java index 9f7f2383a416..2250c89aac41 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java @@ -17,12 +17,12 @@ package android.security.keystore2; import android.annotation.NonNull; +import android.hardware.keymint.KeyParameter; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.system.keystore2.Authorization; -import android.system.keystore2.KeyParameter; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java index 3dde2e592259..eea45c287622 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java @@ -16,12 +16,12 @@ package android.security.keystore2; +import android.hardware.keymint.KeyParameter; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyStoreCryptoOperation; import android.security.keystore.KeymasterUtils; -import android.system.keystore2.KeyParameter; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java index ccd0a4bf92ff..479fd8a6a73a 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java @@ -16,6 +16,8 @@ package android.security.keystore2; +import android.hardware.keymint.KeyParameter; +import android.hardware.keymint.SecurityLevel; import android.security.KeyStore2; import android.security.KeyStoreSecurityLevel; import android.security.keymaster.KeymasterArguments; @@ -29,8 +31,6 @@ import android.system.keystore2.Domain; import android.system.keystore2.IKeystoreSecurityLevel; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyMetadata; -import android.system.keystore2.KeyParameter; -import android.system.keystore2.SecurityLevel; import android.util.Log; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index a747a0e727d8..61725e3e8c24 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -18,6 +18,8 @@ package android.security.keystore2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.keymint.KeyParameter; +import android.hardware.keymint.SecurityLevel; import android.os.Build; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore2; @@ -35,9 +37,7 @@ import android.system.keystore2.Domain; import android.system.keystore2.IKeystoreSecurityLevel; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyMetadata; -import android.system.keystore2.KeyParameter; import android.system.keystore2.ResponseCode; -import android.system.keystore2.SecurityLevel; import android.util.Log; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java index a6ea9723db24..2686ddc20c1d 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java @@ -18,11 +18,11 @@ package android.security.keystore2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.keymint.KeyParameter; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.security.keystore.KeymasterUtils; import android.system.keystore2.Authorization; -import android.system.keystore2.KeyParameter; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java index 5f1b9c0586a1..444dad4cffbe 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java @@ -17,9 +17,9 @@ package android.security.keystore2; import android.annotation.NonNull; +import android.hardware.keymint.KeyParameter; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; -import android.system.keystore2.KeyParameter; import java.security.InvalidKeyException; import java.security.SignatureSpi; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java index 55414b70d403..a168f8feb3db 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java @@ -18,12 +18,12 @@ package android.security.keystore2; import android.annotation.CallSuper; import android.annotation.NonNull; +import android.hardware.keymint.KeyParameter; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.ArrayUtils; import android.security.keystore.KeyStoreCryptoOperation; -import android.system.keystore2.KeyParameter; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 4c26864cb02b..9790a4ae5b65 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -18,6 +18,9 @@ package android.security.keystore2; import android.annotation.NonNull; import android.hardware.biometrics.BiometricManager; +import android.hardware.keymint.HardwareAuthenticatorType; +import android.hardware.keymint.KeyParameter; +import android.hardware.keymint.SecurityLevel; import android.security.GateKeeper; import android.security.KeyStore2; import android.security.KeyStoreParameter; @@ -36,9 +39,7 @@ import android.system.keystore2.IKeystoreSecurityLevel; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyEntryResponse; import android.system.keystore2.KeyMetadata; -import android.system.keystore2.KeyParameter; import android.system.keystore2.ResponseCode; -import android.system.keystore2.SecurityLevel; import android.util.Log; import java.io.ByteArrayInputStream; @@ -871,16 +872,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>(); AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec(); - // TODO Replace with HardwareAuthenticatorType.PASSWORD when KeyMint AIDL spec has landed. - authenticatorSpec.authenticatorType = 1; // HardwareAuthenticatorType.PASSWORD + authenticatorSpec.authenticatorType = HardwareAuthenticatorType.PASSWORD; authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId(); authenticatorSpecs.add(authenticatorSpec); for (long sid : biometricSids) { AuthenticatorSpec authSpec = new AuthenticatorSpec(); - // TODO Replace with HardwareAuthenticatorType.FINGERPRINT when KeyMint AIDL spec has - // landed. - authSpec.authenticatorType = 2; // HardwareAuthenticatorType.FINGERPRINT + authSpec.authenticatorType = HardwareAuthenticatorType.FINGERPRINT; authSpec.authenticatorId = sid; authenticatorSpecs.add(authSpec); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java index 3d5a8f63e7f9..a2d4528b99fd 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java @@ -18,10 +18,10 @@ package android.security.keystore2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.keymint.KeyParameter; import android.security.keymaster.KeymasterDefs; import android.security.keystore.ArrayUtils; import android.security.keystore.KeyProperties; -import android.system.keystore2.KeyParameter; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java index ee67ed3f76d8..8fa532b6e188 100644 --- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java +++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java @@ -18,13 +18,13 @@ package android.security.keystore2; import android.annotation.NonNull; import android.hardware.biometrics.BiometricManager; +import android.hardware.keymint.KeyParameter; +import android.hardware.keymint.SecurityLevel; import android.security.GateKeeper; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.security.keystore.UserAuthArgs; import android.system.keystore2.Authorization; -import android.system.keystore2.KeyParameter; -import android.system.keystore2.SecurityLevel; import java.security.ProviderException; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index beac59b699fa..aa7355b61eda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -89,6 +89,7 @@ public class BubbleController implements Bubbles { // TODO(b/173386799) keep in sync with Launcher3 and also don't do a broadcast public static final String TASKBAR_CHANGED_BROADCAST = "taskbarChanged"; + public static final String EXTRA_TASKBAR_CREATED = "taskbarCreated"; public static final String EXTRA_BUBBLE_OVERFLOW_OPENED = "bubbleOverflowOpened"; public static final String EXTRA_TASKBAR_VISIBLE = "taskbarVisible"; public static final String EXTRA_TASKBAR_POSITION = "taskbarPosition"; @@ -350,12 +351,15 @@ public class BubbleController implements Bubbles { + " itemPosition: " + itemPosition[0] + "," + itemPosition[1] + " iconSize: " + iconSize); PointF point = new PointF(itemPosition[0], itemPosition[1]); - mBubblePositioner.setPinnedLocation(point); + mBubblePositioner.setPinnedLocation(isVisible ? point : null); mBubblePositioner.updateForTaskbar(iconSize, taskbarPosition, isVisible, taskbarSize); if (mStackView != null) { - if (isVisible) { - mStackView.updateStackPosition(); + if (isVisible && b.getBoolean(EXTRA_TASKBAR_CREATED, false /* default */)) { + // If taskbar was created, add and remove the window so that bubbles display on top + removeFromWindowManagerMaybe(); + addToWindowManagerMaybe(); } + mStackView.updateStackPosition(); mBubbleIconFactory = new BubbleIconFactory(mContext); mStackView.onDisplaySizeChanged(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index df6683ebb80b..1bb5eda25058 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.Size; @@ -42,6 +43,9 @@ public class PipBoundsAlgorithm { private final @NonNull PipBoundsState mPipBoundsState; private final PipSnapAlgorithm mSnapAlgorithm; + private float mDefaultSizePercent; + private float mMinAspectRatioForMinSize; + private float mMaxAspectRatioForMinSize; private float mDefaultAspectRatio; private float mMinAspectRatio; private float mMaxAspectRatio; @@ -51,7 +55,7 @@ public class PipBoundsAlgorithm { public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) { mPipBoundsState = pipBoundsState; - mSnapAlgorithm = new PipSnapAlgorithm(context); + mSnapAlgorithm = new PipSnapAlgorithm(); reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which @@ -83,6 +87,11 @@ public class PipBoundsAlgorithm { com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); mMaxAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); + mDefaultSizePercent = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); + mMaxAspectRatioForMinSize = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); + mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } /** @@ -174,7 +183,7 @@ public class PipBoundsAlgorithm { final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize() : defaultMinEdgeSize; // Use the existing size but adjusted to the aspect ratio and min edge size. - size = mSnapAlgorithm.getSizeForAspectRatio( + size = getSizeForAspectRatio( new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize); } else { if (overrideMinSize != null) { @@ -184,7 +193,7 @@ public class PipBoundsAlgorithm { } else { // Calculate the default size using the display size and default min edge size. final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); - size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize, + size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); } } @@ -229,7 +238,7 @@ public class PipBoundsAlgorithm { defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio); } else { // Calculate the default size using the display size and default min edge size. - defaultSize = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio, + defaultSize = getSizeForAspectRatio(mDefaultAspectRatio, mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); } Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(), @@ -270,13 +279,28 @@ public class PipBoundsAlgorithm { getInsetBounds(movementBounds); // Apply the movement bounds adjustments based on the current state. - mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds, + getMovementBounds(stackBounds, movementBounds, movementBounds, (adjustForIme && mPipBoundsState.isImeShowing()) ? mPipBoundsState.getImeHeight() : 0); + return movementBounds; } /** + * Adjusts movementBoundsOut so that it is the movement bounds for the given stackBounds. + */ + public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut, + int bottomOffset) { + // Adjust the right/bottom to ensure the stack bounds never goes offscreen + movementBoundsOut.set(insetBounds); + movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right + - stackBounds.width()); + movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom + - stackBounds.height()); + movementBoundsOut.bottom -= bottomOffset; + } + + /** * @return the default snap fraction to apply instead of the default gravity when calculating * the default stack bounds when first entering PiP. */ @@ -304,6 +328,62 @@ public class PipBoundsAlgorithm { } /** + * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge + * is at least minEdgeSize. + */ + public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth, + int displayHeight) { + final int smallestDisplaySize = Math.min(displayWidth, displayHeight); + final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent); + + final int width; + final int height; + if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) { + // Beyond these points, we can just use the min size as the shorter edge + if (aspectRatio <= 1) { + // Portrait, width is the minimum size + width = minSize; + height = Math.round(width / aspectRatio); + } else { + // Landscape, height is the minimum size + height = minSize; + width = Math.round(height * aspectRatio); + } + } else { + // Within these points, we ensure that the bounds fit within the radius of the limits + // at the points + final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; + final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); + height = (int) Math.round(Math.sqrt((radius * radius) + / (aspectRatio * aspectRatio + 1))); + width = Math.round(height * aspectRatio); + } + return new Size(width, height); + } + + /** + * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the + * minimum edge is at least minEdgeSize. + */ + public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) { + final int smallestSize = Math.min(size.getWidth(), size.getHeight()); + final int minSize = (int) Math.max(minEdgeSize, smallestSize); + + final int width; + final int height; + if (aspectRatio <= 1) { + // Portrait, width is the minimum size. + width = minSize; + height = Math.round(width / aspectRatio); + } else { + // Landscape, height is the minimum size + height = minSize; + width = Math.round(height * aspectRatio); + } + return new Size(width, height); + } + + /** * Dumps internal states. */ public void dump(PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java index 71060752df09..0528e4d88374 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java @@ -20,11 +20,9 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.PointF; import android.graphics.Rect; -import android.util.Size; + +import com.android.internal.annotations.VisibleForTesting; /** * Calculates the snap targets and the snap position for the PIP given a position and a velocity. @@ -32,19 +30,6 @@ import android.util.Size; */ public class PipSnapAlgorithm { - private final float mDefaultSizePercent; - private final float mMinAspectRatioForMinSize; - private final float mMaxAspectRatioForMinSize; - - public PipSnapAlgorithm(Context context) { - Resources res = context.getResources(); - mDefaultSizePercent = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); - mMaxAspectRatioForMinSize = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); - mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; - } - /** * Returns a fraction that describes where the PiP bounds is. * See {@link #getSnapFraction(Rect, Rect, int)}. @@ -136,81 +121,11 @@ public class PipSnapAlgorithm { } /** - * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given - * {@param stackBounds}. - */ - public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut, - int bottomOffset) { - // Adjust the right/bottom to ensure the stack bounds never goes offscreen - movementBoundsOut.set(insetBounds); - movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right - - stackBounds.width()); - movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom - - stackBounds.height()); - movementBoundsOut.bottom -= bottomOffset; - } - - /** - * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge - * is at least {@param minEdgeSize}. - */ - public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth, - int displayHeight) { - final int smallestDisplaySize = Math.min(displayWidth, displayHeight); - final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent); - - final int width; - final int height; - if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) { - // Beyond these points, we can just use the min size as the shorter edge - if (aspectRatio <= 1) { - // Portrait, width is the minimum size - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - } else { - // Within these points, we ensure that the bounds fit within the radius of the limits - // at the points - final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; - final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); - height = (int) Math.round(Math.sqrt((radius * radius) / - (aspectRatio * aspectRatio + 1))); - width = Math.round(height * aspectRatio); - } - return new Size(width, height); - } - - /** - * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the - * minimum edge is at least minEdgeSize. - */ - public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) { - final int smallestSize = Math.min(size.getWidth(), size.getHeight()); - final int minSize = (int) Math.max(minEdgeSize, smallestSize); - - final int width; - final int height; - if (aspectRatio <= 1) { - // Portrait, width is the minimum size. - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - return new Size(width, height); - } - - /** * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes * the new bounds out to {@param boundsOut}. */ - public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, + @VisibleForTesting + void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, @PipBoundsState.StashType int stashType) { int leftEdge = stackBounds.left; if (stashType == STASH_TYPE_LEFT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 1c5d5b8a8262..48fa2115305d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -307,8 +307,7 @@ public class PipTouchHandler { public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) { final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds, - toMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0); final int prevBottom = mPipBoundsState.getMovementBounds().bottom - mMovementBoundsExtraOffsets; if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) { @@ -339,13 +338,13 @@ public class PipTouchHandler { // Re-calculate the expanded bounds Rect normalMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(normalBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(normalBounds, insetBounds, normalMovementBounds, bottomOffset); if (mPipBoundsState.getMovementBounds().isEmpty()) { // mMovementBounds is not initialized yet and a clean movement bounds without // bottom offset shall be used later in this function. - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */); } @@ -353,12 +352,12 @@ public class PipTouchHandler { float aspectRatio = (float) normalBounds.width() / normalBounds.height(); Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size expandedSize = mPipBoundsAlgorithm.getSnapAlgorithm().getSizeForAspectRatio( + Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio( aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y); mPipBoundsState.setExpandedBounds( new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight())); Rect expandedMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds( + mPipBoundsAlgorithm.getMovementBounds( mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds, bottomOffset); @@ -381,7 +380,7 @@ public class PipTouchHandler { } else { final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, toMovementBounds, mIsImeShowing ? mImeHeight : 0); final int prevBottom = mPipBoundsState.getMovementBounds().bottom - mMovementBoundsExtraOffsets; @@ -659,7 +658,7 @@ public class PipTouchHandler { private void animateToUnexpandedState(Rect restoreBounds) { Rect restoredMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(restoreBounds, + mPipBoundsAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction, restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */); @@ -865,7 +864,7 @@ public class PipTouchHandler { * resized. */ private void updateMovementBounds() { - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mPipBoundsState.getBounds(), + mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(), mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0); mMotionHelper.onMovementBoundsChanged(); @@ -877,7 +876,7 @@ public class PipTouchHandler { private Rect getMovementBounds(Rect curBounds) { Rect movementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds, movementBounds, mIsImeShowing ? mImeHeight : 0); return movementBounds; } diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS new file mode 100644 index 000000000000..2c6c7b358e3b --- /dev/null +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -0,0 +1,2 @@ +# includes OWNERS from parent directories +natanieljr@google.com diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index e1c9384f7081..0fb43e263d05 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -92,6 +92,26 @@ fun LayersAssertion.navBarLayerIsAlwaysVisible( } @JvmOverloads +fun LayersAssertion.appPairsDividerIsVisible( + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + end("appPairsDividerIsVisible", bugId, enabled) { + this.showsLayer(FlickerTestBase.APP_PAIRS_DIVIDER) + } +} + +@JvmOverloads +fun LayersAssertion.appPairsDividerIsInvisible( + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + end("appPairsDividerIsInVisible", bugId, enabled) { + this.hasNotLayer(FlickerTestBase.APP_PAIRS_DIVIDER) + } +} + +@JvmOverloads fun LayersAssertion.dockedStackDividerIsVisible( bugId: Int = 0, enabled: Boolean = bugId == 0 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt index 2e6037d148a8..54b8fdc83a1f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt @@ -130,6 +130,7 @@ abstract class FlickerTestBase { const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar" const val STATUS_BAR_WINDOW_TITLE = "StatusBar" const val DOCKED_STACK_DIVIDER = "DockedStackDivider" + const val APP_PAIRS_DIVIDER = "AppPairDivider" const val IMAGE_WALLPAPER = "ImageWallpaper" } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt new file mode 100644 index 000000000000..c33dbbc3f53a --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -0,0 +1,223 @@ +/* + * 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.wm.shell.flicker.apppairs + +import android.os.SystemClock +import android.util.Log +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.compatibility.common.util.SystemUtil +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS +import com.android.wm.shell.flicker.appPairsDividerIsInvisible +import com.android.wm.shell.flicker.appPairsDividerIsVisible +import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized +import java.io.IOException + +/** + * Test AppPairs launch. + * To run this test: `atest WMShellFlickerTests:AppPairsTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class AppPairsTest( + rotationName: String, + rotation: Int +) : AppPairsTestBase(rotationName, rotation) { + private val appPairsSetup: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + val testLaunchActivity = "launch_appPairs_primary_secondary_activities" + withTestName { + testLaunchActivity + } + setup { + eachRun { + uiDevice.wakeUpAndGoToHomeScreen() + primaryApp.open() + uiDevice.pressHome() + secondaryApp.open() + uiDevice.pressHome() + updateTaskId() + } + } + teardown { + eachRun { + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, false /* pair */)) + primaryApp.forceStop() + secondaryApp.forceStop() + } + } + assertions { + layersTrace { + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + } + } + + @Test + fun testAppPairs_pairPrimaryAndSecondaryApps() { + val testTag = "testAppPaired_pairPrimaryAndSecondary" + runWithFlicker(appPairsSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + transitions { + // TODO pair apps through normal UX flow + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + assertions { + layersTrace { + appPairsDividerIsVisible() + end("appsEndingBounds", enabled = false) { + val entry = this.trace.entries.firstOrNull() + ?: throw IllegalStateException("Trace is empty") + val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER) + this.hasVisibleRegion(primaryApp.defaultWindowName, + appPairsHelper.getPrimaryBounds(dividerRegion)) + .and() + .hasVisibleRegion(secondaryApp.defaultWindowName, + appPairsHelper.getSecondaryBounds(dividerRegion)) + } + } + windowManagerTrace { + end { + showsAppWindow(primaryApp.defaultWindowName) + .and() + .showsAppWindow(secondaryApp.defaultWindowName) + } + } + } + } + } + + @Test + fun testAppPairs_unpairPrimaryAndSecondary() { + val testTag = "testAppPairs_unpairPrimaryAndSecondary" + runWithFlicker(appPairsSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + setup { + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + transitions { + // TODO pair apps through normal UX flow + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, false /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + assertions { + layersTrace { + appPairsDividerIsInvisible() + start("appsStartingBounds", enabled = false) { + val entry = this.trace.entries.firstOrNull() + ?: throw IllegalStateException("Trace is empty") + val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER) + this.hasVisibleRegion(primaryApp.defaultWindowName, + appPairsHelper.getPrimaryBounds(dividerRegion)) + .and() + .hasVisibleRegion(secondaryApp.defaultWindowName, + appPairsHelper.getSecondaryBounds(dividerRegion)) + } + end("appsEndingBounds", enabled = false) { + this.hasNotLayer(primaryApp.defaultWindowName) + .and() + .hasNotLayer(secondaryApp.defaultWindowName) + } + } + windowManagerTrace { + end { + hidesAppWindow(primaryApp.defaultWindowName) + .and() + .hidesAppWindow(secondaryApp.defaultWindowName) + } + } + } + } + } + + private fun composePairsCommand( + primaryApp: String, + secondaryApp: String, + pair: Boolean + ): String = buildString { + // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2} + append("dumpsys activity service SystemUIService WMShell ") + if (pair) { + append("pair ") + } else { + append("unpair ") + } + append(primaryApp + " " + secondaryApp) + } + + private fun executeShellCommand(cmd: String) { + try { + SystemUtil.runShellCommand(instrumentation, cmd) + } catch (e: IOException) { + Log.d("AppPairsTest", "executeShellCommand error!" + e) + } + } + + private fun updateTaskId() { + val primaryAppComponent = primaryApp.openAppIntent.component + val secondaryAppComponent = secondaryApp.openAppIntent.component + if (primaryAppComponent != null) { + primaryTaskId = appPairsHelper.getTaskIdForActivity( + primaryAppComponent.packageName, primaryAppComponent.className).toString() + } + if (secondaryAppComponent != null) { + secondaryTaskId = appPairsHelper.getTaskIdForActivity( + secondaryAppComponent.packageName, secondaryAppComponent.className).toString() + } + } + + companion object { + var primaryTaskId = "" + var secondaryTaskId = "" + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt new file mode 100644 index 000000000000..1a4de0a80bec --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt @@ -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.wm.shell.flicker.apppairs + +import com.android.wm.shell.flicker.NonRotationTestBase +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL +import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.SplitScreenHelper + +abstract class AppPairsTestBase( + rotationName: String, + rotation: Int +) : NonRotationTestBase(rotationName, rotation) { + protected val appPairsHelper = AppPairsHelper(instrumentation, + TEST_APP_SPLITSCREEN_PRIMARY_LABEL, + TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME) + protected val primaryApp = SplitScreenHelper(instrumentation, + TEST_APP_SPLITSCREEN_PRIMARY_LABEL, + TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME) + protected val secondaryApp = SplitScreenHelper(instrumentation, + TEST_APP_SPLITSCREEN_SECONDARY_LABEL, + TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME) +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt new file mode 100644 index 000000000000..3b6fcdbee4be --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt @@ -0,0 +1,58 @@ +/* + * 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.wm.shell.flicker.helpers + +import android.app.Instrumentation +import android.content.ComponentName +import android.graphics.Region +import android.system.helpers.ActivityHelper +import com.android.server.wm.flicker.helpers.WindowUtils + +class AppPairsHelper( + instrumentation: Instrumentation, + activityLabel: String, + componentName: ComponentName +) : BaseAppHelper( + instrumentation, + activityLabel, + componentName +) { + val activityHelper = ActivityHelper.getInstance() + + fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region { + val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right, + dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset) + return primaryAppBounds + } + + fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region { + val displayBounds = WindowUtils.displayBounds + val secondaryAppBounds = Region(0, + dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) + return secondaryAppBounds + } + + fun getTaskIdForActivity(pkgName: String, activityName: String): Int { + return activityHelper.getTaskIdForActivity(pkgName, activityName) + } + + companion object { + const val TEST_REPETITIONS = 1 + const val TIMEOUT_MS = 3_000L + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index d343f2a7093b..3813d4dc3700 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -76,12 +76,12 @@ class PipKeyboardTest( } teardown { test { - keyboardApp.exit() + keyboardApp.forceStop() if (device.hasPipWindow()) { device.closePipWindow() } - testApp.exit() + testApp.forceStop() this.setRotation(Surface.ROTATION_0) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 7a6e0c1b41fc..a65d832359d2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -50,6 +50,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private static final float DEFAULT_ASPECT_RATIO = 1f; private static final float MIN_ASPECT_RATIO = 0.5f; private static final float MAX_ASPECT_RATIO = 2f; + private static final int DEFAULT_MIN_EDGE_SIZE = 100; private PipBoundsAlgorithm mPipBoundsAlgorithm; private DisplayInfo mDefaultDisplayInfo; @@ -73,7 +74,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { com.android.internal.R.integer.config_defaultPictureInPictureGravity, Gravity.END | Gravity.BOTTOM); res.addOverride( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100); + com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, + DEFAULT_MIN_EDGE_SIZE); res.addOverride( com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, "16x16"); @@ -112,6 +114,127 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { } @Test + public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() { + final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO, + DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth, + mDefaultDisplayInfo.logicalHeight); + + mPipBoundsState.setOverrideMinSize(null); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height())); + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + } + + @Test + public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() { + overrideDefaultAspectRatio(1.0f); + // The min size's aspect ratio is greater than the default aspect ratio. + final Size overrideMinSize = new Size(150, 120); + + mPipBoundsState.setOverrideMinSize(overrideMinSize); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // The default aspect ratio should trump the min size aspect ratio. + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + // The width of the min size is still used with the default aspect ratio. + assertEquals(overrideMinSize.getWidth(), defaultBounds.width()); + } + + @Test + public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() { + overrideDefaultAspectRatio(1.0f); + // The min size's aspect ratio is greater than the default aspect ratio. + final Size overrideMinSize = new Size(120, 150); + + mPipBoundsState.setOverrideMinSize(overrideMinSize); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // The default aspect ratio should trump the min size aspect ratio. + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + // The height of the min size is still used with the default aspect ratio. + assertEquals(overrideMinSize.getHeight(), defaultBounds.height()); + } + + @Test + public void getDefaultBounds_imeShowing_offsetByImeHeight() { + final int imeHeight = 30; + mPipBoundsState.setImeVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setImeVisibility(true, imeHeight); + final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top); + } + + @Test + public void getDefaultBounds_shelfShowing_offsetByShelfHeight() { + final int shelfHeight = 30; + mPipBoundsState.setShelfVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setShelfVisibility(true, shelfHeight); + final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top); + } + + @Test + public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() { + final int imeHeight = 30; + final int shelfHeight = 40; + mPipBoundsState.setImeVisibility(false, 0); + mPipBoundsState.setShelfVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setImeVisibility(true, imeHeight); + mPipBoundsState.setShelfVisibility(true, shelfHeight); + final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top); + } + + @Test + public void getDefaultBounds_boundsAtDefaultGravity() { + final Rect insetBounds = new Rect(); + mPipBoundsAlgorithm.getInsetBounds(insetBounds); + overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM); + + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(insetBounds.bottom, defaultBounds.bottom); + assertEquals(insetBounds.right, defaultBounds.right); + } + + @Test + public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() { + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // Set an invalid current aspect ratio. + mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2); + final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); + + assertEquals(defaultBounds, normalBounds); + } + + @Test + public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() { + final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds(); + mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio, + MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */); + + // Set a valid current aspect ratio different that the default. + mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO); + final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); + + assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds); + } + + @Test public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() { final float[] aspectRatios = new float[] { (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2, @@ -121,8 +244,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { for (float aspectRatio : aspectRatios) { mPipBoundsState.setAspectRatio(aspectRatio); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - final float actualAspectRatio = - destinationBounds.width() / (destinationBounds.height() * 1f); + final float actualAspectRatio = getRectAspectRatio(destinationBounds); assertEquals("Destination bounds matches the given aspect ratio", aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN); } @@ -274,6 +396,22 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } + private void overrideDefaultAspectRatio(float aspectRatio) { + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride( + com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + aspectRatio); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + } + + private void overrideDefaultStackGravity(int stackGravity) { + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride( + com.android.internal.R.integer.config_defaultPictureInPictureGravity, + stackGravity); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + } + private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) { final Rect expectedWithMargin = new Rect(expected); expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN); @@ -282,4 +420,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { + " with error margin " + ROUNDING_ERROR_MARGIN, expectedWithMargin.contains(actual)); } + + private static float getRectAspectRatio(Rect rect) { + return rect.width() / (rect.height() * 1f); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java new file mode 100644 index 000000000000..dcee2e1847b2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java @@ -0,0 +1,237 @@ +/* + * 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.wm.shell.pip; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link PipSnapAlgorithm}. **/ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipSnapAlgorithmTest extends ShellTestCase { + private static final int DEFAULT_STASH_OFFSET = 32; + private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 2000, 2000); + private static final Rect STACK_BOUNDS_CENTERED = new Rect(900, 900, 1100, 1100); + private static final Rect MOVEMENT_BOUNDS = new Rect(0, 0, + DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width(), + DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width()); + + private PipSnapAlgorithm mPipSnapAlgorithm; + + @Before + public void setUp() { + mPipSnapAlgorithm = new PipSnapAlgorithm(); + } + + @Test + public void testApplySnapFraction_topEdge() { + final float snapFraction = 0.25f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.width() / 4, bounds.left); + assertEquals(MOVEMENT_BOUNDS.top, bounds.top); + } + + @Test + public void testApplySnapFraction_rightEdge() { + final float snapFraction = 1.5f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + assertEquals(MOVEMENT_BOUNDS.height() / 2, bounds.top); + } + + @Test + public void testApplySnapFraction_bottomEdge() { + final float snapFraction = 2.25f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals((int) (MOVEMENT_BOUNDS.width() * 0.75f), bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_leftEdge() { + final float snapFraction = 3.75f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + assertEquals((int) (MOVEMENT_BOUNDS.height() * 0.25f), bounds.top); + } + + @Test + public void testApplySnapFraction_notStashed_isNotOffBounds() { + final float snapFraction = 2f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_NONE, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_stashedLeft() { + final float snapFraction = 3f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + final int offBoundsWidth = bounds.width() - DEFAULT_STASH_OFFSET; + assertEquals(MOVEMENT_BOUNDS.left - offBoundsWidth, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_stashedRight() { + final float snapFraction = 2f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_RIGHT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + assertEquals(DISPLAY_BOUNDS.right - DEFAULT_STASH_OFFSET, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_rightEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the right side. + bounds.offset(10, 0); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + } + + @Test + public void testSnapRectToClosestEdge_leftEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the left side. + bounds.offset(-10, 0); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + } + + @Test + public void testSnapRectToClosestEdge_topEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the top half. + bounds.offset(0, -10); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.top, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_bottomEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the bottom half. + bounds.offset(0, 10); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_stashed_unStahesBounds() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Stash it on the left side. + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, 3.5f, + PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_LEFT); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + } + + @Test + public void testGetSnapFraction_leftEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the left side. + bounds.offset(-10, 0); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(3.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_rightEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the right side. + bounds.offset(10, 0); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(1.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_topEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the top half. + bounds.offset(0, -10); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(0.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_bottomEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the bottom half. + bounds.offset(0, 10); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(2.5f, snapFraction, 0.1f); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index b25c74d12818..abbc681f53fe 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -91,7 +91,7 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipBoundsState = new PipBoundsState(mContext); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm(); - mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); + mPipSnapAlgorithm = new PipSnapAlgorithm(); mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator, mPipUiEventLogger); @@ -115,7 +115,8 @@ public class PipTouchHandlerTest extends ShellTestCase { @Test public void updateMovementBounds_minBounds() { Rect expectedMinMovementBounds = new Rect(); - mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, + 0); mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds, mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation); @@ -129,12 +130,13 @@ public class PipTouchHandlerTest extends ShellTestCase { public void updateMovementBounds_maxBounds() { Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1, + Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1, mContext.getResources().getDimensionPixelSize( R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y); Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight()); Rect expectedMaxMovementBounds = new Rect(); - mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, + 0); mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds, mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation); diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 27b33ace532a..a9da77230214 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -878,11 +878,14 @@ public class Tuner implements AutoCloseable { } /** - * Connects Conditional Access Modules (CAM) through Common Interface (CI) + * Connects Conditional Access Modules (CAM) through Common Interface (CI). * * <p>The demux uses the output from the frontend as the input by default, and must change to * use the output from CI-CAM as the input after this call. * + * <p> Note that this API is used to connect the CI-CAM to the Demux module while + * {@link connectFrontendToCiCam(int)} is used to connect CI-CAM to the Frontend module. + * * @param ciCamId specify CI-CAM Id to connect. * @return result status of the operation. */ @@ -895,23 +898,30 @@ public class Tuner implements AutoCloseable { } /** - * Link Conditional Access Modules (CAM) Frontend to support Common Interface (CI) by-pass mode. + * Connect Conditional Access Modules (CAM) Frontend to support Common Interface (CI) + * by-pass mode. * * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving * the TS directly from the frontend. * - * <p>Use {@link #unlinkFrontendToCicam(int)} to disconnect. + * <p> Note that this API is used to connect the CI-CAM to the Frontend module while + * {@link connectCiCam(int)} is used to connect CI-CAM to the Demux module. + * + * <p>Use {@link #disconnectFrontendToCiCam(int)} to disconnect. * * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause * no-op and return {@link INVALID_LTS_ID}. Use {@link TunerVersionChecker.getTunerVersion()} to * check the version. * - * @param ciCamId specify CI-CAM Id to link. + * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM) + * Common Interface (CI), to link. * @return Local transport stream id when connection is successfully established. Failed - * operation returns {@link INVALID_LTS_ID}. + * operation returns {@link INVALID_LTS_ID} while unsupported version also returns + * {@link INVALID_LTS_ID}. Check the current HAL version using + * {@link TunerVersionChecker.getTunerVersion()}. */ - public int linkFrontendToCiCam(int ciCamId) { + public int connectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "linkFrontendToCiCam")) { if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { @@ -922,10 +932,13 @@ public class Tuner implements AutoCloseable { } /** - * Disconnects Conditional Access Modules (CAM) + * Disconnects Conditional Access Modules (CAM). * * <p>The demux will use the output from the frontend as the input after this call. * + * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while + * {@link disconnectFrontendToCiCam(int)} is used to disconnect CI-CAM to the Frontend module. + * * @return result status of the operation. */ @Result @@ -937,18 +950,23 @@ public class Tuner implements AutoCloseable { } /** - * Unlink Conditional Access Modules (CAM) Frontend. + * Disconnect Conditional Access Modules (CAM) Frontend. * * <p>It is used by the client to unlink CI-CAM to a Frontend. * + * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while + * {@link disconnectCiCam(int)} is used to disconnect CI-CAM to the Frontend module. + * * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. * - * @param ciCamId specify CI-CAM Id to unlink. - * @return result status of the operation. + * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM) + * Common Interface (CI), to disconnect. + * @return result status of the operation. Unsupported version would return + * {@link RESULT_UNAVAILABLE} */ @Result - public int unlinkFrontendToCiCam(int ciCamId) { + public int disconnectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "unlinkFrontendToCiCam")) { if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 82cc78d7e47d..451f54caf7f9 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -186,7 +186,7 @@ public class Filter implements AutoCloseable { value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED, SCRAMBLING_STATUS_SCRAMBLED}) @Retention(RetentionPolicy.SOURCE) - public @interface ScramblingStatusMask {} + public @interface ScramblingStatus {} /** * Content’s scrambling status is unknown @@ -204,6 +204,23 @@ public class Filter implements AutoCloseable { public static final int SCRAMBLING_STATUS_SCRAMBLED = android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.SCRAMBLED; + /** @hide */ + @IntDef(flag = true, + prefix = "MONITOR_EVENT_", + value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) + @Retention(RetentionPolicy.SOURCE) + public @interface MonitorEventTypeMask {} + + /** + * Monitor scrambling status change. + */ + public static final int MONITOR_EVENT_SCRAMBLING_STATUS = + android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.SCRAMBLING_STATUS; + /** + * Monitor ip cid change. + */ + public static final int MONITOR_EVENT_IP_CID_CHANGE = + android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.IP_CID_CHANGE; private static final String TAG = "Filter"; @@ -222,7 +239,7 @@ public class Filter implements AutoCloseable { int type, int subType, FilterConfiguration settings); private native int nativeGetId(); private native long nativeGetId64Bit(); - private native int nativeconfigureScramblingEvent(int scramblingStatusMask); + private native int nativeConfigureMonitorEvent(int monitorEventTypesMask); private native int nativeSetDataSource(Filter source); private native int nativeStartFilter(); private native int nativeStopFilter(); @@ -306,34 +323,40 @@ public class Filter implements AutoCloseable { } /** - * Configure the Filter to monitor specific Scrambling Status through - * {@link ScramblingStatusEvent}. + * Configure the Filter to monitor scrambling status and ip cid change. Set corresponding bit of + * {@link MonitorEventTypeMask} to monitor the change. Reset to stop monitoring. * * <p>{@link ScramblingStatusEvent} should be sent at the following two scenarios: + * <ul> + * <li>When this method is called with {@link MONITOR_EVENT_SCRAMBLING_STATUS}, the first + * detected scrambling status should be sent. + * <li>When the Scrambling status transits into different status, event should be sent. + * <ul/> * + * <p>{@link IpCidChangeEvent} should be sent at the following two scenarios: * <ul> - * <li>When this method is called, the first detected scrambling status should be sent. - * <li>When the filter transits into the monitored statuses configured in - * {@code scramblingStatusMask}, event should be sent. + * <li>When this method is called with {@link MONITOR_EVENT_IP_CID_CHANGE}, the first detected + * CID for the IP should be sent. + * <li>When the CID is changed to different value for the IP filter, event should be sent. * <ul/> * * <p>This configuration is only supported in Tuner 1.1 or higher version. Unsupported version * will cause no-op. Use {@link TunerVersionChecker.getTunerVersion()} to get the version * information. * - * @param scramblingStatusMask Scrambling Statuses to be monitored. Set corresponding bit to - * monitor it. Reset to stop monitoring. + * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to + * monitor it. Reset to stop monitoring. * @return result status of the operation. */ @Result - public int configureScramblingStatusEvent(@ScramblingStatusMask int scramblingStatusMask) { + public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); if (!TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_1_1, "configureScramblingStatusEvent")) { + TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) { return Tuner.RESULT_UNAVAILABLE; } - return nativeconfigureScramblingEvent(scramblingStatusMask); + return nativeConfigureMonitorEvent(monitorEventTypesMask); } } diff --git a/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java new file mode 100644 index 000000000000..c0043f3b15cd --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java @@ -0,0 +1,46 @@ +/* + * 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.tv.tuner.filter; + +import android.annotation.SystemApi; + +/** + * Ip Cid Change event sent from {@link Filter} objects new ip cid. + * + * <p>This event is only sent in Tuner 1.1 or higher version. Use + * {@link TunerVersionChecker.getTunerVersion()} to get the version information. + * + * @hide + */ +@SystemApi +public final class IpCidChangeEvent extends FilterEvent { + private final int mCid; + + private IpCidChangeEvent(int cid) { + mCid = cid; + } + + /** + * Gets ip cid. + * + * <p>This event is only sent in Tuner 1.1 or higher version. Use + * {@link TunerVersionChecker.getTunerVersion()} to get the version information. + */ + public int getIpCid() { + return mCid; + } +} diff --git a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java index a78b96e9cf51..fef539638db8 100644 --- a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java +++ b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java @@ -30,18 +30,17 @@ import android.annotation.SystemApi; public final class ScramblingStatusEvent extends FilterEvent { private final int mScramblingStatus; - private ScramblingStatusEvent(@Filter.ScramblingStatusMask int scramblingStatus) { + private ScramblingStatusEvent(@Filter.ScramblingStatus int scramblingStatus) { mScramblingStatus = scramblingStatus; } /** * Gets Scrambling Status Type. * - * <p>This event field is only sent in Tuner 1.1 or higher version. Unsupported version returns - * default value 0. Use {@link TunerVersionChecker.getTunerVersion()} to get the version - * information. + * <p>This event field is only sent in Tuner 1.1 or higher version. Use + * {@link TunerVersionChecker.getTunerVersion()} to get the version information. */ - @Filter.ScramblingStatusMask + @Filter.ScramblingStatus public int getScramblingStatus() { return mScramblingStatus; } diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index ed56b4398c22..798bf6e2f8ee 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -19,8 +19,7 @@ package android.mtp; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.ContentProviderClient; -import android.content.ContentUris; -import android.content.ContentValues; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -32,7 +31,6 @@ import android.media.ExifInterface; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.BatteryManager; -import android.os.RemoteException; import android.os.SystemProperties; import android.os.storage.StorageVolume; import android.provider.MediaStore; @@ -103,8 +101,6 @@ public class MtpDatabase implements AutoCloseable { private MtpStorageManager mManager; private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; - private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID}; - private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA}; private static final String NO_MEDIA = ".nomedia"; static { @@ -431,7 +427,7 @@ public class MtpDatabase implements AutoCloseable { } // Add the new file to MediaProvider if (succeeded) { - MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile()); + updateMediaStore(mContext, obj.getPath().toFile()); } } @@ -580,32 +576,8 @@ public class MtpDatabase implements AutoCloseable { return MtpConstants.RESPONSE_GENERAL_ERROR; } - // finally update MediaProvider - ContentValues values = new ContentValues(); - values.put(Files.FileColumns.DATA, newPath.toString()); - String[] whereArgs = new String[]{oldPath.toString()}; - try { - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); - } - - // check if nomedia status changed - if (obj.isDir()) { - // for directories, check if renamed from something hidden to something non-hidden - if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) { - MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile()); - } - } else { - // for files, check if renamed from .nomedia to something else - if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA) - && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) { - MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile()); - } - } + updateMediaStore(mContext, oldPath.toFile()); + updateMediaStore(mContext, newPath.toFile()); return MtpConstants.RESPONSE_OK; } @@ -635,48 +607,15 @@ public class MtpDatabase implements AutoCloseable { Log.e(TAG, "Failed to end move object"); return; } - obj = mManager.getObject(objId); if (!success || obj == null) return; - // Get parent info from MediaProvider, since the id is different from MTP's - ContentValues values = new ContentValues(); + Path path = newParentObj.getPath().resolve(name); Path oldPath = oldParentObj.getPath().resolve(name); - values.put(Files.FileColumns.DATA, path.toString()); - if (obj.getParent().isRoot()) { - values.put(Files.FileColumns.PARENT, 0); - } else { - int parentId = findInMedia(newParentObj, path.getParent()); - if (parentId != -1) { - values.put(Files.FileColumns.PARENT, parentId); - } else { - // The new parent isn't in MediaProvider, so delete the object instead - deleteFromMedia(obj, oldPath, obj.isDir()); - return; - } - } - // update MediaProvider - Cursor c = null; - String[] whereArgs = new String[]{oldPath.toString()}; - try { - int parentId = -1; - if (!oldParentObj.isRoot()) { - parentId = findInMedia(oldParentObj, oldPath.getParent()); - } - if (oldParentObj.isRoot() || parentId != -1) { - // Old parent exists in MediaProvider - perform a move - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); - } else { - // Old parent doesn't exist - add the object - MediaStore.scanFile(mContext.getContentResolver(), path.toFile()); - } - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); - } + + updateMediaStore(mContext, oldPath.toFile()); + updateMediaStore(mContext, path.toFile()); } @VisibleForNative @@ -699,7 +638,19 @@ public class MtpDatabase implements AutoCloseable { if (!success) { return; } - MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile()); + + updateMediaStore(mContext, obj.getPath().toFile()); + } + + private static void updateMediaStore(@NonNull Context context, @NonNull File file) { + final ContentResolver resolver = context.getContentResolver(); + // For file, check whether the file name is .nomedia or not. + // If yes, scan the parent directory to update all files in the directory. + if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) { + MediaStore.scanFile(resolver, file.getParentFile()); + } else { + MediaStore.scanFile(resolver, file); + } } @VisibleForNative @@ -928,26 +879,6 @@ public class MtpDatabase implements AutoCloseable { deleteFromMedia(obj, obj.getPath(), obj.isDir()); } - private int findInMedia(MtpStorageManager.MtpObject obj, Path path) { - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - - int ret = -1; - Cursor c = null; - try { - c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE, - new String[]{path.toString()}, null, null); - if (c != null && c.moveToNext()) { - ret = c.getInt(0); - } - } catch (RemoteException e) { - Log.e(TAG, "Error finding " + path + " in MediaProvider"); - } finally { - if (c != null) - c.close(); - } - return ret; - } - private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) { final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); try { @@ -963,13 +894,10 @@ public class MtpDatabase implements AutoCloseable { } String[] whereArgs = new String[]{path.toString()}; - if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) { - if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) { - MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile()); - } - } else { - Log.i(TAG, "Mediaprovider didn't delete " + path); + if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) { + Log.i(TAG, "MediaProvider didn't delete " + path); } + updateMediaStore(mContext, path.toFile()); } catch (Exception e) { Log.d(TAG, "Failed to delete " + path + " from MediaProvider"); } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index b255a48d34d0..72dc32e7998f 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -604,11 +604,16 @@ jobjectArray FilterCallback::getTsRecordEvent( jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber); - jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts) - : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); - jlong firstMbInSlice = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice) - : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); + jlong pts; + jlong firstMbInSlice; + if (eventsExt.size() > i && eventsExt[i].getDiscriminator() == + DemuxFilterEventExt::Event::hidl_discriminator::tsRecord) { + pts = static_cast<jlong>(eventsExt[i].tsRecord().pts); + firstMbInSlice = static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice); + } else { + pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); + firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); + } jobject obj = env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, @@ -632,16 +637,25 @@ jobjectArray FilterCallback::getMmtpRecordEvent( jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask); jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber); - jint mpuSequenceNumber = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber) - : static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM); - jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts) - : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); - jlong firstMbInSlice = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice) - : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); - jlong tsIndexMask = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask) : 0; + + jint mpuSequenceNumber; + jlong pts; + jlong firstMbInSlice; + jlong tsIndexMask; + + if (eventsExt.size() > i && eventsExt[i].getDiscriminator() == + DemuxFilterEventExt::Event::hidl_discriminator::mmtpRecord) { + mpuSequenceNumber = static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber); + pts = static_cast<jlong>(eventsExt[i].mmtpRecord().pts); + firstMbInSlice = static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice); + tsIndexMask = static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask); + } else { + mpuSequenceNumber = + static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM); + pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); + firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); + tsIndexMask = 0; + } jobject obj = env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber, @@ -720,11 +734,21 @@ jobjectArray FilterCallback::getScramblingStatusEvent( jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); - for (int i = 0; i < eventsExt.size(); i++) { - auto scramblingStatus = eventsExt[i].scramblingStatus(); - jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus)); - env->SetObjectArrayElement(arr, i, obj); - } + auto scramblingStatus = eventsExt[0].monitorEvent().scramblingStatus(); + jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus)); + env->SetObjectArrayElement(arr, 0, obj); + return arr; +} + +jobjectArray FilterCallback::getIpCidChangeEvent( + jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); + + auto cid = eventsExt[0].monitorEvent().cid(); + jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(cid)); + env->SetObjectArrayElement(arr, 0, obj); return arr; } @@ -734,11 +758,9 @@ jobjectArray FilterCallback::getRestartEvent( jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); - for (int i = 0; i < eventsExt.size(); i++) { - auto startId = eventsExt[i].startId(); - jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId)); - env->SetObjectArrayElement(arr, i, obj); - } + auto startId = eventsExt[0].startId(); + jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId)); + env->SetObjectArrayElement(arr, 0, obj); return arr; } @@ -747,17 +769,31 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve ALOGD("FilterCallback::onFilterEvent_1_1"); JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobjectArray array; std::vector<DemuxFilterEvent::Event> events = filterEvent.events; std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events; jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent"); - jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL); if (events.empty() && !eventsExt.empty()) { + // Monitor event should be sent with one DemuxFilterMonitorEvent in DemuxFilterEventExt. + array = env->NewObjectArray(1, eventClazz, NULL); auto eventExt = eventsExt[0]; switch (eventExt.getDiscriminator()) { - case DemuxFilterEventExt::Event::hidl_discriminator::scramblingStatus: { - array = getScramblingStatusEvent(array, eventsExt); + case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent: { + switch (eventExt.monitorEvent().getDiscriminator()) { + case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus: { + array = getScramblingStatusEvent(array, eventsExt); + break; + } + case DemuxFilterMonitorEvent::hidl_discriminator::cid: { + array = getIpCidChangeEvent(array, eventsExt); + break; + } + default: { + break; + } + } break; } case DemuxFilterEventExt::Event::hidl_discriminator::startId: { @@ -771,6 +807,7 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve } if (!events.empty()) { + array = env->NewObjectArray(events.size(), eventClazz, NULL); auto event = events[0]; switch (event.getDiscriminator()) { case DemuxFilterEvent::Event::hidl_discriminator::media: { @@ -4069,8 +4106,8 @@ static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject fil ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT); } -static jint android_media_tv_Tuner_configure_scrambling_status_event( - JNIEnv* env, jobject filter, int scramblingStatusMask) { +static jint android_media_tv_Tuner_configure_monitor_event( + JNIEnv* env, jobject filter, int monitorEventType) { sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to configure scrambling event: filter not found"); @@ -4082,7 +4119,7 @@ static jint android_media_tv_Tuner_configure_scrambling_status_event( Result res; if (iFilterSp_1_1 != NULL) { - res = iFilterSp_1_1->configureScramblingEvent(scramblingStatusMask); + res = iFilterSp_1_1->configureMonitorEvent(monitorEventType); } else { ALOGW("configureScramblingEvent is not supported with the current HAL implementation."); return (jint) Result::INVALID_STATE; @@ -4762,8 +4799,8 @@ static const JNINativeMethod gFilterMethods[] = { { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id }, { "nativeGetId64Bit", "()J", (void *)android_media_tv_Tuner_get_filter_64bit_id }, - { "nativeconfigureScramblingEvent", "(I)I", - (void *)android_media_tv_Tuner_configure_scrambling_status_event }, + { "nativeConfigureMonitorEvent", "(I)I", + (void *)android_media_tv_Tuner_configure_monitor_event }, { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_set_filter_data_source }, { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index ebb95f43c52d..e79b5c2e391a 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -44,7 +44,6 @@ using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; using ::android::hardware::kSynchronizedReadWrite; using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent; -using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt; using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus; using ::android::hardware::tv::tuner::V1_0::DemuxFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxPid; @@ -73,6 +72,8 @@ using ::android::hardware::tv::tuner::V1_0::LnbId; using ::android::hardware::tv::tuner::V1_0::PlaybackStatus; using ::android::hardware::tv::tuner::V1_0::RecordStatus; using ::android::hardware::tv::tuner::V1_0::Result; +using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt; +using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent; using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1; using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1; using ::android::hardware::tv::tuner::V1_1::IFrontendCallback; @@ -188,6 +189,8 @@ private: jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); jobjectArray getScramblingStatusEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); + jobjectArray getIpCidChangeEvent( + jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); jobjectArray getRestartEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); }; diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index d134b37c6b70..eca67bd7d211 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -151,6 +151,7 @@ LIBANDROID { AHardwareBuffer_allocate; # introduced=26 AHardwareBuffer_describe; # introduced=26 AHardwareBuffer_fromHardwareBuffer; # introduced=26 + AHardwareBuffer_getId; # introduced=31 AHardwareBuffer_getNativeHandle; # introduced=26 AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; # introduced=26 diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index cea7903b8726..9f1c96d88905 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1109,6 +1109,8 @@ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until charged</string> <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration --> <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string> + <!-- [CHAR_LIMIT=40] Label for battery level chart when charge been limited --> + <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Battery limited temporarily</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index d16aebb4d4b4..9331b5e09f38 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -269,6 +269,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.EMERGENCY_GESTURE_CALL_NUMBER, NONE_NEGATIVE_LONG_VALIDATOR); VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put( Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index a3bda96337bb..af12ddd8895b 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -315,7 +315,6 @@ public class SettingsBackupTest { Settings.Global.KERNEL_CPU_THREAD_READER, Settings.Global.LANG_ID_UPDATE_CONTENT_URL, Settings.Global.LANG_ID_UPDATE_METADATA_URL, - Settings.Global.LATENCY_TRACKER, Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, @@ -743,6 +742,7 @@ public class SettingsBackupTest { Settings.Secure.SKIP_GESTURE, Settings.Secure.SILENCE_GESTURE, Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, + Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER, Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, Settings.Secure.FACE_UNLOCK_RE_ENROLL, Settings.Secure.TAP_GESTURE, diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index f7e9fedd5f66..d95ab374a5cb 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -79,6 +79,9 @@ is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. --> <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string> + <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's limited temporarily. --> + <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Battery limited temporarily</string> + <!-- When the lock screen is showing and the battery is low, warn user to plug in the phone soon. --> <string name="keyguard_low_battery">Connect your charger.</string> diff --git a/packages/SystemUI/res/layout/controls_dialog_pin.xml b/packages/SystemUI/res/layout/controls_dialog_pin.xml index 170b32b6c669..d0ef10b649bf 100644 --- a/packages/SystemUI/res/layout/controls_dialog_pin.xml +++ b/packages/SystemUI/res/layout/controls_dialog_pin.xml @@ -27,6 +27,7 @@ android:layout_height="wrap_content" android:minHeight="48dp" android:longClickable="false" + android:textAlignment="viewStart" android:inputType="numberPassword" /> <CheckBox android:id="@+id/controls_pin_use_alpha" diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 283918912c76..f38e653190b0 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -305,6 +305,9 @@ <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast"> </style> + <style name="Animation.MediaOutputDialog" parent="@android:style/Animation.InputMethod"> + </style> + <!-- Standard animations for hiding and showing the status bar. --> <style name="Animation.StatusBar"> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java new file mode 100644 index 000000000000..4a870f17ab0a --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import android.annotation.Nullable; +import android.graphics.Rect; +import android.graphics.Region; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; + +import java.util.HashMap; + +public class ViewTreeObserverWrapper { + + private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener> + sOnComputeInsetsListenerMap = new HashMap<>(); + + /** + * Register a callback to be invoked when the invoked when it is time to + * compute the window's insets. + * + * @param listener The callback to add + * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false + */ + public static void addOnComputeInsetsListener( + ViewTreeObserver observer, OnComputeInsetsListener listener) { + final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> { + final InsetsInfo inOutInfo = new InsetsInfo(); + inOutInfo.contentInsets.set(internalInOutInfo.contentInsets); + inOutInfo.visibleInsets.set(internalInOutInfo.visibleInsets); + inOutInfo.touchableRegion.set(internalInOutInfo.touchableRegion); + listener.onComputeInsets(inOutInfo); + internalInOutInfo.contentInsets.set(inOutInfo.contentInsets); + internalInOutInfo.visibleInsets.set(inOutInfo.visibleInsets); + internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion); + internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets); + }; + sOnComputeInsetsListenerMap.put(listener, internalListener); + observer.addOnComputeInternalInsetsListener(internalListener); + } + + /** + * Remove a previously installed insets computation callback + * + * @param victim The callback to remove + * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false + * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener) + */ + public void removeOnComputeInsetsListener( + ViewTreeObserver observer, OnComputeInsetsListener victim) { + final OnComputeInternalInsetsListener listener = sOnComputeInsetsListenerMap.get(victim); + if (listener != null) { + observer.removeOnComputeInternalInsetsListener(listener); + } + } + + /** + * Interface definition for a callback to be invoked when layout has + * completed and the client can compute its interior insets. + */ + public interface OnComputeInsetsListener { + /** + * Callback method to be invoked when layout has completed and the + * client can compute its interior insets. + * + * @param inoutInfo Should be filled in by the implementation with + * the information about the insets of the window. This is called + * with whatever values the previous OnComputeInsetsListener + * returned, if there are multiple such listeners in the window. + */ + void onComputeInsets(InsetsInfo inoutInfo); + } + + /** + * Parameters used with OnComputeInsetsListener. + */ + public final static class InsetsInfo { + + /** + * Offsets from the frame of the window at which the content of + * windows behind it should be placed. + */ + public final Rect contentInsets = new Rect(); + + /** + * Offsets from the frame of the window at which windows behind it + * are visible. + */ + public final Rect visibleInsets = new Rect(); + + /** + * Touchable region defined relative to the origin of the frame of the window. + * Only used when {@link #setTouchableInsets(int)} is called with + * the option {@link #TOUCHABLE_INSETS_REGION}. + */ + public final Region touchableRegion = new Region(); + + /** + * Option for {@link #setTouchableInsets(int)}: the entire window frame + * can be touched. + */ + public static final int TOUCHABLE_INSETS_FRAME = + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; + + /** + * Option for {@link #setTouchableInsets(int)}: the area inside of + * the content insets can be touched. + */ + public static final int TOUCHABLE_INSETS_CONTENT = + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; + + /** + * Option for {@link #setTouchableInsets(int)}: the area inside of + * the visible insets can be touched. + */ + public static final int TOUCHABLE_INSETS_VISIBLE = + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE; + + /** + * Option for {@link #setTouchableInsets(int)}: the area inside of + * the provided touchable region in {@link #touchableRegion} can be touched. + */ + public static final int TOUCHABLE_INSETS_REGION = + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; + + /** + * Set which parts of the window can be touched: either + * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT}, + * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}. + */ + public void setTouchableInsets(int val) { + mTouchableInsets = val; + } + + int mTouchableInsets; + + @Override + public int hashCode() { + int result = contentInsets.hashCode(); + result = 31 * result + visibleInsets.hashCode(); + result = 31 * result + touchableRegion.hashCode(); + result = 31 * result + mTouchableInsets; + return result; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final InsetsInfo other = (InsetsInfo) o; + return mTouchableInsets == other.mTouchableInsets && + contentInsets.equals(other.contentInsets) && + visibleInsets.equals(other.visibleInsets) && + touchableRegion.equals(other.touchableRegion); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index bb7906e7c3ae..714d267bb07d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -23,6 +23,7 @@ import android.content.BroadcastReceiver; import android.content.ContentProvider; import android.content.Context; import android.content.Intent; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -94,7 +95,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - // no-op + Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e); } } ); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 2b33f8cd036e..182b3e114887 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -101,7 +101,7 @@ public class AuthContainerView extends LinearLayout @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; - private @ContainerState int mContainerState = STATE_UNKNOWN; + @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN; // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason; @@ -632,10 +632,11 @@ public class AuthContainerView extends LinearLayout mWindowManager.removeView(this); } - private void onDialogAnimatedIn() { + @VisibleForTesting + void onDialogAnimatedIn() { if (mContainerState == STATE_PENDING_DISMISS) { Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); - animateAway(false /* sendReason */, 0); + animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); return; } mContainerState = STATE_SHOWING; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 3fc9f4d2aa61..8776532debcd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -187,12 +187,13 @@ class UdfpsController implements DozeReceiver { // TODO(b/152419866): Use the UDFPS window type when it becomes available. WindowManager.LayoutParams.TYPE_BOOT_PROGRESS, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); mCoreLayoutParams.setTitle(TAG); mCoreLayoutParams.setFitInsetsTypes(0); + mCoreLayoutParams.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false); mView.setSensorProperties(mSensorProps); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 58e49f896931..6888d1ad6063 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -134,7 +134,7 @@ public class DozeTriggers implements DozeMachine.Part { DOZING_UPDATE_SENSOR_TAP(441), @UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.") - DOZING_UPDATE_AUTH_TRIGGERED(442); + DOZING_UPDATE_AUTH_TRIGGERED(657); private final int mId; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 37bcb163d6f3..bd3b899adee7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -329,7 +329,7 @@ public class KeyguardSliceProvider extends SliceProvider implements Method injectMethod = rootComponent.getClass() .getMethod("inject", getClass()); injectMethod.invoke(rootComponent, this); - Log.w("TAG", "mMediaManager is now: " + mMediaManager); + Log.w(TAG, "mMediaManager is now: " + mMediaManager); } catch (NoSuchMethodException ex) { Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex); } catch (IllegalAccessException | InvocationTargetException ex) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index 78939dffb4fb..8a9a6e694658 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -105,6 +105,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements window.setAttributes(lp); window.setContentView(mDialogView); window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + window.setWindowAnimations(R.style.Animation_MediaOutputDialog); mHeaderTitle = mDialogView.requireViewById(R.id.header_title); mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index a27e9ac61848..8c66ba32ec79 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -16,6 +16,8 @@ package com.android.systemui.power; +import static android.app.PendingIntent.FLAG_IMMUTABLE; + import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -334,10 +336,14 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } private PendingIntent pendingBroadcast(String action) { - return PendingIntent.getBroadcastAsUser(mContext, 0, - new Intent(action).setPackage(mContext.getPackageName()) - .setFlags(Intent.FLAG_RECEIVER_FOREGROUND), - 0, UserHandle.CURRENT); + return PendingIntent.getBroadcastAsUser( + mContext, + 0 /* request code */, + new Intent(action) + .setPackage(mContext.getPackageName()) + .setFlags(Intent.FLAG_RECEIVER_FOREGROUND), + FLAG_IMMUTABLE /* flags */, + UserHandle.CURRENT); } private static Intent settings(String action) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index fbcfef3964af..153ba514fc33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -2554,12 +2554,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { ExpandableView child = (ExpandableView) getChildAt(i); - if (child.getVisibility() != View.GONE && !(child instanceof StackScrollerDecorView) - && child != mShelf) { + if (child.getVisibility() != View.GONE + && !(child instanceof StackScrollerDecorView) + && child != mShelf + && !mAmbientState.getDraggedViews().contains(child)) { children.add(child); } } - return children; } @@ -5524,10 +5525,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable void addDraggedView(View view) { mAmbientState.onBeginDrag(view); + updateFirstAndLastBackgroundViews(); } void removeDraggedView(View view) { mAmbientState.onDragFinished(view); + updateFirstAndLastBackgroundViews(); } void setTopHeadsUpEntry(NotificationEntry topEntry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 7cee365bc7d3..f5385050f063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -463,7 +463,7 @@ public class NotificationStackScrollLayoutController { @Override public void onChildSnappedBack(View animView, float targetLeft) { - mView.addDraggedView(animView); + mView.removeDraggedView(animView); mView.updateContinuousShadowDrawing(); mView.updateContinuousBackgroundDrawing(); if (animView instanceof ExpandableNotificationRow) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java index 777db952591e..5088a53a2d5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java @@ -211,6 +211,16 @@ public class AuthContainerViewTest extends SysuiTestCase { } @Test + public void testOnDialogAnimatedIn_sendsCancelReason_whenPendingDismiss() { + initializeContainer(Authenticators.BIOMETRIC_WEAK); + mAuthContainer.mContainerState = AuthContainerView.STATE_PENDING_DISMISS; + mAuthContainer.onDialogAnimatedIn(); + verify(mCallback).onDismissed( + eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq(null) /* credentialAttestation */); + } + + @Test public void testLayoutParams_hasSecureWindowFlag() { final IBinder windowToken = mock(IBinder.class); final WindowManager.LayoutParams layoutParams = diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 15bd4dc66ccc..ae024ff6d043 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -332,5 +332,9 @@ message SystemMessage { // Notify the user that the admin suspended personal apps on the device. // Package: android NOTE_PERSONAL_APPS_SUSPENDED = 1003; + + // Notify the user that window magnification is available. + // package: android + NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE = 1004; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 857ac6ae62a9..be7643ecbd4e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -37,6 +37,7 @@ import com.android.server.accessibility.gestures.TouchExplorer; import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; import com.android.server.accessibility.magnification.MagnificationGestureHandler; import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; +import com.android.server.accessibility.magnification.WindowMagnificationPromptController; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; @@ -561,8 +562,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo detectControlGestures, triggerable, displayId); } else { magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext, - mAms.getFullScreenMagnificationController(), mAms::onMagnificationScaleChanged, - detectControlGestures, triggerable, displayId); + mAms.getFullScreenMagnificationController(), + mAms::onMagnificationScaleChanged, detectControlGestures, triggerable, + new WindowMagnificationPromptController(displayContext, mUserId), displayId); } return magnificationGestureHandler; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 35481a282701..4c85490a8086 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -80,7 +80,6 @@ import android.os.ShellCallback; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.provider.Settings; import android.provider.SettingsStringUtil.SettingStringHelper; import android.text.TextUtils; @@ -122,6 +121,7 @@ import com.android.server.accessibility.magnification.FullScreenMagnificationCon import com.android.server.accessibility.magnification.MagnificationController; import com.android.server.accessibility.magnification.MagnificationGestureHandler; import com.android.server.accessibility.magnification.WindowMagnificationManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java index 5b46cb4ab378..f731c440d5f3 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java @@ -18,11 +18,7 @@ package com.android.server.accessibility.gestures; import static android.view.MotionEvent.INVALID_POINTER_ID; -import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM; import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex; -import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM; -import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS; -import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_START_SWIPE_MS; import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG; import android.content.Context; @@ -30,7 +26,6 @@ import android.graphics.PointF; import android.os.Handler; import android.util.DisplayMetrics; import android.util.Slog; -import android.util.TypedValue; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -49,9 +44,6 @@ class MultiFingerSwipe extends GestureMatcher { public static final int RIGHT = 1; public static final int UP = 2; public static final int DOWN = 3; - // This is the calculated movement threshold used track if the user is still - // moving their finger. - private final float mGestureDetectionThresholdPixels; // Buffer for storing points for gesture detection. private final ArrayList<PointF>[] mStrokeBuffers; @@ -93,10 +85,7 @@ class MultiFingerSwipe extends GestureMatcher { mStrokeBuffers = new ArrayList[mTargetFingerCount]; mDirection = direction; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - mGestureDetectionThresholdPixels = - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics) - * GESTURE_CONFIRM_CM; - // Calculate minimum gesture velocity + // Calculate gesture sampling interval. final float pixelsPerCmX = displayMetrics.xdpi / GestureUtils.CM_PER_INCH; final float pixelsPerCmY = displayMetrics.ydpi / GestureUtils.CM_PER_INCH; mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX; @@ -150,7 +139,6 @@ class MultiFingerSwipe extends GestureMatcher { return; } mPointerIds[pointerIndex] = pointerId; - cancelAfterPauseThreshold(event, rawEvent, policyFlags); if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) { final float x = rawEvent.getX(actionIndex); final float y = rawEvent.getY(actionIndex); @@ -197,7 +185,6 @@ class MultiFingerSwipe extends GestureMatcher { return; } mPointerIds[pointerIndex] = pointerId; - cancelAfterPauseThreshold(event, rawEvent, policyFlags); if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) { final float x = rawEvent.getX(actionIndex); final float y = rawEvent.getY(actionIndex); @@ -286,65 +273,42 @@ class MultiFingerSwipe extends GestureMatcher { Math.abs(x - mBase[pointerIndex].x), Math.abs(y - mBase[pointerIndex].y)); if (DEBUG) { - Slog.d( - getGestureName(), - "moveDelta:" - + Double.toString(moveDelta) - + " mGestureDetectionThreshold: " - + Float.toString(mGestureDetectionThresholdPixels)); + Slog.d(getGestureName(), "moveDelta:" + moveDelta); } if (getState() == STATE_CLEAR) { if (moveDelta < (mTargetFingerCount * mTouchSlop)) { // This still counts as a touch not a swipe. continue; - } else if (mStrokeBuffers[pointerIndex].size() == 0) { - // First, make sure we have the right number of fingers down. - if (mCurrentFingerCount != mTargetFingerCount) { - cancelGesture(event, rawEvent, policyFlags); - return; - } - // Then, make sure the pointer is going in the right direction. - int direction = - toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y); - if (direction != mDirection) { - cancelGesture(event, rawEvent, policyFlags); - return; - } else { - // This is confirmed to be some kind of swipe so start tracking points. - cancelAfterPauseThreshold(event, rawEvent, policyFlags); - for (int i = 0; i < mTargetFingerCount; ++i) { - mStrokeBuffers[i].add(new PointF(mBase[i])); - } - } } - if (moveDelta > mGestureDetectionThresholdPixels) { - // Try to cancel if the finger starts to go the wrong way. - // Note that this only works because this matcher assumes one direction. - int direction = - toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y); - if (direction != mDirection) { - cancelGesture(event, rawEvent, policyFlags); - return; - } - // If the pointer has moved more than the threshold, - // update the stored values. - mBase[pointerIndex].x = x; - mBase[pointerIndex].y = y; - mPreviousGesturePoint[pointerIndex].x = x; - mPreviousGesturePoint[pointerIndex].y = y; - if (getState() == STATE_CLEAR) { - startGesture(event, rawEvent, policyFlags); - cancelAfterPauseThreshold(event, rawEvent, policyFlags); - } + // First, make sure we have the right number of fingers down. + if (mCurrentFingerCount != mTargetFingerCount) { + cancelGesture(event, rawEvent, policyFlags); + return; + } + // Then, make sure the pointer is going in the right direction. + int direction = toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y); + if (direction != mDirection) { + cancelGesture(event, rawEvent, policyFlags); + return; + } + // This is confirmed to be some kind of swipe so start tracking points. + startGesture(event, rawEvent, policyFlags); + for (int i = 0; i < mTargetFingerCount; ++i) { + mStrokeBuffers[i].add(new PointF(mBase[i])); + } + } else if (getState() == STATE_GESTURE_STARTED) { + // Cancel if the finger starts to go the wrong way. + // Note that this only works because this matcher assumes one direction. + int direction = toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y); + if (direction != mDirection) { + cancelGesture(event, rawEvent, policyFlags); + return; } - } - if (getState() == STATE_GESTURE_STARTED) { if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) { // Sample every 2.5 MM in order to guard against minor variations in path. mPreviousGesturePoint[pointerIndex].x = x; mPreviousGesturePoint[pointerIndex].y = y; mStrokeBuffers[pointerIndex].add(new PointF(x, y)); - cancelAfterPauseThreshold(event, rawEvent, policyFlags); } } } @@ -379,24 +343,6 @@ class MultiFingerSwipe extends GestureMatcher { } /** - * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have - * transitioned to STATE_GESTURE_STARTED the delay is longer. - */ - private void cancelAfterPauseThreshold( - MotionEvent event, MotionEvent rawEvent, int policyFlags) { - cancelPendingTransitions(); - switch (getState()) { - case STATE_CLEAR: - cancelAfter(MAX_TIME_TO_START_SWIPE_MS, event, rawEvent, policyFlags); - break; - case STATE_GESTURE_STARTED: - cancelAfter(MAX_TIME_TO_CONTINUE_SWIPE_MS, event, rawEvent, policyFlags); - break; - default: - break; - } - } - /** * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then transitions * to the complete or cancel state depending on the result. */ @@ -502,8 +448,6 @@ class MultiFingerSwipe extends GestureMatcher { if (getState() != STATE_GESTURE_CANCELED) { builder.append(", mBase: ") .append(mBase.toString()) - .append(", mGestureDetectionThreshold:") - .append(mGestureDetectionThresholdPixels) .append(", mMinPixelsBetweenSamplesX:") .append(mMinPixelsBetweenSamplesX) .append(", mMinPixelsBetweenSamplesY:") diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index d50e9d720861..efb9d87a0bfd 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -137,6 +137,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @VisibleForTesting final ViewportDraggingState mViewportDraggingState; private final ScreenStateReceiver mScreenStateReceiver; + private final WindowMagnificationPromptController mPromptController; /** * {@code true} if this detector should detect and respond to triple-tap @@ -178,6 +179,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH ScaleChangedListener listener, boolean detectTripleTap, boolean detectShortcutTrigger, + @NonNull WindowMagnificationPromptController promptController, int displayId) { super(listener); if (DEBUG_ALL) { @@ -186,6 +188,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + ", detectShortcutTrigger = " + detectShortcutTrigger + ")"); } mFullScreenMagnificationController = fullScreenMagnificationController; + mPromptController = promptController; mDisplayId = displayId; mDelegatingState = new DelegatingState(); @@ -195,7 +198,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mDetectTripleTap = detectTripleTap; mDetectShortcutTrigger = detectShortcutTrigger; - if (mDetectShortcutTrigger) { mScreenStateReceiver = new ScreenStateReceiver(context, this); mScreenStateReceiver.register(); @@ -264,6 +266,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (mScreenStateReceiver != null) { mScreenStateReceiver.unregister(); } + mPromptController.onDestroy(); // Check if need to reset when MagnificationGestureHandler is the last magnifying service. mFullScreenMagnificationController.resetAllIfNeeded( AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); @@ -278,6 +281,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (wasMagnifying) { clearAndTransitionToStateDetecting(); } else { + mPromptController.showNotificationIfNeeded(); mDetectingState.toggleShortcutTriggered(); } } @@ -950,6 +954,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (mFullScreenMagnificationController.isMagnifying(mDisplayId)) { zoomOff(); } else { + mPromptController.showNotificationIfNeeded(); zoomOn(up.getX(), up.getY()); } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java new file mode 100644 index 000000000000..721220729a33 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java @@ -0,0 +1,211 @@ +/* + * 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.accessibility.magnification; + +import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT; + +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; +import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE; + +import android.Manifest; +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.app.ActivityOptions; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.notification.SystemNotificationChannels; + +/** + * A class to show notification to prompt the user that this feature is available. + */ +public class WindowMagnificationPromptController { + + private static final Uri MAGNIFICATION_WINDOW_MODE_PROMPT_URI = Settings.Secure.getUriFor( + ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT); + @VisibleForTesting + static final String ACTION_DISMISS = + "com.android.server.accessibility.magnification.action.DISMISS"; + @VisibleForTesting + static final String ACTION_TURN_ON_IN_SETTINGS = + "com.android.server.accessibility.magnification.action.TURN_ON_IN_SETTINGS"; + private final Context mContext; + private final NotificationManager mNotificationManager; + private final ContentObserver mContentObserver; + private final int mUserId; + @VisibleForTesting + BroadcastReceiver mNotificationActionReceiver; + + private boolean mNeedToShowNotification; + + @MainThread + public WindowMagnificationPromptController(@NonNull Context context, int userId) { + mContext = context; + mNotificationManager = context.getSystemService(NotificationManager.class); + mUserId = userId; + mContentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + onPromptSettingsValueChanged(); + } + }; + context.getContentResolver().registerContentObserver(MAGNIFICATION_WINDOW_MODE_PROMPT_URI, + false, mContentObserver, mUserId); + mNeedToShowNotification = isWindowMagnificationPromptEnabled(); + } + + @VisibleForTesting + protected void onPromptSettingsValueChanged() { + final boolean needToShowNotification = isWindowMagnificationPromptEnabled(); + if (mNeedToShowNotification == needToShowNotification) { + return; + } + mNeedToShowNotification = needToShowNotification; + if (!mNeedToShowNotification) { + unregisterReceiverIfNeeded(); + mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + } + } + + /** + * Shows the prompt notification that could bring users to magnification settings if necessary. + */ + @MainThread + void showNotificationIfNeeded() { + if (!mNeedToShowNotification) return; + + final Notification.Builder notificationBuilder = new Notification.Builder(mContext, + SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION); + notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp) + .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title)) + .setContentText(mContext.getString(R.string.window_magnification_prompt_content)) + .setLargeIcon(Icon.createWithResource(mContext, + R.drawable.ic_accessibility_magnification)) + .setTicker(mContext.getString(R.string.window_magnification_prompt_title)) + .setOnlyAlertOnce(true) + .setDeleteIntent(createPendingIntent(ACTION_DISMISS)) + .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)) + .setActions(buildTurnOnAction(), buildDismissAction()); + mNotificationManager.notify(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE, + notificationBuilder.build()); + registerReceiverIfNeeded(); + } + + /** + * Called when this object is not used anymore to release resources if necessary. + */ + @VisibleForTesting + @MainThread + public void onDestroy() { + dismissNotification(); + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + } + + private boolean isWindowMagnificationPromptEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId) == 1; + } + + private Notification.Action buildTurnOnAction() { + return new Notification.Action.Builder(null, + mContext.getString(R.string.turn_on_magnification_settings_action), + createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)).build(); + } + + private Notification.Action buildDismissAction() { + return new Notification.Action.Builder(null, mContext.getString(R.string.dismiss_action), + createPendingIntent(ACTION_DISMISS)).build(); + } + + private PendingIntent createPendingIntent(String action) { + final Intent intent = new Intent(action); + intent.setPackage(mContext.getPackageName()); + return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); + } + + private void registerReceiverIfNeeded() { + if (mNotificationActionReceiver != null) { + return; + } + mNotificationActionReceiver = new NotificationActionReceiver(); + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_DISMISS); + intentFilter.addAction(ACTION_TURN_ON_IN_SETTINGS); + mContext.registerReceiver(mNotificationActionReceiver, intentFilter, + Manifest.permission.MANAGE_ACCESSIBILITY, null); + } + + private void launchMagnificationSettings() { + final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, + MAGNIFICATION_COMPONENT_NAME.flattenToShortString()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId( + mContext.getDisplayId()).toBundle(); + mContext.startActivityAsUser(intent, bundle, UserHandle.of(mUserId)); + mContext.getSystemService(StatusBarManager.class).collapsePanels(); + } + + private void dismissNotification() { + unregisterReceiverIfNeeded(); + mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + } + + private void unregisterReceiverIfNeeded() { + if (mNotificationActionReceiver == null) { + return; + } + mContext.unregisterReceiver(mNotificationActionReceiver); + mNotificationActionReceiver = null; + } + + private class NotificationActionReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TextUtils.isEmpty(action)) return; + + mNeedToShowNotification = false; + Settings.Secure.putIntForUser(mContext.getContentResolver(), + ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId); + + if (ACTION_TURN_ON_IN_SETTINGS.equals(action)) { + launchMagnificationSettings(); + dismissNotification(); + } else if (ACTION_DISMISS.equals(action)) { + dismissNotification(); + } + } + } +} diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index e32324941aef..5fedf9f89fc1 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -62,7 +62,6 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.provider.Settings; import android.provider.SettingsStringUtil.ComponentNameSet; import android.text.BidiFormatter; @@ -88,6 +87,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import org.xmlpull.v1.XmlPullParser; diff --git a/services/core/Android.bp b/services/core/Android.bp index 4bde31fc2f05..3a137263d182 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -61,6 +61,7 @@ genrule { java_library_static { name: "services.core.unboosted", + defaults: ["platform_service_defaults"], srcs: [ ":services.core.protologsrc", ":dumpstate_aidl", @@ -146,7 +147,6 @@ java_genrule { java_library { name: "services.core", - defaults: ["platform_service_defaults"], static_libs: ["services.core.priorityboosted"], } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 9455051ad740..bb4bbd5bc6d4 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -437,6 +437,10 @@ public final class BatteryService extends SystemService { info.legacy.legacy.batteryChargeCounter); Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.legacy.legacy.batteryCurrent); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", + plugType(info.legacy.legacy)); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", + info.legacy.legacy.batteryStatus); synchronized (mLock) { if (!mUpdatesStopped) { @@ -471,6 +475,18 @@ public final class BatteryService extends SystemService { dst.batteryTechnology = src.batteryTechnology; } + private static int plugType(HealthInfo healthInfo) { + if (healthInfo.chargerAcOnline) { + return BatteryManager.BATTERY_PLUGGED_AC; + } else if (healthInfo.chargerUsbOnline) { + return BatteryManager.BATTERY_PLUGGED_USB; + } else if (healthInfo.chargerWirelessOnline) { + return BatteryManager.BATTERY_PLUGGED_WIRELESS; + } else { + return BATTERY_PLUGGED_NONE; + } + } + private void processValuesLocked(boolean force) { boolean logOutlier = false; long dischargeDuration = 0; @@ -478,15 +494,7 @@ public final class BatteryService extends SystemService { mBatteryLevelCritical = mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN && mHealthInfo.batteryLevel <= mCriticalBatteryLevel; - if (mHealthInfo.chargerAcOnline) { - mPlugType = BatteryManager.BATTERY_PLUGGED_AC; - } else if (mHealthInfo.chargerUsbOnline) { - mPlugType = BatteryManager.BATTERY_PLUGGED_USB; - } else if (mHealthInfo.chargerWirelessOnline) { - mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; - } else { - mPlugType = BATTERY_PLUGGED_NONE; - } + mPlugType = plugType(mHealthInfo); if (DEBUG) { Slog.d(TAG, "Processing new values: " diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index 66ac889ff2ca..fdda239e7fde 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -131,6 +131,7 @@ public class BinderCallsStatsService extends Binder { private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state"; private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid"; private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count"; + private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status"; private boolean mEnabled; private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS); @@ -184,6 +185,9 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setTrackDirectCallerUid( mParser.getBoolean(SETTINGS_TRACK_DIRECT_CALLING_UID_KEY, BinderCallsStats.DEFAULT_TRACK_DIRECT_CALLING_UID)); + mBinderCallsStats.setIgnoreBatteryStatus( + mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY, + BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS)); final boolean enabled = diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index f6a29aa917f2..0a684287849a 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -63,8 +63,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -77,6 +75,8 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d586f0017a1e..1eba37d8b02d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -137,7 +137,6 @@ import android.net.netlink.InetDiagMessage; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.net.util.NetdService; -import android.os.BasicShellCommandHandler; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -190,6 +189,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.LocationPermissionChecker; import com.android.internal.util.MessageUtils; import com.android.internal.util.XmlUtils; +import com.android.modules.utils.BasicShellCommandHandler; import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult; import com.android.net.module.util.LinkPropertiesUtils.CompareResult; import com.android.server.am.BatteryStatsService; diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index 965b64bd9853..de40b5207a6e 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -52,6 +52,7 @@ public class LooperStatsService extends Binder { private static final String SETTINGS_ENABLED_KEY = "enabled"; private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state"; + private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status"; private static final String DEBUG_SYS_LOOPER_STATS_ENABLED = "debug.sys.looper_stats_enabled"; private static final int DEFAULT_SAMPLING_INTERVAL = 1000; @@ -64,6 +65,7 @@ public class LooperStatsService extends Binder { // Default should be false so that the first call to #setEnabled installed the looper observer. private boolean mEnabled = false; private boolean mTrackScreenInteractive = false; + private boolean mIgnoreBatteryStatus = LooperStats.DEFAULT_IGNORE_BATTERY_STATUS; private LooperStatsService(Context context, LooperStats stats) { this.mContext = context; @@ -85,6 +87,9 @@ public class LooperStatsService extends Binder { setTrackScreenInteractive( parser.getBoolean(SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY, DEFAULT_TRACK_SCREEN_INTERACTIVE)); + setIgnoreBatteryStatus( + parser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY, + LooperStats.DEFAULT_IGNORE_BATTERY_STATUS)); // Manually specified value takes precedence over Settings. setEnabled(SystemProperties.getBoolean( DEBUG_SYS_LOOPER_STATS_ENABLED, @@ -168,6 +173,14 @@ public class LooperStatsService extends Binder { } } + private void setIgnoreBatteryStatus(boolean ignore) { + if (mIgnoreBatteryStatus != ignore) { + mStats.setIgnoreBatteryStatus(ignore); + mIgnoreBatteryStatus = ignore; + mStats.reset(); + } + } + private void setSamplingInterval(int samplingInterval) { if (samplingInterval > 0) { mStats.setSamplingInterval(samplingInterval); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 783866ab3420..8033ce787b5f 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -102,7 +102,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.storage.DiskInfo; import android.os.storage.IObbActionListener; import android.os.storage.IStorageEventListener; @@ -151,6 +150,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.pm.Installer; +import com.android.server.pm.UserManagerInternal; import com.android.server.storage.AppFuseBridge; import com.android.server.storage.StorageSessionController; import com.android.server.storage.StorageSessionController.ExternalStorageServiceException; diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 71a18218110e..5556e48a2018 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -24,7 +24,6 @@ import android.content.pm.UserInfo; import android.os.Environment; import android.os.SystemClock; import android.os.Trace; -import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.EventLog; import android.util.IndentingPrintWriter; @@ -35,6 +34,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; import com.android.server.am.EventLogTags; +import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; import dalvik.system.PathClassLoader; diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java index 76918529d071..d7e95df332fa 100644 --- a/services/core/java/com/android/server/adb/AdbShellCommand.java +++ b/services/core/java/com/android/server/adb/AdbShellCommand.java @@ -16,7 +16,7 @@ package com.android.server.adb; -import android.os.BasicShellCommandHandler; +import com.android.modules.utils.BasicShellCommandHandler; import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 01e77f8afdf3..d6f72990e4ac 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1630,6 +1630,8 @@ public final class ActiveServices { } } + // TODO: remove as part of fixing b/173627642 + @SuppressWarnings("AndroidFrameworkCompatChange") private void postFgsNotificationLocked(ServiceRecord r) { boolean showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled; if (!showNow) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 6765132ffd81..c3ba15024a78 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -37,7 +37,6 @@ import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.os.WorkSource; import android.os.connectivity.CellularBatteryStats; import android.os.connectivity.GpsBatteryStats; @@ -66,6 +65,7 @@ import com.android.internal.util.ParseUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.Watchdog; +import com.android.server.pm.UserManagerInternal; import java.io.File; import java.io.FileDescriptor; diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 057b007cfb5e..364ad21b93cf 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -806,6 +806,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN void updateKeepWarmLocked() { mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name) && (ams.mUserController.getCurrentUserId() == userId + || ams.mUserController.isCurrentProfile(userId) || ams.isSingleton(processName, appInfo, instanceName.getClassName(), serviceInfo.flags)); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 4c5b747cd167..12fcc9cc26bf 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -80,7 +80,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.text.format.DateUtils; @@ -103,6 +102,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemServiceManager; import com.android.server.am.UserState.KeyEvictedCallback; +import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.ActivityTaskManagerInternal; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 52fc93f101ab..89150aee44ad 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -121,8 +121,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; @@ -151,6 +149,8 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.audio.AudioServiceEvents.PhoneStateEvent; import com.android.server.audio.AudioServiceEvents.VolumeEvent; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserManagerService; import com.android.server.wm.ActivityTaskManagerInternal; diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 2784f46a96c2..b430521a2885 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -543,6 +543,10 @@ public class BiometricScheduler { return mCurrentOperation.clientMonitor; } + public int getCurrentPendingCount() { + return mPendingOperations.size(); + } + public void recordCrashState() { if (mCrashStates.size() >= CrashState.NUM_ENTRIES) { mCrashStates.removeFirst(); @@ -564,8 +568,22 @@ public class BiometricScheduler { public void dump(PrintWriter pw) { pw.println("Dump of BiometricScheduler " + getTag()); + pw.println("Current operation: " + mCurrentOperation); + pw.println("Pending operations: " + mPendingOperations.size()); + for (Operation operation : mPendingOperations) { + pw.println("Pending operation: " + operation); + } for (CrashState crashState : mCrashStates) { pw.println("Crash State " + crashState); } } + + /** + * Clears the scheduler of anything work-related. This should be used for example when the + * HAL dies. + */ + public void reset() { + mPendingOperations.clear(); + mCurrentOperation = null; + } } 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 0dee81681fc4..bbd652357888 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -219,7 +219,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde return mSensorId; } - public final T getFreshDaemon() { + public T getFreshDaemon() { return mLazyDaemon.getDaemon(); } 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 4906aacd06c0..47897a1a0588 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 @@ -64,6 +64,7 @@ import com.android.server.biometrics.sensors.face.hidl.Face10; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -425,6 +426,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba provider.dumpProtoMetrics(props.sensorId, fd); } } + } else if (args.length > 1 && "--hal".equals(args[0])) { + for (ServiceProvider provider : mServiceProviders) { + for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { + provider.dumpHal(props.sensorId, fd, + Arrays.copyOfRange(args, 1, args.length, args.getClass())); + } + } } else { for (ServiceProvider provider : mServiceProviders) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index d1578fa7bbf3..be4e2482acc9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -118,4 +118,6 @@ public interface ServiceProvider { @NonNull ITestSession createTestSession(int sensorId, @NonNull String opPackageName); + + void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java new file mode 100644 index 000000000000..13bbb7e8a704 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java @@ -0,0 +1,205 @@ +/* + * 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.face.aidl; + +import static android.Manifest.permission.TEST_BIOMETRIC; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.ITestSession; +import android.hardware.face.Face; +import android.hardware.face.IFaceServiceReceiver; +import android.os.Binder; +import android.util.Slog; + +import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.face.FaceUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +/** + * A test session implementation for {@link FaceProvider}. See + * {@link android.hardware.biometrics.BiometricTestSession}. + */ +public class BiometricTestSessionImpl extends ITestSession.Stub { + + private static final String TAG = "BiometricTestSessionImpl"; + + @NonNull private final Context mContext; + private final int mSensorId; + @NonNull private final FaceProvider mProvider; + @NonNull private final Sensor mSensor; + @NonNull private final Set<Integer> mEnrollmentIds; + @NonNull private final Random mRandom; + + /** + * Internal receiver currently only used for enroll. Results do not need to be forwarded to the + * test, since enrollment is a platform-only API. The authentication path is tested through + * the public BiometricPrompt APIs and does not use this receiver. + */ + private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() { + @Override + public void onEnrollResult(Face face, int remaining) { + + } + + @Override + public void onAcquired(int acquireInfo, int vendorCode) { + + } + + @Override + public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) { + + } + + @Override + public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) { + + } + + @Override + public void onAuthenticationFailed() { + + } + + @Override + public void onError(int error, int vendorCode) { + + } + + @Override + public void onRemoved(Face face, int remaining) { + + } + + @Override + public void onFeatureSet(boolean success, int feature) { + + } + + @Override + public void onFeatureGet(boolean success, int feature, boolean value) { + + } + + @Override + public void onChallengeGenerated(int sensorId, long challenge) { + + } + + @Override + public void onChallengeInterrupted(int sensorId) { + + } + + @Override + public void onChallengeInterruptFinished(int sensorId) { + + } + }; + + BiometricTestSessionImpl(@NonNull Context context, int sensorId, + @NonNull FaceProvider provider, @NonNull Sensor sensor) { + mContext = context; + mSensorId = sensorId; + mProvider = provider; + mSensor = sensor; + mEnrollmentIds = new HashSet<>(); + mRandom = new Random(); + } + + @Override + public void setTestHalEnabled(boolean enabled) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mProvider.setTestHalEnabled(enabled); + mSensor.setTestHalEnabled(enabled); + } + + @Override + public void startEnroll(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, + mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */); + } + + @Override + public void finishEnroll(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + int nextRandomId = mRandom.nextInt(); + while (mEnrollmentIds.contains(nextRandomId)) { + nextRandomId = mRandom.nextInt(); + } + + mEnrollmentIds.add(nextRandomId); + mSensor.getSessionForUser(userId).mHalSessionCallback + .onEnrollmentProgress(nextRandomId, 0 /* remaining */); + } + + @Override + public void acceptAuthentication(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + // Fake authentication with any of the existing faces + List<Face> faces = FaceUtils.getInstance(mSensorId) + .getBiometricsForUser(mContext, userId); + if (faces.isEmpty()) { + Slog.w(TAG, "No faces, returning"); + return; + } + final int fid = faces.get(0).getBiometricId(); + mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationSucceeded(fid, + HardwareAuthTokenUtils.toHardwareAuthToken(new byte[69])); + } + + @Override + public void rejectAuthentication(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed(); + } + + @Override + public void notifyAcquired(int userId, int acquireInfo) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mSensor.getSessionForUser(userId).mHalSessionCallback + .onAcquired((byte) acquireInfo, 0 /* vendorCode */); + } + + @Override + public void notifyError(int userId, int errorCode) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mSensor.getSessionForUser(userId).mHalSessionCallback.onError((byte) errorCode, + 0 /* vendorCode */); + } + + @Override + public void cleanupInternalState(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mProvider.scheduleInternalCleanup(mSensorId, userId); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java index 3280e9815660..c27b6e5a4b7d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java @@ -47,6 +47,11 @@ class FaceGetAuthenticatorIdClient extends ClientMonitor<ISession> { // Nothing to do here } + public void start(@NonNull Callback callback) { + super.start(callback); + startHalOperation(); + } + @Override protected void startHalOperation() { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 01c16fd1f4a6..26f62644e38b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitor; @@ -67,9 +68,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private static final String TAG = "FaceProvider"; private static final int ENROLL_TIMEOUT_SEC = 75; + private boolean mTestHalEnabled; + @NonNull private final Context mContext; @NonNull private final String mHalInstanceName; - @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports + @NonNull @VisibleForTesting + final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports @NonNull private final ClientMonitor.LazyDaemon<IFace> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @@ -150,6 +154,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Nullable private synchronized IFace getHalInstance() { + if (mTestHalEnabled) { + return new TestHal(); + } + if (mDaemon != null) { return mDaemon; } @@ -525,7 +533,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) { - + if (mSensors.contains(sensorId)) { + mSensors.get(sensorId).dumpProtoState(sensorId, proto); + } } @Override @@ -576,18 +586,28 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) { - return null; // TODO + return mSensors.get(sensorId).createTestSession(); } @Override + public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {} + + @Override public void binderDied() { Slog.e(getTag(), "HAL died"); mHandler.post(() -> { mDaemon = null; for (int i = 0; i < mSensors.size(); i++) { + final Sensor sensor = mSensors.valueAt(i); final int sensorId = mSensors.keyAt(i); PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); + sensor.getScheduler().recordCrashState(); + sensor.getScheduler().reset(); } }); } + + void setTestHalEnabled(boolean enabled) { + mTestHalEnabled = enabled; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index 5d100ec5e6dd..5b1f5465ea0a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -61,6 +61,11 @@ public class FaceResetLockoutClient extends ClientMonitor<ISession> { // Nothing to do here } + public void start(@NonNull Callback callback) { + super.start(callback); + startHalOperation(); + } + @Override protected void startHalOperation() { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 2dd6e7375613..d9d473722fd5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -19,7 +19,9 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.face.Error; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; @@ -31,10 +33,15 @@ import android.hardware.keymaster.HardwareAuthToken; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserManager; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.SensorServiceStateProto; +import com.android.server.biometrics.SensorStateProto; +import com.android.server.biometrics.UserStateProto; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; @@ -56,6 +63,8 @@ import java.util.Map; */ public class Sensor implements IBinder.DeathRecipient { + private boolean mTestHalEnabled; + @NonNull private final String mTag; @NonNull private final FaceProvider mProvider; @NonNull private final Context mContext; @@ -67,27 +76,6 @@ public class Sensor implements IBinder.DeathRecipient { @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession; @Nullable private Session mCurrentSession; - @Override - public void binderDied() { - Slog.e(mTag, "Binder died"); - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (client instanceof Interruptable) { - Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); - final Interruptable interruptable = (Interruptable) client; - interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - - mScheduler.recordCrashState(); - - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, - BiometricsProtoEnums.MODALITY_FACE, - BiometricsProtoEnums.ISSUE_HAL_DEATH); - mCurrentSession = null; - } - }); - } - static class Session { @NonNull final HalSessionCallback mHalSessionCallback; @NonNull private final String mTag; @@ -104,67 +92,6 @@ public class Sensor implements IBinder.DeathRecipient { } } - Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, - @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) { - mTag = tag; - mProvider = provider; - mContext = context; - mHandler = handler; - mSensorProperties = sensorProperties; - mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */); - mLockoutCache = new LockoutCache(); - mAuthenticatorIds = new HashMap<>(); - mLazySession = () -> (mCurrentSession != null) ? mCurrentSession.mSession : null; - } - - @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() { - return mLazySession; - } - - @NonNull FaceSensorPropertiesInternal getSensorProperties() { - return mSensorProperties; - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - boolean hasSessionForUser(int userId) { - return mCurrentSession != null && mCurrentSession.mUserId == userId; - } - - @Nullable Session getSessionForUser(int userId) { - if (mCurrentSession != null && mCurrentSession.mUserId == userId) { - return mCurrentSession; - } else { - return null; - } - } - - void createNewSession(@NonNull IFace daemon, int sensorId, int userId) - throws RemoteException { - - final HalSessionCallback.Callback callback = () -> { - Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); - mCurrentSession = null; - }; - final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler, - mTag, mScheduler, sensorId, userId, callback); - - final ISession newSession = daemon.createSession(sensorId, userId, resultController); - newSession.asBinder().linkToDeath(this, 0 /* flags */); - mCurrentSession = new Session(mTag, newSession, userId, resultController); - } - - @NonNull BiometricScheduler getScheduler() { - return mScheduler; - } - - @NonNull LockoutCache getLockoutCache() { - return mLockoutCache; - } - - @NonNull Map<Integer, Long> getAuthenticatorIds() { - return mAuthenticatorIds; - } - static class HalSessionCallback extends ISessionCallback.Stub { /** * Interface to sends results to the HalSessionCallback's owner. @@ -453,4 +380,120 @@ public class Sensor implements IBinder.DeathRecipient { } } + + Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, + @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) { + mTag = tag; + mProvider = provider; + mContext = context; + mHandler = handler; + mSensorProperties = sensorProperties; + mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */); + mLockoutCache = new LockoutCache(); + mAuthenticatorIds = new HashMap<>(); + mLazySession = () -> { + if (mTestHalEnabled) { + return new TestSession(mCurrentSession.mHalSessionCallback); + } else { + return mCurrentSession != null ? mCurrentSession.mSession : null; + } + }; + } + + @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() { + return mLazySession; + } + + @NonNull FaceSensorPropertiesInternal getSensorProperties() { + return mSensorProperties; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + boolean hasSessionForUser(int userId) { + return mCurrentSession != null && mCurrentSession.mUserId == userId; + } + + @Nullable Session getSessionForUser(int userId) { + if (mCurrentSession != null && mCurrentSession.mUserId == userId) { + return mCurrentSession; + } else { + return null; + } + } + + @NonNull ITestSession createTestSession() { + return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this); + } + + void createNewSession(@NonNull IFace daemon, int sensorId, int userId) + throws RemoteException { + + final HalSessionCallback.Callback callback = () -> { + Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); + mCurrentSession = null; + }; + final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler, + mTag, mScheduler, sensorId, userId, callback); + + final ISession newSession = daemon.createSession(sensorId, userId, resultController); + newSession.asBinder().linkToDeath(this, 0 /* flags */); + mCurrentSession = new Session(mTag, newSession, userId, resultController); + } + + @NonNull BiometricScheduler getScheduler() { + return mScheduler; + } + + @NonNull LockoutCache getLockoutCache() { + return mLockoutCache; + } + + @NonNull Map<Integer, Long> getAuthenticatorIds() { + return mAuthenticatorIds; + } + + void setTestHalEnabled(boolean enabled) { + mTestHalEnabled = enabled; + } + + void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) { + final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); + + proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null); + + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + + final long userToken = proto.start(SensorStateProto.USER_STATES); + proto.write(UserStateProto.USER_ID, userId); + proto.write(UserStateProto.NUM_ENROLLED, + FaceUtils.getInstance(mSensorProperties.sensorId) + .getBiometricsForUser(mContext, userId).size()); + proto.end(userToken); + } + + proto.end(sensorToken); + } + + @Override + public void binderDied() { + Slog.e(mTag, "Binder died"); + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (client instanceof Interruptable) { + Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + + mScheduler.recordCrashState(); + + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.MODALITY_FACE, + BiometricsProtoEnums.ISSUE_HAL_DEATH); + mCurrentSession = null; + } + }); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java new file mode 100644 index 000000000000..34bf9bc63a64 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java @@ -0,0 +1,98 @@ +/* + * 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.face.aidl; + +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.ISession; +import android.hardware.biometrics.face.ISessionCallback; +import android.hardware.biometrics.face.SensorProps; +import android.hardware.common.NativeHandle; +import android.hardware.keymaster.HardwareAuthToken; +import android.os.Binder; +import android.os.IBinder; + +/** + * Test HAL that provides only no-ops. + */ +public class TestHal extends IFace.Stub { + @Override + public SensorProps[] getSensorProps() { + return new SensorProps[0]; + } + + @Override + public ISession createSession(int sensorId, int userId, ISessionCallback cb) { + return new ISession() { + @Override + public void generateChallenge(int cookie, int timeoutSec) { + + } + + @Override + public void revokeChallenge(int cookie, long challenge) { + + } + + @Override + public ICancellationSignal enroll(int cookie, HardwareAuthToken hat, + NativeHandle previewSurface) { + return null; + } + + @Override + public ICancellationSignal authenticate(int cookie, long operationId) { + return null; + } + + @Override + public ICancellationSignal detectInteraction(int cookie) { + return null; + } + + @Override + public void enumerateEnrollments(int cookie) { + + } + + @Override + public void removeEnrollments(int cookie, int[] enrollmentIds) { + + } + + @Override + public void getAuthenticatorId(int cookie) { + + } + + @Override + public void invalidateAuthenticatorId(int cookie) { + + } + + @Override + public void resetLockout(int cookie, HardwareAuthToken hat) { + + } + + @Override + public IBinder asBinder() { + return new Binder(); + } + }; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java new file mode 100644 index 000000000000..9707eddfee74 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java @@ -0,0 +1,102 @@ +/* + * 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.face.aidl; + +import android.annotation.NonNull; +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.face.Error; +import android.hardware.biometrics.face.ISession; +import android.hardware.common.NativeHandle; +import android.hardware.keymaster.HardwareAuthToken; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * Test session that provides mostly no-ops. + */ +public class TestSession extends ISession.Stub { + private static final String TAG = "TestSession"; + + @NonNull + private final Sensor.HalSessionCallback mHalSessionCallback; + + TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) { + mHalSessionCallback = halSessionCallback; + } + + @Override + public void generateChallenge(int cookie, int timeoutSec) { + mHalSessionCallback.onChallengeGenerated(0 /* challenge */); + } + + @Override + public void revokeChallenge(int cookie, long challenge) { + mHalSessionCallback.onChallengeRevoked(challenge); + } + + @Override + public ICancellationSignal enroll(int cookie, HardwareAuthToken hat, + NativeHandle previewSurface) { + return null; + } + + @Override + public ICancellationSignal authenticate(int cookie, long operationId) { + return new ICancellationSignal() { + @Override + public void cancel() throws RemoteException { + mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */); + } + + @Override + public IBinder asBinder() { + return new Binder(); + } + }; + } + + @Override + public ICancellationSignal detectInteraction(int cookie) { + return null; + } + + @Override + public void enumerateEnrollments(int cookie) { + + } + + @Override + public void removeEnrollments(int cookie, int[] enrollmentIds) { + + } + + @Override + public void getAuthenticatorId(int cookie) { + mHalSessionCallback.onAuthenticatorIdRetrieved(0); + } + + @Override + public void invalidateAuthenticatorId(int cookie) { + + } + + @Override + public void resetLockout(int cookie, HardwareAuthToken hat) { + mHalSessionCallback.onLockoutCleared(); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 27ca33de415d..3555bbe629ae 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -333,16 +333,17 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @VisibleForTesting - public Face10(@NonNull Context context, int sensorId, + Face10(@NonNull Context context, int sensorId, @BiometricManager.Authenticators.Types int strength, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - boolean supportsSelfIllumination, int maxTemplatesAllowed) { + boolean supportsSelfIllumination, int maxTemplatesAllowed, + @NonNull BiometricScheduler scheduler) { mSensorProperties = new FaceSensorPropertiesInternal(sensorId, Utils.authenticatorStrengthToPropertyStrength(strength), maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination); mContext = context; mSensorId = sensorId; - mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */); + mScheduler = scheduler; mHandler = new Handler(Looper.getMainLooper()); mUsageStats = new UsageStats(context); mAuthenticatorIds = new HashMap<>(); @@ -369,7 +370,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull LockoutResetDispatcher lockoutResetDispatcher) { this(context, sensorId, strength, lockoutResetDispatcher, context.getResources().getBoolean(R.bool.config_faceAuthSupportsSelfIllumination), - context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)); + context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser), + new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */)); } @Override @@ -388,12 +390,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mScheduler.recordCrashState(); - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, BiometricsProtoEnums.MODALITY_FACE, BiometricsProtoEnums.ISSUE_HAL_DEATH); } + + mScheduler.recordCrashState(); + mScheduler.reset(); }); } @@ -874,7 +877,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { }); } - public void dumpHal(@NonNull FileDescriptor fd, @NonNull String[] args) { + /** + * Sends a debug message to the HAL with the provided FileDescriptor and arguments. + */ + public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) { // WARNING: CDD restricts image data from leaving TEE unencrypted on // production devices: // [C-1-10] MUST not allow unencrypted access to identifiable biometric diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java index 973132533661..3f9aef2b8651 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -143,7 +143,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); // Fake authentication with any of the existing fingers - List<Fingerprint> fingerprints = FingerprintUtils.getInstance() + List<Fingerprint> fingerprints = FingerprintUtils.getInstance(mSensorId) .getBiometricsForUser(mContext, userId); if (fingerprints.isEmpty()) { Slog.w(TAG, "No fingerprints, returning"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 5f3be488c12c..db34d1444650 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitor; @@ -67,7 +68,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final Context mContext; @NonNull private final String mHalInstanceName; - @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports + @NonNull @VisibleForTesting + final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @@ -607,8 +609,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mDaemon = null; for (int i = 0; i < mSensors.size(); i++) { + final Sensor sensor = mSensors.valueAt(i); final int sensorId = mSensors.keyAt(i); PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); + sensor.getScheduler().recordCrashState(); + sensor.getScheduler().reset(); } }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index 380608f1b0a9..ecb998594d44 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -80,27 +80,6 @@ class Sensor implements IBinder.DeathRecipient { @Nullable private Session mCurrentSession; @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession; - @Override - public void binderDied() { - Slog.e(mTag, "Binder died"); - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (client instanceof Interruptable) { - Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); - final Interruptable interruptable = (Interruptable) client; - interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - - mScheduler.recordCrashState(); - - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, - BiometricsProtoEnums.MODALITY_FINGERPRINT, - BiometricsProtoEnums.ISSUE_HAL_DEATH); - mCurrentSession = null; - } - }); - } - static class Session { @NonNull private final String mTag; @NonNull private final ISession mSession; @@ -501,11 +480,33 @@ class Sensor implements IBinder.DeathRecipient { final long userToken = proto.start(SensorStateProto.USER_STATES); proto.write(UserStateProto.USER_ID, userId); - proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance() - .getBiometricsForUser(mContext, userId).size()); + proto.write(UserStateProto.NUM_ENROLLED, + FingerprintUtils.getInstance(mSensorProperties.sensorId) + .getBiometricsForUser(mContext, userId).size()); proto.end(userToken); } proto.end(sensorToken); } + + @Override + public void binderDied() { + Slog.e(mTag, "Binder died"); + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (client instanceof Interruptable) { + Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + + mScheduler.recordCrashState(); + + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + BiometricsProtoEnums.ISSUE_HAL_DEATH); + mCurrentSession = null; + } + }); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java index dd9150a32914..d6378780594f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java @@ -23,7 +23,7 @@ import android.hardware.keymaster.HardwareAuthToken; import android.util.Slog; /** - * Test HAL that provides only provides mostly no-ops. + * Test session that provides mostly no-ops. */ class TestSession extends ISession.Stub { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 9924d4710436..11372a30599d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -386,12 +386,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mScheduler.recordCrashState(); - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ISSUE_HAL_DEATH); } + + mScheduler.recordCrashState(); + mScheduler.reset(); }); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 4d959d08d3ef..bdd315de32b1 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -207,7 +207,8 @@ public class Vpn { @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; - private String mInterface; + @VisibleForTesting + protected String mInterface; private Connection mConnection; /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */ diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index d0cc8e71dbfa..42dcbeb73fff 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -501,15 +501,6 @@ final class Constants { static final int DISABLED = 0; static final int ENABLED = 1; - @IntDef({ - VERSION_1_4, - VERSION_2_0 - }) - @interface CecVersion {} - static final int VERSION_1_3 = 0x04; - static final int VERSION_1_4 = 0x05; - static final int VERSION_2_0 = 0x06; - static final int ALL_DEVICE_TYPES_TV = 7; static final int ALL_DEVICE_TYPES_RECORDER = 6; static final int ALL_DEVICE_TYPES_TUNER = 5; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index e7f302c1977a..fb71d9510627 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -18,6 +18,7 @@ package com.android.server.hdmi; import android.annotation.CallSuper; import android.annotation.Nullable; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.input.InputManager; @@ -560,7 +561,7 @@ abstract class HdmiCecLocalDevice { protected abstract List<Integer> getDeviceFeatures(); protected boolean handleGiveFeatures(HdmiCecMessage message) { - if (mService.getCecVersion() < Constants.VERSION_2_0) { + if (mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) { return false; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 1a481b632606..96303ce9a76c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -16,10 +16,10 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import com.android.server.hdmi.Constants.AudioCodec; -import com.android.server.hdmi.Constants.CecVersion; import java.io.UnsupportedEncodingException; import java.util.Arrays; @@ -696,9 +696,9 @@ public class HdmiCecMessageBuilder { return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES); } - static HdmiCecMessage buildReportFeatures(int src, @CecVersion int cecVersion, - List<Integer> allDeviceTypes, int rcProfile, List<Integer> rcFeatures, - List<Integer> deviceFeatures) { + static HdmiCecMessage buildReportFeatures(int src, + @HdmiControlManager.HdmiCecVersion int cecVersion, List<Integer> allDeviceTypes, + int rcProfile, List<Integer> rcFeatures, List<Integer> deviceFeatures) { byte cecVersionByte = (byte) (cecVersion & 0xFF); byte deviceTypes = 0; for (Integer deviceType : allDeviceTypes) { diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 7fc3d2b302ff..c4c0f688bd67 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -31,7 +31,6 @@ import android.os.Binder; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; @@ -43,7 +42,7 @@ import com.android.internal.infra.AbstractRemoteService; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; +import com.android.server.pm.UserManagerInternal; import java.io.PrintWriter; import java.lang.annotation.Retention; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 2d7f62d90914..a4844f6931ed 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -111,7 +111,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.provider.Settings; import android.text.TextUtils; import android.text.style.SuggestionSpan; @@ -184,6 +183,7 @@ import com.android.server.SystemService; import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener; import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.wm.WindowManagerInternal; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 77e2fbd25670..0e908d471f74 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -30,7 +30,6 @@ import android.os.Build; import android.os.LocaleList; import android.os.RemoteException; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; @@ -46,6 +45,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.StartInputFlags; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import com.android.server.textservices.TextServicesManagerInternal; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java index 8c1afab00b9a..7d3ccbf5a1b4 100644 --- a/services/core/java/com/android/server/location/LocationShellCommand.java +++ b/services/core/java/com/android/server/location/LocationShellCommand.java @@ -16,9 +16,10 @@ package com.android.server.location; -import android.os.BasicShellCommandHandler; import android.os.UserHandle; +import com.android.modules.utils.BasicShellCommandHandler; + import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ad96e7633283..f7f34407b867 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -83,7 +83,6 @@ import android.os.StrictMode; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; @@ -130,6 +129,7 @@ import com.android.server.locksettings.LockSettingsStorage.PersistentData; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.WindowManagerInternal; import libcore.util.HexEncoding; diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java index 285e722886c2..9857fb637b59 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java @@ -16,24 +16,39 @@ package com.android.server.locksettings.recoverablekeystore; +import android.security.keystore2.AndroidKeyStoreProvider; + import java.io.IOException; -import java.security.cert.CertificateException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; /** * Implementation of {@link KeyStoreProxy} that delegates all method calls to the {@link KeyStore}. */ public class KeyStoreProxyImpl implements KeyStoreProxy { - private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private final KeyStore mKeyStore; /** + * TODO This function redirects keystore access to the legacy keystore during a transitional + * phase during which not all calling code has been adjusted to use Keystore 2.0. + * This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete. + * The specific bug for this component is b/171305545. + */ + static String androidKeystoreProviderName() { + if (AndroidKeyStoreProvider.isInstalled()) { + return "AndroidKeyStoreLegacy"; + } else { + return "AndroidKeyStore"; + } + + } + + /** * A new instance, delegating to {@code keyStore}. */ public KeyStoreProxyImpl(KeyStore keyStore) { @@ -69,7 +84,7 @@ public class KeyStoreProxyImpl implements KeyStoreProxy { * @throws KeyStoreException if there was a problem getting or initializing the key store. */ public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); try { keyStore.load(/*param=*/ null); } catch (CertificateException | IOException | NoSuchAlgorithmException e) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java index cb5ca85b0e18..dff1df7afa11 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -86,8 +86,6 @@ public class PlatformKeyManager { private final KeyStoreProxy mKeyStore; private final RecoverableKeyStoreDb mDatabase; - private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; - /** * A new instance operating on behalf of {@code userId}, storing its prefs in the location * defined by {@code context}. @@ -472,7 +470,7 @@ public class PlatformKeyManager { * @throws KeyStoreException if there was a problem getting or initializing the key store. */ private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName()); try { keyStore.load(/*param=*/ null); } catch (CertificateException | IOException | NoSuchAlgorithmException e) { diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java index 6e82c24a6e9b..f19d353a1f7b 100644 --- a/services/core/java/com/android/server/oemlock/OemLockService.java +++ b/services/core/java/com/android/server/oemlock/OemLockService.java @@ -28,14 +28,14 @@ import android.os.IBinder; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.service.oemlock.IOemLockService; import android.util.Slog; import com.android.server.LocalServices; import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemService; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserRestrictionsUtils; /** diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index dd338655732b..c4a23f961072 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -73,7 +73,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.provider.Settings; import android.util.Log; import android.util.Pair; @@ -775,6 +774,9 @@ public class LauncherAppsService extends SystemService { throw new IllegalArgumentException( "To query by locus ID, package name must also be set"); } + if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) { + ensureStrictAccessShortcutsPermission(callingPackage); + } // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below. return new ParceledListSlice<>((List<ShortcutInfo>) diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 4a799b5f86a9..4cee2e58d20e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -517,7 +517,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements String installerAttributionTag, int userId) throws IOException { final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission( + mPm.enforceCrossUserPermission( callingUid, userId, true, true, "createSession"); if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { @@ -912,7 +912,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public ParceledListSlice<SessionInfo> getAllSessions(int userId) { final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission( + mPm.enforceCrossUserPermission( callingUid, userId, true, false, "getAllSessions"); final List<SessionInfo> result = new ArrayList<>(); @@ -930,7 +930,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) { - mPermissionManager.enforceCrossUserPermission( + mPm.enforceCrossUserPermission( Binder.getCallingUid(), userId, true, false, "getMySessions"); mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName); @@ -954,7 +954,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId) { final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); + mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) { mAppOps.checkPackage(callingUid, callerPackageName); } @@ -1006,7 +1006,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements String callerPackageName, IntentSender statusReceiver, int userId) { final int callingUid = Binder.getCallingUid(); mContext.enforceCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES, null); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); + mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) { mAppOps.checkPackage(callingUid, callerPackageName); } @@ -1037,7 +1037,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public void registerCallback(IPackageInstallerCallback callback, int userId) { - mPermissionManager.enforceCrossUserPermission( + mPm.enforceCrossUserPermission( Binder.getCallingUid(), userId, true, false, "registerCallback"); registerCallback(callback, eventUserId -> userId == eventUserId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a03c405c79f0..ad686f2cab89 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -160,6 +160,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; +import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; @@ -274,7 +275,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.incremental.IStorageHealthListener; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; @@ -4632,8 +4632,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) { throw new SecurityException("User doesn't exist"); } - mPermissionManager.enforceCrossUserPermission( - callingUid, userId, false, false, "checkPackageStartable"); + enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable"); final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); @@ -4664,8 +4663,8 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isPackageAvailable(String packageName, int userId) { if (!mUserManager.exists(userId)) return false; final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, "is package available"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "is package available"); synchronized (mLock) { AndroidPackage p = mPackages.get(packageName); if (p != null) { @@ -4707,7 +4706,7 @@ public class PackageManagerService extends IPackageManager.Stub int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId); - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader @@ -5009,8 +5008,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return -1; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "getPackageUid"); return getPackageUidInternal(packageName, flags, userId, callingUid); } @@ -5042,8 +5041,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "getPackageGids"); // reader synchronized (mLock) { @@ -5126,7 +5125,7 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForApplication(flags, userId); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get application info"); } @@ -5430,8 +5429,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & PackageManager.MATCH_ANY_USER) != 0) { // require the permission to be held; the calling uid and given user id referring // to the same user is not sufficient - mPermissionManager.enforceCrossUserPermission( - Binder.getCallingUid(), userId, false, false, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId), "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at " + Debug.getCallers(5)); @@ -5553,7 +5551,7 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForComponent(flags, userId); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); } @@ -5635,8 +5633,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /* requireFullPermission */, false /* checkShell */, "get receiver info"); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "get receiver info"); synchronized (mLock) { ParsedActivity a = mComponentResolver.getReceiver(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -5744,9 +5742,8 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES, "getDeclaredSharedLibraries"); int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "getDeclaredSharedLibraries"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "getDeclaredSharedLibraries"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0"); @@ -5860,9 +5857,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId); - mPermissionManager.enforceCrossUserOrProfilePermission( - callingUid, userId, false /* requireFullPermission */, false /* checkShell */, - "get service info"); + enforceCrossUserOrProfilePermission(callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "get service info"); synchronized (mLock) { ParsedService s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -5891,8 +5887,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /* requireFullPermission */, false /* checkShell */, "get provider info"); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "get provider info"); synchronized (mLock) { ParsedProvider p = mComponentResolver.getProvider(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -6029,8 +6025,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) { return null; } - mPermissionManager.enforceCrossUserPermission( - callingUid, userId, false, false, "getChangedPackages"); + enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages"); synchronized (mLock) { if (sequenceNumber >= mChangedPackagesSequenceNumber) { return null; @@ -6655,8 +6650,8 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, flags)); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, @@ -7363,7 +7358,7 @@ public class PackageManagerService extends IPackageManager.Stub int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!mUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); @@ -8170,9 +8165,8 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, flags)); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, - "query intent activity options"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags @@ -8351,9 +8345,8 @@ public class PackageManagerService extends IPackageManager.Stub String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!mUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, - "query intent receivers"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, @@ -8473,7 +8466,7 @@ public class PackageManagerService extends IPackageManager.Stub String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!mUserManager.exists(userId)) return Collections.emptyList(); - mPermissionManager.enforceCrossUserOrProfilePermission(callingUid, + enforceCrossUserOrProfilePermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, @@ -8757,9 +8750,8 @@ public class PackageManagerService extends IPackageManager.Stub final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /* requireFullPermission */, false /* checkShell */, - "get installed packages"); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "get installed packages"); // writer synchronized (mLock) { @@ -8869,9 +8861,8 @@ public class PackageManagerService extends IPackageManager.Stub String[] permissions, int flags, int userId) { if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId); - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, false /* checkShell */, - "get packages holding permissions"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, + false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer @@ -8913,7 +8904,7 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForApplication(flags, userId); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; - mPermissionManager.enforceCrossUserPermission( + enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, @@ -8987,9 +8978,8 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, false /* checkShell */, - "getEphemeralApplications"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, + false /* checkShell */, "getEphemeralApplications"); synchronized (mLock) { List<InstantAppInfo> instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); @@ -9003,9 +8993,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isInstantApp(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "isInstantApp"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "isInstantApp"); return isInstantAppInternal(packageName, userId, callingUid); } @@ -9039,9 +9028,8 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, false /* checkShell */, - "getInstantAppCookie"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, + false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } @@ -9057,9 +9045,8 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, true /* checkShell */, - "setInstantAppCookie"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, + true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } @@ -9079,9 +9066,8 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, false /* checkShell */, - "getInstantAppIcon"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, + false /* checkShell */, "getInstantAppIcon"); synchronized (mLock) { return mInstantAppRegistry.getInstantAppIconLPw( @@ -9157,8 +9143,7 @@ public class PackageManagerService extends IPackageManager.Stub } } if (!checkedGrants) { - mPermissionManager.enforceCrossUserPermission( - callingUid, userId, false, false, "resolveContentProvider"); + enforceCrossUserPermission(callingUid, userId, false, false, "resolveContentProvider"); } if (providerInfo == null) { return null; @@ -9843,6 +9828,171 @@ public class PackageManagerService extends IPackageManager.Stub } } + /** + * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS + * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller. + * + * @param checkShell whether to prevent shell from access if there's a debugging restriction + * @param message the message to log on security exception + */ + void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, + boolean requireFullPermission, boolean checkShell, String message) { + enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false, + message); + } + + /** + * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS + * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller. + * + * @param checkShell whether to prevent shell from access if there's a debugging restriction + * @param requirePermissionWhenSameUser When {@code true}, still require the cross user + * permission to be held even if the callingUid and userId + * reference the same user. + * @param message the message to log on security exception + */ + private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, + boolean requireFullPermission, boolean checkShell, + boolean requirePermissionWhenSameUser, String message) { + if (userId < 0) { + throw new IllegalArgumentException("Invalid userId " + userId); + } + if (checkShell) { + PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); + } + final int callingUserId = UserHandle.getUserId(callingUid); + if (hasCrossUserPermission( + callingUid, callingUserId, userId, requireFullPermission, + requirePermissionWhenSameUser)) { + return; + } + String errorMessage = buildInvalidCrossUserPermissionMessage( + callingUid, userId, message, requireFullPermission); + Slog.w(TAG, errorMessage); + throw new SecurityException(errorMessage); + } + + /** + * Checks if the request is from the system or an app that has the appropriate cross-user + * permissions defined as follows: + * <ul> + * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li> + * <li>INTERACT_ACROSS_USERS if the given {@code userId} is in a different profile group + * to the caller.</li> + * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@code userId} is in the same profile + * group as the caller.</li> + * </ul> + * + * @param checkShell whether to prevent shell from access if there's a debugging restriction + * @param message the message to log on security exception + */ + private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, + boolean requireFullPermission, boolean checkShell, String message) { + if (userId < 0) { + throw new IllegalArgumentException("Invalid userId " + userId); + } + if (checkShell) { + PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); + } + final int callingUserId = UserHandle.getUserId(callingUid); + if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission, + /*requirePermissionWhenSameUser= */ false)) { + return; + } + final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId); + if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight( + mContext, + android.Manifest.permission.INTERACT_ACROSS_PROFILES, + PermissionChecker.PID_UNKNOWN, + callingUid, + mPmInternal.getPackage(callingUid).getPackageName()) + == PermissionChecker.PERMISSION_GRANTED) { + return; + } + String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage( + callingUid, userId, message, requireFullPermission, isSameProfileGroup); + Slog.w(TAG, errorMessage); + throw new SecurityException(errorMessage); + } + + private boolean hasCrossUserPermission( + int callingUid, int callingUserId, int userId, boolean requireFullPermission, + boolean requirePermissionWhenSameUser) { + if (!requirePermissionWhenSameUser && userId == callingUserId) { + return true; + } + if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { + return true; + } + if (requireFullPermission) { + return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + } + return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS); + } + + private boolean hasPermission(String permission) { + return mContext.checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; + } + + private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { + final long identity = Binder.clearCallingIdentity(); + try { + return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private static String buildInvalidCrossUserPermissionMessage(int callingUid, + @UserIdInt int userId, String message, boolean requireFullPermission) { + StringBuilder builder = new StringBuilder(); + if (message != null) { + builder.append(message); + builder.append(": "); + } + builder.append("UID "); + builder.append(callingUid); + builder.append(" requires "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + if (!requireFullPermission) { + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); + } + builder.append(" to access user "); + builder.append(userId); + builder.append("."); + return builder.toString(); + } + + private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid, + @UserIdInt int userId, String message, boolean requireFullPermission, + boolean isSameProfileGroup) { + StringBuilder builder = new StringBuilder(); + if (message != null) { + builder.append(message); + builder.append(": "); + } + builder.append("UID "); + builder.append(callingUid); + builder.append(" requires "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + if (!requireFullPermission) { + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); + if (isSameProfileGroup) { + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES); + } + } + builder.append(" to access user "); + builder.append("."); + return builder.toString(); + } + @Override public void performFstrimIfNeeded() { enforceSystemOrRoot("Only the system can request fstrim"); @@ -13271,9 +13421,8 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); PackageSetting pkgSetting; final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, true /* checkShell */, - "setApplicationHiddenSetting for user " + userId); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + true /* checkShell */, "setApplicationHiddenSetting for user " + userId); if (hidden && isPackageDeviceAdmin(packageName, userId)) { Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin"); @@ -13459,9 +13608,8 @@ public class PackageManagerService extends IPackageManager.Stub public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "getApplicationHidden for user " + userId); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "getApplicationHidden for user " + userId); PackageSetting ps; final long callingId = Binder.clearCallingIdentity(); try { @@ -13511,9 +13659,8 @@ public class PackageManagerService extends IPackageManager.Stub + android.Manifest.permission.INSTALL_PACKAGES + "."); } PackageSetting pkgSetting; - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, true /* checkShell */, - "installExistingPackage for user " + userId); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + true /* checkShell */, "installExistingPackage for user " + userId); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { return PackageManager.INSTALL_FAILED_USER_RESTRICTED; } @@ -13878,9 +14025,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageSuspendedForUser(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "isPackageSuspendedForUser for user " + userId); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "isPackageSuspendedForUser for user " + userId); synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { @@ -17718,6 +17864,17 @@ public class PackageManagerService extends IPackageManager.Stub } } + /* + * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges + * as this only works for packages that are installed + * + * TODO: Move logic for permission group compatibility into PermissionManagerService + */ + @SuppressWarnings("AndroidFrameworkCompatChange") + private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) { + return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S; + } + @GuardedBy("mInstallLock") private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res) throws PrepareFailure { @@ -17760,7 +17917,7 @@ public class PackageManagerService extends IPackageManager.Stub | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); - ParsedPackage parsedPackage; + final ParsedPackage parsedPackage; try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null, mPackageParserCallback)) { parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); @@ -17876,15 +18033,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - /* - * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges - * as this only works for packages that are installed - * - * TODO: Move logic for permission group compatibility into PermissionManagerService - */ - boolean cannotInstallWithBadPermissionGroups = - parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S; - PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); @@ -17942,7 +18090,8 @@ public class PackageManagerService extends IPackageManager.Stub parsedPackage.getPermissionGroups().get(groupNum); final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0); - if (sourceGroup != null && cannotInstallWithBadPermissionGroups) { + if (sourceGroup != null + && cannotInstallWithBadPermissionGroups(parsedPackage)) { final String sourcePackageName = sourceGroup.packageName; if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName)) @@ -18017,7 +18166,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (perm.getGroup() != null && cannotInstallWithBadPermissionGroups) { + if (perm.getGroup() != null + && cannotInstallWithBadPermissionGroups(parsedPackage)) { boolean isPermGroupDefinedByPackage = false; for (int groupNum = 0; groupNum < numGroups; groupNum++) { if (parsedPackage.getPermissionGroups().get(groupNum).getName() @@ -19987,8 +20137,8 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.CLEAR_APP_USER_DATA, null); final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, "clear application data"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "clear application data"); final boolean filterApp; synchronized (mLock) { @@ -20141,9 +20291,8 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null); } - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - /* requireFullPermission= */ true, /* checkShell= */ false, - "delete application cache files"); + enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true, + /* checkShell= */ false, "delete application cache files"); final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission( android.Manifest.permission.ACCESS_INSTANT_APPS); @@ -20269,8 +20418,8 @@ public class PackageManagerService extends IPackageManager.Stub String opname, boolean removeExisting) { // writer int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, "add preferred activity"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "add preferred activity"); if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { @@ -20344,9 +20493,8 @@ public class PackageManagerService extends IPackageManager.Stub } final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "replace preferred activity"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "replace preferred activity"); if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { @@ -21555,8 +21703,8 @@ public class PackageManagerService extends IPackageManager.Stub permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); } - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /* requireFullPermission */, true /* checkShell */, "set enabled"); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, + true /* checkShell */, "set enabled"); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); boolean sendNow = false; boolean isApp = (className == null); @@ -21781,7 +21929,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) { return; } - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/, false /* checkShell */, "flushPackageRestrictions"); synchronized (mLock) { flushPackageRestrictionsAsUserInternalLocked(userId); @@ -21846,8 +21994,8 @@ public class PackageManagerService extends IPackageManager.Stub final int permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, true /* checkShell */, "stop package"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + true /* checkShell */, "stop package"); // writer synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); @@ -21994,8 +22142,8 @@ public class PackageManagerService extends IPackageManager.Stub public int getApplicationEnabledSetting(String packageName, int userId) { if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED; int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /* requireFullPermission */, false /* checkShell */, "get enabled"); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "get enabled"); // reader synchronized (mLock) { try { @@ -22015,8 +22163,8 @@ public class PackageManagerService extends IPackageManager.Stub if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT; if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED; int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + false /*checkShell*/, "getComponentEnabled"); synchronized (mLock) { try { if (shouldFilterApplicationLocked( @@ -26111,9 +26259,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int getInstallReason(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "get install reason"); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, + false /* checkShell */, "get install reason"); synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (shouldFilterApplicationLocked(ps, callingUid, userId)) { @@ -26195,9 +26342,8 @@ public class PackageManagerService extends IPackageManager.Stub public String getInstantAppAndroidId(String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppAndroidId"); - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, false /* checkShell */, - "getInstantAppAndroidId"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, + false /* checkShell */, "getInstantAppAndroidId"); // Make sure the target is an Instant App. if (!isInstantApp(packageName, userId)) { return null; @@ -26313,8 +26459,8 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingAppId = UserHandle.getAppId(callingUid); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo"); + enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/, + true /*checkShell*/, "setHarmfulAppInfo"); if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID && checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) { @@ -26334,8 +26480,8 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingAppId = UserHandle.getAppId(callingUid); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo"); + enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/, + true /*checkShell*/, "getHarmfulAppInfo"); if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID && checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) { @@ -26353,8 +26499,8 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingAppId = UserHandle.getAppId(callingUid); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, true /*checkShell*/, "isPackageStateProtected"); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, + true /*checkShell*/, "isPackageStateProtected"); if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID && checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index b05fd47383fd..9f1d6566e279 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -46,7 +46,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.os.incremental.IncrementalManager; import android.os.incremental.V4Signature; import android.os.incremental.V4Signature.HashingInfo; diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 69721d0769cb..804faa1b7f0f 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -252,43 +252,6 @@ public class PreferredComponent { return numMatch == NS; } - public boolean sameSet(PreferredComponent pc) { - if (mSetPackages == null || pc == null || pc.mSetPackages == null - || !sameComponent(pc.mComponent)) { - return false; - } - final int otherPackageCount = pc.mSetPackages.length; - final int packageCount = mSetPackages.length; - int numMatch = 0; - for (int i = 0; i < otherPackageCount; i++) { - boolean good = false; - for (int j = 0; j < packageCount; j++) { - if (mSetPackages[j].equals(pc.mSetPackages[j]) - && mSetClasses[j].equals(pc.mSetClasses[j])) { - numMatch++; - good = true; - break; - } - } - if (!good) { - return false; - } - } - return numMatch == packageCount; - } - - /** Returns true if the preferred component represents the provided ComponentName. */ - private boolean sameComponent(ComponentName comp) { - if (mComponent == null || comp == null) { - return false; - } - if (mComponent.getPackageName().equals(comp.getPackageName()) - && mComponent.getClassName().equals(comp.getClassName())) { - return true; - } - return false; - } - public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) { if (mSetPackages == null) { return query == null; diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java index ff3df130a3cc..a261e29b05a7 100644 --- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java @@ -22,7 +22,6 @@ import android.content.IntentFilter; import java.io.PrintWriter; import com.android.server.IntentResolver; -import java.util.ArrayList; public class PreferredIntentResolver extends IntentResolver<PreferredActivity, PreferredActivity> { @@ -46,24 +45,4 @@ public class PreferredIntentResolver protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) { return input; } - - public boolean shouldAddPreferredActivity(PreferredActivity pa) { - ArrayList<PreferredActivity> pal = findFilters(pa); - if (pal == null || pal.isEmpty()) { - return true; - } - if (!pa.mPref.mAlways) { - return false; - } - final int activityCount = pal.size(); - for (int i = 0; i < activityCount; i++) { - PreferredActivity cur = pal.get(i); - if (cur.mPref.mAlways - && cur.mPref.mMatch == (pa.mPref.mMatch & IntentFilter.MATCH_CATEGORY_MASK) - && cur.mPref.sameSet(pa.mPref)) { - return false; - } - } - return true; - } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9a5e05f38819..966090cb96a7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1312,7 +1312,8 @@ public final class Settings { PreferredActivity pa = new PreferredActivity(parser); if (pa.mPref.getParseError() == null) { final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId); - if (resolver.shouldAddPreferredActivity(pa)) { + ArrayList<PreferredActivity> pal = resolver.findFilters(pa); + if (pal == null || pal.size() == 0 || pa.mPref.mAlways) { resolver.addFilter(pa); } } else { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index d7c46a50e1c5..2d771820865d 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -83,7 +83,6 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.ArraySet; @@ -2838,10 +2837,14 @@ public class ShortcutService extends IShortcutService.Stub { int queryFlags, int userId, int callingPid, int callingUid) { final ArrayList<ShortcutInfo> ret = new ArrayList<>(); - final boolean cloneKeyFieldOnly = - ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); - final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO - : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER; + int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER; + if ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0) { + flags = ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO; + } else if ((queryFlags & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) { + flags &= ~ShortcutInfo.CLONE_REMOVE_PERSON; + } + final int cloneFlag = flags; + if (packageName == null) { shortcutIds = null; // LauncherAppsService already threw for it though. } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index d535f7b8e0f7..85c4ab2c8a10 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -49,7 +49,6 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.text.TextUtils; diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index 76b5ba5eb82d..47d0a3d561b1 100644 --- a/services/core/java/android/os/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.os; +package com.android.server.pm; import android.annotation.IntDef; import android.annotation.NonNull; @@ -22,8 +22,8 @@ import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.UserInfo; import android.graphics.Bitmap; - -import com.android.server.pm.RestrictionsSet; +import android.os.Bundle; +import android.os.UserManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 14352f3ddc49..c51e75c716cc 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -78,8 +78,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.EnforcingUser; import android.os.UserManager.QuietModeFlag; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; @@ -112,6 +110,7 @@ import com.android.server.LocalServices; import com.android.server.LockGuard; import com.android.server.SystemService; import com.android.server.am.UserState; +import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.ActivityTaskManagerInternal; diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 145dd24e2998..d0c3a95eafc7 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -33,7 +33,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.provider.Settings; import android.provider.Settings.Global; import android.telephony.SubscriptionInfo; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index f81906399be2..6c03a28d03c7 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -77,7 +77,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -96,6 +95,7 @@ import android.content.pm.permission.SplitPermissionInfoParcelable; import android.metrics.LogMaker; import android.os.Binder; import android.os.Build; +import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -107,7 +107,6 @@ import android.os.ServiceManager; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.storage.StorageManager; import android.permission.IOnPermissionsChangeListener; import android.permission.IPermissionManager; @@ -147,6 +146,7 @@ import com.android.server.pm.ApexManager; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; +import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -754,7 +754,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { enforceCrossUserPermission(callingUid, userId, true, // requireFullPermission false, // checkShell - false, // requirePermissionWhenSameUser "getPermissionFlags"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); @@ -841,7 +840,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { enforceCrossUserPermission(callingUid, userId, true, // requireFullPermission true, // checkShell - false, // requirePermissionWhenSameUser "updatePermissionFlags"); if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) { @@ -951,7 +949,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { enforceCrossUserPermission(callingUid, userId, true, // requireFullPermission true, // checkShell - false, // requirePermissionWhenSameUser "updatePermissionFlagsForAllApps"); // Only the system can change system fixed flags. @@ -1555,7 +1552,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { enforceCrossUserPermission(callingUid, userId, true, // requireFullPermission true, // checkShell - false, // requirePermissionWhenSameUser "grantRuntimePermission"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); @@ -1722,7 +1718,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { enforceCrossUserPermission(callingUid, userId, true, // requireFullPermission true, // checkShell - false, // requirePermissionWhenSameUser "revokeRuntimePermission"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); @@ -4494,25 +4489,22 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** - * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS - * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. + * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS + * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller. + * * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, - boolean requireFullPermission, boolean checkShell, - boolean requirePermissionWhenSameUser, String message) { + boolean requireFullPermission, boolean checkShell, @Nullable String message) { if (userId < 0) { throw new IllegalArgumentException("Invalid userId " + userId); } if (checkShell) { - PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt, - UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); + enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); } final int callingUserId = UserHandle.getUserId(callingUid); - if (hasCrossUserPermission( - callingUid, callingUserId, userId, requireFullPermission, - requirePermissionWhenSameUser)) { + if (checkCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission)) { return; } String errorMessage = buildInvalidCrossUserPermissionMessage( @@ -4522,82 +4514,45 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** - * Checks if the request is from the system or an app that has the appropriate cross-user - * permissions defined as follows: - * <ul> - * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li> - * <li>INTERACT_ACROSS_USERS if the given {@userId} is in a different profile group - * to the caller.</li> - * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@userId} is in the same profile group - * as the caller.</li> - * </ul> - * - * @param checkShell whether to prevent shell from access if there's a debugging restriction - * @param message the message to log on security exception + * Enforces that if the caller is shell, it does not have the provided user restriction. */ - private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, - boolean requireFullPermission, boolean checkShell, - String message) { - if (userId < 0) { - throw new IllegalArgumentException("Invalid userId " + userId); - } - if (checkShell) { - PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt, - UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); - } - final int callingUserId = UserHandle.getUserId(callingUid); - if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission, - /*requirePermissionWhenSameUser= */ false)) { - return; - } - final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId); - if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight( - mContext, - android.Manifest.permission.INTERACT_ACROSS_PROFILES, - PermissionChecker.PID_UNKNOWN, - callingUid, - mPackageManagerInt.getPackage(callingUid).getPackageName()) - == PermissionChecker.PERMISSION_GRANTED) { - return; + private void enforceShellRestriction(@NonNull String restriction, int callingUid, + @UserIdInt int userId) { + if (callingUid == Process.SHELL_UID) { + if (userId >= 0 && mUserManagerInt.hasUserRestriction(restriction, userId)) { + throw new SecurityException("Shell does not have permission to access user " + + userId); + } else if (userId < 0) { + Slog.e(LOG_TAG, "Unable to check shell permission for user " + + userId + "\n\t" + Debug.getCallers(3)); + } } - String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage( - callingUid, userId, message, requireFullPermission, isSameProfileGroup); - Slog.w(TAG, errorMessage); - throw new SecurityException(errorMessage); } - private boolean hasCrossUserPermission( - int callingUid, int callingUserId, int userId, boolean requireFullPermission, - boolean requirePermissionWhenSameUser) { - if (!requirePermissionWhenSameUser && userId == callingUserId) { + private boolean checkCrossUserPermission(int callingUid, @UserIdInt int callingUserId, + @UserIdInt int userId, boolean requireFullPermission) { + if (userId == callingUserId) { return true; } if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { return true; } if (requireFullPermission) { - return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + return checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); } - return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) - || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS); + return checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + || checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS); } - private boolean hasPermission(String permission) { + private boolean checkCallingOrSelfPermission(String permission) { return mContext.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; } - private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { - final long identity = Binder.clearCallingIdentity(); - try { - return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - + @NonNull private static String buildInvalidCrossUserPermissionMessage(int callingUid, - @UserIdInt int userId, String message, boolean requireFullPermission) { + @UserIdInt int userId, @Nullable String message, boolean requireFullPermission) { StringBuilder builder = new StringBuilder(); if (message != null) { builder.append(message); @@ -4617,31 +4572,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return builder.toString(); } - private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid, - @UserIdInt int userId, String message, boolean requireFullPermission, - boolean isSameProfileGroup) { - StringBuilder builder = new StringBuilder(); - if (message != null) { - builder.append(message); - builder.append(": "); - } - builder.append("UID "); - builder.append(callingUid); - builder.append(" requires "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - if (!requireFullPermission) { - builder.append(" or "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); - if (isSameProfileGroup) { - builder.append(" or "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES); - } - } - builder.append(" to access user "); - builder.append("."); - return builder.toString(); - } - @GuardedBy("mLock") private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) { int size = 0; @@ -5140,30 +5070,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { Preconditions.checkArgumentNonNegative(userId, "userId"); mPackageManagerInt.forEachPackage(pkg -> resetRuntimePermissionsInternal(pkg, userId)); } - @Override - public void enforceCrossUserPermission(int callingUid, int userId, - boolean requireFullPermission, boolean checkShell, String message) { - PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId, - requireFullPermission, checkShell, false, message); - } - @Override - public void enforceCrossUserPermission(int callingUid, int userId, - boolean requireFullPermission, boolean checkShell, - boolean requirePermissionWhenSameUser, String message) { - PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId, - requireFullPermission, checkShell, requirePermissionWhenSameUser, message); - } - - @Override - public void enforceCrossUserOrProfilePermission(int callingUid, int userId, - boolean requireFullPermission, boolean checkShell, String message) { - PermissionManagerService.this.enforceCrossUserOrProfilePermission( - callingUid, - userId, - requireFullPermission, - checkShell, - message); - } @Override public Permission getPermissionTEMP(String permName) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 7656b2ef7b04..df9d0d397c56 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -393,32 +393,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @NonNull public abstract String[] getAppOpPermissionPackages(@NonNull String permissionName); - /** - * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS - * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller. - * @param checkShell whether to prevent shell from access if there's a debugging restriction - * @param message the message to log on security exception - */ - public abstract void enforceCrossUserPermission(int callingUid, int userId, - boolean requireFullPermission, boolean checkShell, @NonNull String message); - - /** - * Similar to {@link #enforceCrossUserPermission(int, int, boolean, boolean, String)} - * but also allows INTERACT_ACROSS_PROFILES permission if calling user and {@code userId} are - * in the same profile group. - */ - public abstract void enforceCrossUserOrProfilePermission(int callingUid, int userId, - boolean requireFullPermission, boolean checkShell, @NonNull String message); - - /** - * @see #enforceCrossUserPermission(int, int, boolean, boolean, String) - * @param requirePermissionWhenSameUser When {@code true}, still require the cross user - * permission to be held even if the callingUid and userId reference the same user. - */ - public abstract void enforceCrossUserPermission(int callingUid, int userId, - boolean requireFullPermission, boolean checkShell, - boolean requirePermissionWhenSameUser, @NonNull String message); - /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ @Nullable public abstract Permission getPermissionTEMP(@NonNull String permName); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index e1cd9e334f4c..907743314c7c 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -47,7 +47,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.permission.PermissionControllerManager; import android.provider.Settings; import android.provider.Telephony; @@ -68,7 +67,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; +import com.android.server.pm.UserManagerInternal; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index f7e6822612d8..8beec35ebc64 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -208,7 +208,6 @@ import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; -import com.android.server.wm.AppTransition; import com.android.server.wm.DisplayPolicy; import com.android.server.wm.DisplayRotation; import com.android.server.wm.WindowManagerInternal; @@ -1959,14 +1958,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() { @Override - public int onAppTransitionStartingLocked(int transit, long duration, + public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { - return handleStartTransitionForKeyguardLw(transit, duration); + return handleStartTransitionForKeyguardLw(keyguardGoingAway, duration); } @Override - public void onAppTransitionCancelledLocked(int transit) { - handleStartTransitionForKeyguardLw(transit, 0 /* duration */); + public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) { + handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */); } }); mKeyguardDelegate = new KeyguardServiceDelegate(mContext, @@ -3194,7 +3193,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private int handleStartTransitionForKeyguardLw(int transit, long duration) { + private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) { if (mKeyguardOccludedChanged) { if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded=" + mPendingKeyguardOccluded); @@ -3203,7 +3202,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER; } } - if (AppTransition.isKeyguardGoingAwayTransit(transit)) { + if (keyguardGoingAway) { if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation"); startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration); } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index e7b1b4e4a687..ab6ada2f85f7 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -46,7 +46,6 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -71,7 +70,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; +import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.ByteArrayOutputStream; diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java index 1c29c69239f6..b79fc8dd1e1a 100644 --- a/services/core/java/com/android/server/storage/StorageUserConnection.java +++ b/services/core/java/com/android/server/storage/StorageUserConnection.java @@ -35,7 +35,6 @@ import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.os.storage.StorageManagerInternal; import android.os.storage.StorageVolume; import android.service.storage.ExternalStorageService; @@ -46,6 +45,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import java.io.IOException; import java.util.HashMap; diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index afbe5527a5cb..510893b2940b 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -87,6 +87,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.SystemService; @@ -337,6 +338,7 @@ public final class TvInputManagerService extends SystemService { inputState = new TvInputState(); } inputState.info = info; + inputState.uid = getInputUid(info); inputMap.put(info.getId(), inputState); } @@ -371,6 +373,16 @@ public final class TvInputManagerService extends SystemService { userState.inputMap = inputMap; } + private int getInputUid(TvInputInfo info) { + try { + return getContext().getPackageManager().getApplicationInfo( + info.getServiceInfo().packageName, 0).uid; + } catch (NameNotFoundException e) { + Slog.w(TAG, "Unable to get UID for " + info, e); + return Process.INVALID_UID; + } + } + @GuardedBy("mLock") private void buildTvContentRatingSystemListLocked(int userId) { UserState userState = getOrCreateUserStateLocked(userId); @@ -405,7 +417,7 @@ public final class TvInputManagerService extends SystemService { return; } if (mUserStates.contains(mCurrentUserId)) { - UserState userState = mUserStates.get(mCurrentUserId); + UserState userState = getUserStateLocked(mCurrentUserId); List<SessionState> sessionStatesToRelease = new ArrayList<>(); for (SessionState sessionState : userState.sessionStateMap.values()) { if (sessionState.session != null && !sessionState.isRecordingSession) { @@ -474,7 +486,7 @@ public final class TvInputManagerService extends SystemService { private void removeUser(int userId) { synchronized (mLock) { - UserState userState = mUserStates.get(userId); + UserState userState = getUserStateLocked(userId); if (userState == null) { return; } @@ -535,7 +547,7 @@ public final class TvInputManagerService extends SystemService { @GuardedBy("mLock") private UserState getOrCreateUserStateLocked(int userId) { - UserState userState = mUserStates.get(userId); + UserState userState = getUserStateLocked(userId); if (userState == null) { userState = new UserState(mContext, userId); mUserStates.put(userId, userState); @@ -715,7 +727,8 @@ public final class TvInputManagerService extends SystemService { } @GuardedBy("mLock") - private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { + @Nullable + private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { SessionState sessionState = null; try { sessionState = getSessionStateLocked(sessionToken, callingUid, userId); @@ -738,6 +751,7 @@ public final class TvInputManagerService extends SystemService { } } removeSessionStateLocked(sessionToken, userId); + return sessionState; } @GuardedBy("mLock") @@ -908,6 +922,7 @@ public final class TvInputManagerService extends SystemService { return; } inputState.info = inputInfo; + inputState.uid = getInputUid(inputInfo); int n = userState.mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { @@ -1248,7 +1263,22 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "createSession"); final long identity = Binder.clearCallingIdentity(); - // Generate a unique session id with a random UUID. + /** + * A randomly generated id for this this session. + * + * <p>This field contains no user or device reference and is large enough to be + * effectively globally unique. + * + * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + * <ul> + * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState + * <li>{@link #logTuneStateChanged} + * <li>{@link TvInputManagerService.BinderService#createSession} + * <li>{@link SessionState#sessionId} + * </ul> + */ String uniqueSessionId = UUID.randomUUID().toString(); try { synchronized (mLock) { @@ -1269,6 +1299,8 @@ public final class TvInputManagerService extends SystemService { TvInputInfo info = inputState.info; ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); if (serviceState == null) { + int tisUid = PackageManager.getApplicationInfoAsUserCached( + info.getComponent().getPackageName(), 0, resolvedUserId).uid; serviceState = new ServiceState(info.getComponent(), resolvedUserId); userState.serviceStateMap.put(info.getComponent(), serviceState); } @@ -1301,6 +1333,8 @@ public final class TvInputManagerService extends SystemService { } else { updateServiceConnectionLocked(info.getComponent(), resolvedUserId); } + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED, + sessionState, inputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1317,8 +1351,17 @@ public final class TvInputManagerService extends SystemService { userId, "releaseSession"); final long identity = Binder.clearCallingIdentity(); try { + SessionState sessionState = null; + UserState userState = null; synchronized (mLock) { - releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + userState = getUserStateLocked(userId); + } + if (sessionState != null) { + TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState, + userState); + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED, + sessionState, tvInputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1372,10 +1415,13 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, userId, "setSurface"); final long identity = Binder.clearCallingIdentity(); + SessionState sessionState = null; + UserState userState = null; try { synchronized (mLock) { try { - SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + userState = getUserStateLocked(userId); + sessionState = getSessionStateLocked(sessionToken, callingUid, resolvedUserId); if (sessionState.hardwareSessionToken == null) { getSessionLocked(sessionState).setSurface(surface); @@ -1392,6 +1438,14 @@ public final class TvInputManagerService extends SystemService { // surface is not used in TvInputManagerService. surface.release(); } + if (sessionState != null) { + int state = surface == null + ? + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED + : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED; + logTuneStateChanged(state, sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); + } Binder.restoreCallingIdentity(identity); } } @@ -1479,6 +1533,10 @@ public final class TvInputManagerService extends SystemService { return; } + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED, + sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); // Log the start of watch. SomeArgs args = SomeArgs.obtain(); args.arg1 = sessionState.componentName.getPackageName(); @@ -2302,6 +2360,16 @@ public final class TvInputManagerService extends SystemService { } } + @Nullable + private static TvInputState getTvInputState( + SessionState sessionState, + @Nullable UserState userState) { + if (userState != null) { + return userState.inputMap.get(sessionState.inputId); + } + return null; + } + @GuardedBy("mLock") private List<TunedInfo> getCurrentTunedInfosInternalLocked( UserState userState, int callingPid, int callingUid) { @@ -2367,6 +2435,28 @@ public final class TvInputManagerService extends SystemService { } } + /** + * Log Tune state changes to {@link FrameworkStatsLog}. + * + * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + * <ul> + * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState + * <li>{@link #logTuneStateChanged} + * <li>{@link TvInputManagerService.BinderService#createSession} + * <li>{@link SessionState#sessionId} + * </ul> + */ + private void logTuneStateChanged(int state, SessionState sessionState, + @Nullable TvInputState inputState) { + // TODO(b/173536904): log input type and id + FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, + new int[]{sessionState.callingUid, + inputState == null ? Process.INVALID_UID : inputState.uid}, + new String[]{"tif_player", "tv_input_service"}, state, sessionState.sessionId); + } + private static final class UserState { // A mapping from the TV input id to its TvInputState. private Map<String, TvInputState> inputMap = new HashMap<>(); @@ -2464,10 +2554,25 @@ public final class TvInputManagerService extends SystemService { } private static final class TvInputState { - // A TvInputInfo object which represents the TV input. + + /** A TvInputInfo object which represents the TV input. */ private TvInputInfo info; - // The state of TV input. Connected by default. + /** + * The kernel user-ID that has been assigned to the application the TvInput is a part of. + * + * <p> + * Currently this is not a unique ID (multiple applications can have + * the same uid). + */ + private int uid; + + /** + * The state of TV input. + * + * <p> + * Connected by default + */ private int state = INPUT_STATE_CONNECTED; @Override @@ -2478,6 +2583,23 @@ public final class TvInputManagerService extends SystemService { private final class SessionState implements IBinder.DeathRecipient { private final String inputId; + + /** + * A randomly generated id for this this session. + * + * <p>This field contains no user or device reference and is large enough to be + * effectively globally unique. + * + * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + * <ul> + * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState + * <li>{@link #logTuneStateChanged} + * <li>{@link TvInputManagerService.BinderService#createSession} + * <li>{@link SessionState#sessionId} + * </ul> + */ private final String sessionId; private final ComponentName componentName; private final boolean isRecordingSession; @@ -2545,7 +2667,7 @@ public final class TvInputManagerService extends SystemService { Slog.d(TAG, "onServiceConnected(component=" + component + ")"); } synchronized (mLock) { - UserState userState = mUserStates.get(mUserId); + UserState userState = getUserStateLocked(mUserId); if (userState == null) { // The user was removed while connecting. mContext.unbindService(this); @@ -2815,8 +2937,13 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } + TvInputState tvInputState = getTvInputState(mSessionState, + getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoAvailable(mSessionState.seq); + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE, + mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoAvailable", e); } @@ -2832,8 +2959,20 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } + TvInputState tvInputState = getTvInputState(mSessionState, + getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); + int loggedReason = reason + FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + if (loggedReason < FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN + || loggedReason > FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { + loggedReason = FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + } + logTuneStateChanged(loggedReason, mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoUnavailable", e); } @@ -3019,6 +3158,10 @@ public final class TvInputManagerService extends SystemService { } } + private UserState getUserStateLocked(int userId) { + return mUserStates.get(userId); + } + private static final class WatchLogHandler extends Handler { // There are only two kinds of watch events that can happen on the system: // 1. The current TV input session is tuned to a new channel. diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 329589eea904..e07540a0c746 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -78,7 +78,6 @@ import android.os.SELinux; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.storage.StorageManager; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; @@ -107,7 +106,7 @@ import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; +import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.WindowManagerInternal; diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index d8d1a6563675..b4ca7c5f6ff1 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -3,8 +3,10 @@ package com.android.server.wm; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.processStateAmToProto; +import static android.app.WaitResult.INVALID_DELAY; import static android.app.WaitResult.LAUNCH_STATE_COLD; import static android.app.WaitResult.LAUNCH_STATE_HOT; +import static android.app.WaitResult.LAUNCH_STATE_RELAUNCH; import static android.app.WaitResult.LAUNCH_STATE_WARM; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -130,7 +132,6 @@ class ActivityMetricsLogger { * transition, in the case the launch is standalone (e.g. from recents). */ private static final int IGNORE_CALLER = -1; - private static final int INVALID_DELAY = -1; // Preallocated strings we are sending to tron, so we don't have to allocate a new one every // time we log. @@ -220,6 +221,8 @@ class ActivityMetricsLogger { boolean mLoggedStartingWindowDrawn; /** If the any app transitions have been logged as starting. */ boolean mLoggedTransitionStarting; + /** Whether any activity belonging to this transition has relaunched. */ + boolean mRelaunched; /** Non-null if the application has reported drawn but its window hasn't. */ @Nullable Runnable mPendingFullyDrawn; @@ -351,6 +354,7 @@ class ActivityMetricsLogger { */ final int windowsFullyDrawnDelayMs; final int activityRecordIdHashCode; + final boolean relaunched; private TransitionInfoSnapshot(TransitionInfo info) { this(info, info.mLastLaunchedActivity, INVALID_DELAY); @@ -379,6 +383,7 @@ class ActivityMetricsLogger { launchedActivityShortComponentName = launchedActivity.shortComponentName; activityRecordIdHashCode = System.identityHashCode(launchedActivity); this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs; + relaunched = info.mRelaunched; } @WaitResult.LaunchState int getLaunchState() { @@ -386,7 +391,7 @@ class ActivityMetricsLogger { case TYPE_TRANSITION_WARM_LAUNCH: return LAUNCH_STATE_WARM; case TYPE_TRANSITION_HOT_LAUNCH: - return LAUNCH_STATE_HOT; + return relaunched ? LAUNCH_STATE_RELAUNCH : LAUNCH_STATE_HOT; case TYPE_TRANSITION_COLD_LAUNCH: return LAUNCH_STATE_COLD; default: @@ -673,6 +678,13 @@ class ActivityMetricsLogger { } } + void notifyActivityRelaunched(ActivityRecord r) { + final TransitionInfo info = getActiveTransitionInfo(r); + if (info != null) { + info.mRelaunched = true; + } + } + /** Makes sure that the reference to the removed activity is cleared. */ void notifyActivityRemoved(@NonNull ActivityRecord r) { mLastTransitionInfo.remove(r); @@ -800,13 +812,13 @@ class ActivityMetricsLogger { FrameworkStatsLog.APP_START_CANCELED, activity.info.applicationInfo.uid, activity.packageName, - convertAppStartTransitionType(type), + getAppStartTransitionType(type, info.mRelaunched), activity.info.name); if (DEBUG_METRICS) { Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)", activity.info.applicationInfo.uid, activity.packageName, - convertAppStartTransitionType(type), + getAppStartTransitionType(type, info.mRelaunched), activity.info.name)); } } @@ -871,7 +883,7 @@ class ActivityMetricsLogger { FrameworkStatsLog.APP_START_OCCURRED, info.applicationInfo.uid, info.packageName, - convertAppStartTransitionType(info.type), + getAppStartTransitionType(info.type, info.relaunched), info.launchedActivityName, info.launchedActivityLaunchedFromPackage, isInstantApp, @@ -891,7 +903,7 @@ class ActivityMetricsLogger { Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)", info.applicationInfo.uid, info.packageName, - convertAppStartTransitionType(info.type), + getAppStartTransitionType(info.type, info.relaunched), info.launchedActivityName, info.launchedActivityLaunchedFromPackage)); } @@ -918,7 +930,7 @@ class ActivityMetricsLogger { Log.i(TAG, sb.toString()); } - private int convertAppStartTransitionType(int tronType) { + private static int getAppStartTransitionType(int tronType, boolean relaunched) { if (tronType == TYPE_TRANSITION_COLD_LAUNCH) { return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD; } @@ -926,17 +938,13 @@ class ActivityMetricsLogger { return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM; } if (tronType == TYPE_TRANSITION_HOT_LAUNCH) { - return FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT; + return relaunched + ? FrameworkStatsLog.APP_START_OCCURRED__TYPE__RELAUNCH + : FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT; } return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN; } - /** @return the last known window drawn delay of the given activity. */ - int getLastDrawnDelayMs(ActivityRecord r) { - final TransitionInfo info = mLastTransitionInfo.get(r); - return info != null ? info.mWindowsDrawnDelayMs : INVALID_DELAY; - } - /** @see android.app.Activity#reportFullyDrawn */ TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d8b750ea7484..8cc02e58b30f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -103,9 +103,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.TransitionOldType; @@ -150,7 +150,6 @@ import static com.android.server.wm.ActivityRecordProto.VISIBLE; import static com.android.server.wm.ActivityRecordProto.VISIBLE_REQUESTED; import static com.android.server.wm.ActivityRecordProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW; import static com.android.server.wm.ActivityRecordProto.WINDOW_TOKEN; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -177,6 +176,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_F import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; @@ -226,7 +226,7 @@ import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.app.ResultInfo; -import android.app.WaitResult.LaunchState; +import android.app.WaitResult; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityRelaunchItem; @@ -2609,7 +2609,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (DEBUG_VISIBILITY || DEBUG_TRANSITION) { Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this); } - mDisplayContent.prepareAppTransitionOld(transit, false); mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); // When finishing the activity preemptively take the snapshot before the app window @@ -2693,7 +2692,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void prepareActivityHideTransitionAnimation(@TransitionOldType int transit) { final DisplayContent dc = mDisplayContent; - dc.prepareAppTransitionOld(transit, false); dc.prepareAppTransition(TRANSIT_CLOSE); setVisibility(false); dc.executeAppTransition(); @@ -3132,6 +3130,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void finishRelaunching() { + mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this); unfreezeBounds(); if (mPendingRelaunchCount > 0) { @@ -4274,14 +4273,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A displayContent.mClosingApps.add(this); mEnteringAnimation = false; } - if (appTransition.getAppTransitionOld() == TRANSIT_OLD_TASK_OPEN_BEHIND) { + if ((appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0) { // We're launchingBehind, add the launching activity to mOpeningApps. final WindowState win = getDisplayContent().findFocusedWindow(); if (win != null) { final ActivityRecord focusedActivity = win.mActivityRecord; if (focusedActivity != null) { ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, - "TRANSIT_TASK_OPEN_BEHIND, adding %s to mOpeningApps", + "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps", focusedActivity); // Force animation to be loaded. @@ -4297,7 +4296,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @Override - boolean applyAnimation(LayoutParams lp, int transit, boolean enter, + boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) { if (mUseTransferredAnimation) { return false; @@ -5437,14 +5436,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs); final boolean validInfo = info != null; final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY; - final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1; + final @WaitResult.LaunchState int launchState = + validInfo ? info.getLaunchState() : WaitResult.LAUNCH_STATE_UNKNOWN; // The activity may have been requested to be invisible (another activity has been launched) // so there is no valid info. But if it is the current top activity (e.g. sleeping), the // invalid state is still reported to make sure the waiting result is notified. if (validInfo || this == getDisplayArea().topRunningActivity()) { mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, windowsDrawnDelayMs, launchState); - mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs); + mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState); } finishLaunchTickingLocked(); if (task != null) { @@ -6312,7 +6312,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mThumbnail = null; } - public int getTransit() { + public @TransitionOldType int getTransit() { return mTransit; } @@ -7165,11 +7165,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { mRelaunchReason = RELAUNCH_REASON_NONE; } - if (!attachedToProcess()) { - ProtoLog.v(WM_DEBUG_CONFIGURATION, - "Config is destroying non-running %s", this); - destroyImmediately("config"); - } else if (mState == PAUSING) { + if (mState == PAUSING) { // A little annoying: we are waiting for this activity to finish pausing. Let's not // do anything now, but just flag that it needs to be restarted when done pausing. ProtoLog.v(WM_DEBUG_CONFIGURATION, diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index f472a5dd7ba9..d560839f465b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -28,8 +28,6 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WaitResult.LAUNCH_STATE_COLD; -import static android.app.WaitResult.LAUNCH_STATE_HOT; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -819,8 +817,6 @@ class ActivityStarter { break; } case START_TASK_TO_FRONT: { - mRequest.waitResult.launchState = - r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD; // ActivityRecord may represent a different activity, but it should not be // in the resumed state. if (r.nowVisible && r.isState(RESUMED)) { @@ -1285,6 +1281,15 @@ class ActivityStarter { return false; } + // IME should always be allowed to start activity, like IME settings. + final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow(); + if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")"); + } + return false; + } + // App switching will be allowed if BAL app switching flag is not enabled, or if // its app switching rule allows it. // This is used to block background activity launch even if the app is still diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 43326bd2e8ce..e4c607dd8cdd 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -67,7 +67,6 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OLD_NONE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; @@ -95,10 +94,6 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE; import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen; import static com.android.server.am.EventLogTags.writeConfigurationChanged; -import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY; @@ -117,6 +112,10 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEI import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG; import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; +import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -164,7 +163,6 @@ import android.app.assist.AssistStructure; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.EnterPipRequestedItem; import android.app.usage.UsageStatsManagerInternal; -import android.compat.annotation.ChangeId; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; @@ -6248,7 +6246,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final boolean wasTransitionSet = dc.mAppTransition.isTransitionSet(); if (!wasTransitionSet) { - dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false /* alwaysKeepCurrent */); dc.prepareAppTransition(TRANSIT_NONE); } mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index e91a6d8e2439..01d77d5b6cc8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -553,14 +553,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // down to the max limit while they are still waiting to finish. mFinishingActivities.remove(r); - stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY); + stopWaitingForActivityVisible(r); } + /** There is no valid launch time, just stop waiting. */ void stopWaitingForActivityVisible(ActivityRecord r) { - stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r)); + stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN); } - void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) { + void stopWaitingForActivityVisible(ActivityRecord r, long totalTime, + @WaitResult.LaunchState int launchState) { boolean changed = false; for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) { final WaitInfo w = mWaitingForActivityVisible.get(i); @@ -570,6 +572,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { result.timeout = false; result.who = w.getComponent(); result.totalTime = totalTime; + result.launchState = launchState; mWaitingForActivityVisible.remove(w); } } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index e146adadffe7..758aaa0fee9a 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANI import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; @@ -115,7 +116,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -184,7 +184,6 @@ public class AppTransition implements Dump { private final WindowManagerService mService; private final DisplayContent mDisplayContent; - private @TransitionOldType int mNextAppTransitionOld = TRANSIT_OLD_UNSET; private @TransitionFlags int mNextAppTransitionFlags = 0; private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>(); private @TransitionOldType int mLastUsedAppTransition = TRANSIT_OLD_UNSET; @@ -330,46 +329,20 @@ public class AppTransition implements Dump { } boolean isTransitionSet() { - return mNextAppTransitionOld != TRANSIT_OLD_UNSET || !mNextAppTransitionRequests.isEmpty(); - } - - // TODO(new-app-tranistion): Remove this after migrating to new app transition system. - boolean isTransitionOldEqual(@TransitionOldType int transit) { - return mNextAppTransitionOld == transit; + return !mNextAppTransitionRequests.isEmpty(); } boolean isUnoccluding() { - return WindowManagerService.sUseNewAppTransit - ? mNextAppTransitionRequests.contains(TRANSIT_OLD_KEYGUARD_UNOCCLUDE) - : mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_UNOCCLUDE; + return mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_UNOCCLUDE); } - @TransitionOldType - int getAppTransitionOld() { - return mNextAppTransitionOld; - } - boolean transferFrom(AppTransition other) { - prepareAppTransitionOld(other.getAppTransitionOld(), true /* alwaysKeepCurrent */, - 0 /* flags */, false /* forceOverride */); mNextAppTransitionRequests.addAll(other.mNextAppTransitionRequests); return prepare(); } - private void setAppTransitionOld(@TransitionOldType int transit, int flags) { - mNextAppTransitionOld = transit; - mNextAppTransitionFlags |= flags; - setLastAppTransition(TRANSIT_OLD_UNSET, null, null, null); - updateBooster(); - if (isTransitionSet()) { - removeAppTransitionTimeoutCallbacks(); - mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable, - APP_TRANSITION_TIMEOUT_MS); - } - } - - void setLastAppTransition(int transit, ActivityRecord openingApp, ActivityRecord closingApp, - ActivityRecord changingApp) { + void setLastAppTransition(@TransitionOldType int transit, ActivityRecord openingApp, + ActivityRecord closingApp, ActivityRecord changingApp) { mLastUsedAppTransition = transit; mLastOpeningApp = "" + openingApp; mLastClosingApp = "" + closingApp; @@ -460,8 +433,7 @@ public class AppTransition implements Dump { * @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another * layout pass needs to be done */ - int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) { - mNextAppTransitionOld = TRANSIT_OLD_UNSET; + int goodToGo(@TransitionOldType int transit, ActivityRecord topOpeningApp) { mNextAppTransitionFlags = 0; mNextAppTransitionRequests.clear(); setAppTransitionState(APP_STATE_RUNNING); @@ -469,7 +441,8 @@ public class AppTransition implements Dump { topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null; final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null; - int redoLayout = notifyAppTransitionStartingLocked(transit, + int redoLayout = notifyAppTransitionStartingLocked( + AppTransition.isKeyguardGoingAwayTransitOld(transit), topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0, topOpeningAnim != null ? topOpeningAnim.getStatusBarTransitionsStartTime() @@ -494,7 +467,9 @@ public class AppTransition implements Dump { } void freeze() { - final int transit = mNextAppTransitionOld; + final boolean keyguardGoingAway = mNextAppTransitionRequests.contains( + TRANSIT_KEYGUARD_GOING_AWAY); + // The RemoteAnimationControl didn't register AppTransitionListener and // only initialized the finish and timeout callback when goodToGo(). // So cancel the remote animation here to prevent the animation can't do @@ -502,10 +477,10 @@ public class AppTransition implements Dump { if (mRemoteAnimationController != null) { mRemoteAnimationController.cancelAnimation("freeze"); } - setAppTransitionOld(TRANSIT_OLD_UNSET, 0 /* flags */); + mNextAppTransitionRequests.clear(); clear(); setReady(); - notifyAppTransitionCancelledLocked(transit); + notifyAppTransitionCancelledLocked(keyguardGoingAway); } private void setAppTransitionState(int state) { @@ -524,7 +499,7 @@ public class AppTransition implements Dump { private boolean needsBoosting() { final boolean recentsAnimRunning = mService.getRecentsAnimationController() != null; - return mNextAppTransitionOld != TRANSIT_OLD_UNSET + return !mNextAppTransitionRequests.isEmpty() || mAppTransitionState == APP_STATE_READY || mAppTransitionState == APP_STATE_RUNNING || recentsAnimRunning; @@ -550,9 +525,9 @@ public class AppTransition implements Dump { } } - private void notifyAppTransitionCancelledLocked(int transit) { + private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAway) { for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onAppTransitionCancelledLocked(transit); + mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAway); } } @@ -562,12 +537,12 @@ public class AppTransition implements Dump { } } - private int notifyAppTransitionStartingLocked(int transit, long duration, + private int notifyAppTransitionStartingLocked(boolean keyguardGoingAway, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { int redoLayout = 0; for (int i = 0; i < mListeners.size(); i++) { - redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(transit, duration, - statusBarAnimationStartTime, statusBarAnimationDuration); + redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway, + duration, statusBarAnimationStartTime, statusBarAnimationDuration); } return redoLayout; } @@ -1578,7 +1553,7 @@ public class AppTransition implements Dump { && !mNextAppTransitionOverrideRequested && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL - && mNextAppTransitionOld != TRANSIT_OLD_KEYGUARD_GOING_AWAY; + && !mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY); } RemoteAnimationController getRemoteAnimationController() { @@ -1616,7 +1591,7 @@ public class AppTransition implements Dump { } Animation a; - if (isKeyguardGoingAwayTransit(transit) && enter) { + if (isKeyguardGoingAwayTransitOld(transit) && enter) { a = loadKeyguardExitAnimation(transit); } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { a = null; @@ -1645,9 +1620,8 @@ public class AppTransition implements Dump { } else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) { a = createRelaunchAnimation(frame, insets); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, - "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", a, - mNextAppTransitionOld, appTransitionOldToString(transit), - Debug.getCallers(3)); + "applyAnimation: anim=%s transit=%s Callers=%s", a, + appTransitionOldToString(transit), Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { a = loadAnimationRes(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); @@ -1799,14 +1773,12 @@ public class AppTransition implements Dump { int getAppStackClipMode() { return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH) || mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY) - || mNextAppTransitionOld == TRANSIT_OLD_ACTIVITY_RELAUNCH || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL - || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY - || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER ? STACK_CLIP_NONE : STACK_CLIP_AFTER_ANIM; } + @TransitionFlags public int getTransitFlags() { return mNextAppTransitionFlags; } @@ -2001,9 +1973,7 @@ public class AppTransition implements Dump { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("mNextAppTransition="); - sb.append(appTransitionOldToString(mNextAppTransitionOld)); - sb.append(", mNextAppTransitionRequests=["); + sb.append("mNextAppTransitionRequests=["); boolean separator = false; for (Integer transit : mNextAppTransitionRequests) { @@ -2194,6 +2164,8 @@ public class AppTransition implements Dump { "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION")); sFlagToString.add(new Pair<>(TRANSIT_FLAG_APP_CRASHED, "TRANSIT_FLAG_APP_CRASHED")); + sFlagToString.add(new Pair<>(TRANSIT_FLAG_OPEN_BEHIND, + "TRANSIT_FLAG_OPEN_BEHIND")); } /** @@ -2291,47 +2263,6 @@ public class AppTransition implements Dump { mCurrentUserId = newUserId; } - /** - * @return true if transition is not running and should not be skipped, false if transition is - * already running - */ - boolean prepareAppTransitionOld(@TransitionOldType int transit, boolean alwaysKeepCurrent, - @TransitionFlags int flags, boolean forceOverride) { - if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) { - return false; - } - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d " - + "Callers=%s", - appTransitionOldToString(transit), this, alwaysKeepCurrent, - mDisplayContent.getDisplayId(), Debug.getCallers(5)); - final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransitionOld) - && transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; - if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet() - || mNextAppTransitionOld == TRANSIT_OLD_NONE || allowSetCrashing) { - setAppTransitionOld(transit, flags); - } - // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic - // relies on the fact that we always execute a Keyguard transition after preparing one. We - // also don't want to change away from a crashing transition. - else if (!alwaysKeepCurrent && !isKeyguardTransit(mNextAppTransitionOld) - && mNextAppTransitionOld != TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) { - if (transit == TRANSIT_OLD_TASK_OPEN && isTransitionOldEqual(TRANSIT_OLD_TASK_CLOSE)) { - // Opening a new task always supersedes a close for the anim. - setAppTransitionOld(transit, flags); - } else if (transit == TRANSIT_OLD_ACTIVITY_OPEN - && isTransitionOldEqual(TRANSIT_OLD_ACTIVITY_CLOSE)) { - // Opening a new activity always supersedes a close for the anim. - setAppTransitionOld(transit, flags); - } else if (isTaskTransit(transit) && isActivityTransit(mNextAppTransitionOld)) { - // Task animations always supersede activity animations, because if we have both, it - // usually means that activity transition were just trampoline activities. - setAppTransitionOld(transit, flags); - } - } - return prepare(); - } - boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) { if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) { return false; @@ -2349,39 +2280,39 @@ public class AppTransition implements Dump { * @return true if {@param transit} is representing a transition in which Keyguard is going * away, false otherwise */ - public static boolean isKeyguardGoingAwayTransit(int transit) { + public static boolean isKeyguardGoingAwayTransitOld(int transit) { return transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; } - static boolean isKeyguardTransit(@TransitionOldType int transit) { - return isKeyguardGoingAwayTransit(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE + static boolean isKeyguardTransitOld(@TransitionOldType int transit) { + return isKeyguardGoingAwayTransitOld(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE || transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } - static boolean isTaskTransit(@TransitionOldType int transit) { - return isTaskOpenTransit(transit) + static boolean isTaskTransitOld(@TransitionOldType int transit) { + return isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_TASK_CLOSE || transit == TRANSIT_OLD_TASK_TO_BACK; } - private static boolean isTaskOpenTransit(@TransitionOldType int transit) { + private static boolean isTaskOpenTransitOld(@TransitionOldType int transit) { return transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_OPEN_BEHIND || transit == TRANSIT_OLD_TASK_TO_FRONT; } - static boolean isActivityTransit(@TransitionOldType int transit) { + static boolean isActivityTransitOld(@TransitionOldType int transit) { return transit == TRANSIT_OLD_ACTIVITY_OPEN || transit == TRANSIT_OLD_ACTIVITY_CLOSE || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH; } - static boolean isChangeTransit(@TransitionOldType int transit) { + static boolean isChangeTransitOld(@TransitionOldType int transit) { return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; } - static boolean isClosingTransit(@TransitionOldType int transit) { + static boolean isClosingTransitOld(@TransitionOldType int transit) { return transit == TRANSIT_OLD_ACTIVITY_CLOSE || transit == TRANSIT_OLD_TASK_CLOSE || transit == TRANSIT_OLD_WALLPAPER_CLOSE @@ -2390,6 +2321,51 @@ public class AppTransition implements Dump { || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; } + static boolean isNormalTransit(@TransitionType int transit) { + return transit == TRANSIT_OPEN + || transit == TRANSIT_CLOSE + || transit == TRANSIT_TO_FRONT + || transit == TRANSIT_TO_BACK; + } + + static boolean isKeyguardTransit(@TransitionType int transit) { + return transit == TRANSIT_KEYGUARD_GOING_AWAY + || transit == TRANSIT_KEYGUARD_OCCLUDE + || transit == TRANSIT_KEYGUARD_UNOCCLUDE; + } + + @TransitionType int getKeyguardTransition() { + // In case we unocclude Keyguard and occlude it again, meaning that we never actually + // unoccclude/occlude Keyguard, but just run a normal transition. + final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE); + if (occludeIndex != -1 + && occludeIndex < mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE)) { + return TRANSIT_NONE; + } + + for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) { + final @TransitionType int transit = mNextAppTransitionRequests.get(i); + if (isKeyguardTransit(transit)) { + return transit; + } + } + return TRANSIT_NONE; + } + + @TransitionType int getFirstAppTransition() { + for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) { + final @TransitionType int transit = mNextAppTransitionRequests.get(i); + if (transit != TRANSIT_NONE && !isKeyguardTransit(transit)) { + return transit; + } + } + return TRANSIT_NONE; + } + + boolean containsTransitRequest(@TransitionType int transit) { + return mNextAppTransitionRequests.contains(transit); + } + /** * @return whether the transition should show the thumbnail being scaled down. */ diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 069f962111db..64cbb0dec635 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -17,19 +17,31 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH; import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; @@ -38,6 +50,10 @@ import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_RELAUNCH; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; @@ -46,7 +62,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_L import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; -import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; +import static com.android.server.wm.AppTransition.isNormalTransit; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; @@ -54,6 +70,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; @@ -63,7 +80,9 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.TransitionFlags; import android.view.WindowManager.TransitionOldType; +import android.view.WindowManager.TransitionType; import android.view.animation.Animation; import com.android.internal.annotations.VisibleForTesting; @@ -97,6 +116,30 @@ public class AppTransitionController { } /** + * Returns the currently visible window that is associated with the wallpaper in case we are + * transitioning from an activity with a wallpaper to one without. + */ + private @Nullable WindowState getOldWallpaper() { + final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); + final @TransitionType int firstTransit = + mDisplayContent.mAppTransition.getFirstAppTransition(); + + final ArraySet<WindowContainer> openingWcs = getAnimationTargets( + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, true /* visible */); + final boolean showWallpaper = wallpaperTarget != null + && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 + // Update task open transition to wallpaper transition when wallpaper is visible. + // (i.e.launching app info activity from recent tasks) + || ((firstTransit == TRANSIT_OPEN || firstTransit == TRANSIT_TO_FRONT) + && (!openingWcs.isEmpty() && openingWcs.valueAt(0).asTask() != null) + && mWallpaperControllerLocked.isWallpaperVisible())); + // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, + // don't consider upgrading to wallpaper transition. + return (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) + ? null : wallpaperTarget; + } + + /** * Handle application transition for given display. */ void handleAppTransitionReady() { @@ -109,13 +152,8 @@ public class AppTransitionController { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO"); - // TODO(new-app-transition): Compute the best transition from - // appTransition.mNextAppTransitionRequests + // TODO(new-app-transition): Remove code using appTransition.getAppTransition() final AppTransition appTransition = mDisplayContent.mAppTransition; - int transit = appTransition.getAppTransitionOld(); - if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { - transit = WindowManager.TRANSIT_OLD_UNSET; - } mDisplayContent.mSkipAppTransitionAnimation = false; mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); @@ -147,17 +185,18 @@ public class AppTransitionController { mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( mDisplayContent.mOpeningApps); - // Determine if closing and opening app token sets are wallpaper targets, in which case - // special animations are needed. - final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null; - final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps) - && hasWallpaperTarget; - final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps) - && hasWallpaperTarget; + final @TransitionOldType int transit = getTransitCompatType( + mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper()); - transit = maybeUpdateTransitToTranslucentAnim(transit); - transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, - closingAppHasWallpaper); + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, + "handleAppTransitionReady: displayId=%d appTransition={%s}" + + " openingApps=[%s] closingApps=[%s] transit=%s", + mDisplayContent.mDisplayId, + appTransition.toString(), + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + AppTransition.appTransitionOldToString(transit)); // Find the layout params of the top-most application window in the tokens, which is // what will control the animation theme. If all closing windows are obscured, then there is @@ -191,8 +230,7 @@ public class AppTransitionController { topClosingApp, topChangingApp); final int flags = appTransition.getTransitFlags(); - layoutRedo = appTransition.goodToGo(transit, topOpeningApp, - mDisplayContent.mOpeningApps); + layoutRedo = appTransition.goodToGo(transit, topOpeningApp); handleNonAppWindowsInTransition(transit, flags); appTransition.postAnimationCallback(); appTransition.clear(); @@ -222,6 +260,162 @@ public class AppTransitionController { layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; } + /** + * Get old transit type based on the current transit requests. + * + * @param appTransition {@link AppTransition} for managing app transition state. + * @param openingApps {@link ActivityRecord}s which are becoming visible. + * @param closingApps {@link ActivityRecord}s which are becoming invisible. + * @param wallpaperTarget If non-null, this is the currently visible window that is associated + * with the wallpaper. + * @param oldWallpaper The currently visible window that is associated with the wallpaper in + * case we are transitioning from an activity with a wallpaper to one + * without. Otherwise null. + */ + static @TransitionOldType int getTransitCompatType(AppTransition appTransition, + ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, + @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper) { + + // Determine if closing and opening app token sets are wallpaper targets, in which case + // special animations are needed. + final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps) + && wallpaperTarget != null; + final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps) + && wallpaperTarget != null; + + // Keyguard transit has highest priority. + switch (appTransition.getKeyguardTransition()) { + case TRANSIT_KEYGUARD_GOING_AWAY: + return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER + : TRANSIT_OLD_KEYGUARD_GOING_AWAY; + case TRANSIT_KEYGUARD_OCCLUDE: + // When there is a closing app, the keyguard has already been occluded by an + // activity, and another activity has started on top of that activity, so normal + // app transition animation should be used. + return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE + : TRANSIT_OLD_ACTIVITY_OPEN; + case TRANSIT_KEYGUARD_UNOCCLUDE: + return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; + } + + final @TransitionFlags int flags = appTransition.getTransitFlags(); + final @TransitionType int firstTransit = appTransition.getFirstAppTransition(); + + // Special transitions + // TODO(new-app-transitions): Revisit if those can be rewritten by using flags. + if (appTransition.containsTransitRequest(TRANSIT_CHANGE_WINDOWING_MODE)) { + return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; + } + if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) { + return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; + } + if (firstTransit == TRANSIT_NONE) { + return TRANSIT_OLD_NONE; + } + + /* + * There are cases where we open/close a new task/activity, but in reality only a + * translucent activity on top of existing activities is opening/closing. For that one, we + * have a different animation because non of the task/activity animations actually work well + * with translucent apps. + */ + if (isNormalTransit(firstTransit)) { + boolean allOpeningVisible = true; + boolean allTranslucentOpeningApps = !openingApps.isEmpty(); + for (int i = openingApps.size() - 1; i >= 0; i--) { + final ActivityRecord activity = openingApps.valueAt(i); + if (!activity.isVisible()) { + allOpeningVisible = false; + if (activity.fillsParent()) { + allTranslucentOpeningApps = false; + } + } + } + boolean allTranslucentClosingApps = !closingApps.isEmpty(); + for (int i = closingApps.size() - 1; i >= 0; i--) { + if (closingApps.valueAt(i).fillsParent()) { + allTranslucentClosingApps = false; + break; + } + } + + if (allTranslucentClosingApps && allOpeningVisible) { + return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; + } + if (allTranslucentOpeningApps && closingApps.isEmpty()) { + return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; + } + } + + final ActivityRecord topOpeningApp = getTopApp(openingApps, + false /* ignoreHidden */); + final ActivityRecord topClosingApp = getTopApp(closingApps, + true /* ignoreHidden */); + + if (closingAppHasWallpaper && openingAppHasWallpaper) { + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!"); + switch (firstTransit) { + case TRANSIT_OPEN: + return TRANSIT_OLD_WALLPAPER_INTRA_OPEN; + case TRANSIT_CLOSE: + return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; + } + } else if (oldWallpaper != null && !openingApps.isEmpty() + && !openingApps.contains(oldWallpaper.mActivityRecord) + && closingApps.contains(oldWallpaper.mActivityRecord) + && topClosingApp == oldWallpaper.mActivityRecord) { + // We are transitioning from an activity with a wallpaper to one without. + return TRANSIT_OLD_WALLPAPER_CLOSE; + } else if (wallpaperTarget != null && wallpaperTarget.isVisible() + && openingApps.contains(wallpaperTarget.mActivityRecord) + && topOpeningApp == wallpaperTarget.mActivityRecord + /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) { + // We are transitioning from an activity without + // a wallpaper to now showing the wallpaper + return TRANSIT_OLD_WALLPAPER_OPEN; + } + + final ArraySet<WindowContainer> openingWcs = getAnimationTargets( + openingApps, closingApps, true /* visible */); + final ArraySet<WindowContainer> closingWcs = getAnimationTargets( + openingApps, closingApps, false /* visible */); + final boolean isActivityOpening = !openingWcs.isEmpty() + && openingWcs.valueAt(0).asActivityRecord() != null; + final boolean isActivityClosing = !closingWcs.isEmpty() + && closingWcs.valueAt(0).asActivityRecord() != null; + final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening; + final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing; + + if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) { + return TRANSIT_OLD_TASK_TO_FRONT; + } + if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) { + return TRANSIT_OLD_TASK_TO_BACK; + } + if (appTransition.containsTransitRequest(TRANSIT_OPEN)) { + if (isTaskOpening) { + return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0 + ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN; + } + if (isActivityOpening) { + return TRANSIT_OLD_ACTIVITY_OPEN; + } + } + if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) { + if (isTaskClosing) { + return TRANSIT_OLD_TASK_CLOSE; + } + if (isActivityClosing) { + return TRANSIT_OLD_ACTIVITY_CLOSE; + } + } + if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH) + && !openingWcs.isEmpty() && !openingApps.isEmpty()) { + return TRANSIT_OLD_ACTIVITY_RELAUNCH; + } + return TRANSIT_OLD_NONE; + } + private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) { final WindowState mainWindow = activity != null ? activity.findMainWindow() : null; return mainWindow != null ? mainWindow.mAttrs : null; @@ -691,137 +885,6 @@ public class AppTransitionController { return true; } - private int maybeUpdateTransitToWallpaper(@TransitionOldType int transit, - boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) { - // Given no app transition pass it through instead of a wallpaper transition. - // Never convert the crashing transition. - // Never convert a change transition since the top activity isn't changing and will likely - // still be above an opening wallpaper. - if (transit == TRANSIT_OLD_NONE || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE - || AppTransition.isChangeTransit(transit)) { - return transit; - } - - final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); - final boolean showWallpaper = wallpaperTarget != null - && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 - // Update task open transition to wallpaper transition when wallpaper is visible. - // (i.e.launching app info activity from recent tasks) - || ((transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT) - && mWallpaperControllerLocked.isWallpaperVisible())); - // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, - // don't consider upgrading to wallpaper transition. - final WindowState oldWallpaper = - (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) - ? null - : wallpaperTarget; - final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps; - final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps; - final ActivityRecord topOpeningApp = getTopApp(mDisplayContent.mOpeningApps, - false /* ignoreHidden */); - final ActivityRecord topClosingApp = getTopApp(mDisplayContent.mClosingApps, - true /* ignoreHidden */); - - boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps); - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s", - wallpaperTarget, oldWallpaper, openingApps, closingApps); - - if (openingCanBeWallpaperTarget && transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY) { - transit = TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "New transit: %s", AppTransition.appTransitionOldToString(transit)); - } - // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic - // relies on the fact that we always execute a Keyguard transition after preparing one. - else if (!isKeyguardGoingAwayTransit(transit)) { - if (closingAppHasWallpaper && openingAppHasWallpaper) { - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!"); - switch (transit) { - case TRANSIT_OLD_ACTIVITY_OPEN: - case TRANSIT_OLD_TASK_OPEN: - case TRANSIT_OLD_TASK_TO_FRONT: - transit = TRANSIT_OLD_WALLPAPER_INTRA_OPEN; - break; - case TRANSIT_OLD_ACTIVITY_CLOSE: - case TRANSIT_OLD_TASK_CLOSE: - case TRANSIT_OLD_TASK_TO_BACK: - transit = TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; - break; - } - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "New transit: %s", AppTransition.appTransitionOldToString(transit)); - } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty() - && !openingApps.contains(oldWallpaper.mActivityRecord) - && closingApps.contains(oldWallpaper.mActivityRecord) - && topClosingApp == oldWallpaper.mActivityRecord) { - // We are transitioning from an activity with a wallpaper to one without. - transit = TRANSIT_OLD_WALLPAPER_CLOSE; - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "New transit away from wallpaper: %s", - AppTransition.appTransitionOldToString(transit)); - } else if (wallpaperTarget != null && wallpaperTarget.isVisible() - && openingApps.contains(wallpaperTarget.mActivityRecord) - && topOpeningApp == wallpaperTarget.mActivityRecord - && transit != TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE) { - // We are transitioning from an activity without - // a wallpaper to now showing the wallpaper - transit = TRANSIT_OLD_WALLPAPER_OPEN; - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "New transit into wallpaper: %s", - AppTransition.appTransitionOldToString(transit)); - } - } - return transit; - } - - /** - * There are cases where we open/close a new task/activity, but in reality only a translucent - * activity on top of existing activities is opening/closing. For that one, we have a different - * animation because non of the task/activity animations actually work well with translucent - * apps. - * - * @param transit The current transition type. - * @return The current transition type or - * {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE}/ - * {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the - * situation. - */ - @VisibleForTesting - int maybeUpdateTransitToTranslucentAnim(@TransitionOldType int transit) { - if (AppTransition.isChangeTransit(transit)) { - // There's no special animation to handle change animations with translucent apps - return transit; - } - final boolean taskOrActivity = AppTransition.isTaskTransit(transit) - || AppTransition.isActivityTransit(transit); - boolean allOpeningVisible = true; - boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty(); - for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) { - final ActivityRecord activity = mDisplayContent.mOpeningApps.valueAt(i); - if (!activity.isVisible()) { - allOpeningVisible = false; - if (activity.fillsParent()) { - allTranslucentOpeningApps = false; - } - } - } - boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty(); - for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) { - if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) { - allTranslucentClosingApps = false; - break; - } - } - - if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) { - return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; - } - if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) { - return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; - } - return transit; - } - /** * Identifies whether the current transition occurs within a single task or not. This is used * to determine whether animations should be clipped to the task bounds instead of stack bounds. @@ -855,7 +918,7 @@ public class AppTransitionController { return true; } - private boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) { + private static boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) { for (int i = apps.size() - 1; i >= 0; i--) { if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { return true; @@ -873,7 +936,7 @@ public class AppTransitionController { * {@link ActivityRecord#isVisible}. * @return The top {@link ActivityRecord}. */ - private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps, + private static ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps, boolean ignoreInvisible) { int topPrefixOrderIndex = Integer.MIN_VALUE; ActivityRecord topApp = null; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 388422e19090..097f4ed97e15 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -73,9 +73,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; -import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; @@ -627,12 +626,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private boolean mInEnsureActivitiesVisible = false; - /** - * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to - * prevent duplicate requests to input. - */ - WindowState mLastRequestedFocus = null; - private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final ActivityRecord activity = w.mActivityRecord; @@ -4547,22 +4540,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit, - boolean alwaysKeepCurrent) { - prepareAppTransitionOld(transit, alwaysKeepCurrent, 0 /* flags */, - false /* forceOverride */); - } - - void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit, - boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags, - boolean forceOverride) { - final boolean prepared = mAppTransition.prepareAppTransitionOld( - transit, alwaysKeepCurrent, flags, forceOverride); - if (prepared && okToAnimate()) { - mSkipAppTransitionAnimation = false; - } - } - void prepareAppTransition(@WindowManager.TransitionType int transit) { prepareAppTransition(transit, 0 /* flags */); } @@ -4641,10 +4618,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Check if pending app transition is for activity / task launch. */ boolean isNextTransitionForward() { - final int transit = mAppTransition.getAppTransitionOld(); - return transit == TRANSIT_OLD_ACTIVITY_OPEN - || transit == TRANSIT_OLD_TASK_OPEN - || transit == TRANSIT_OLD_TASK_TO_FRONT; + return mAppTransition.containsTransitRequest(TRANSIT_OPEN) + || mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT); } /** @@ -5607,7 +5582,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - public void onAppTransitionCancelledLocked(int transit) { + public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) { continueUpdateOrientationForDiffOrienLaunchingApp(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index e4f92565db9b..f14a2ee8e7ee 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -601,7 +601,7 @@ public class DisplayPolicy { } @Override - public int onAppTransitionStartingLocked(int transit, long duration, + public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { mHandler.post(() -> { StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); @@ -614,7 +614,7 @@ public class DisplayPolicy { } @Override - public void onAppTransitionCancelledLocked(int transit) { + public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) { mHandler.post(mAppTransitionCancelled); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index e7d8ad690bc4..ad4e64a08183 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -45,7 +45,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.util.Slog; import android.view.Display; import android.view.DragEvent; @@ -64,6 +63,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.IDragAndDropPermissions; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java new file mode 100644 index 000000000000..d00faef1c147 --- /dev/null +++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java @@ -0,0 +1,356 @@ +/* + * 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.wm; + +import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.attestation.IImpressionAttestationService; +import android.service.attestation.ImpressionAttestationService; +import android.service.attestation.ImpressionToken; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; + +/** + * Handles requests into {@link ImpressionAttestationService} + * + * Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are + * blocking calls into another service. + */ +public class ImpressionAttestationController { + private static final String TAG = "ImpressionAttestationController"; + private static final boolean DEBUG = false; + + private final Object mServiceConnectionLock = new Object(); + + @GuardedBy("mServiceConnectionLock") + private ImpressionAttestationServiceConnection mServiceConnection; + + private final Context mContext; + + /** + * Lock used for the cached {@link #mImpressionAlgorithms} array + */ + private final Object mImpressionAlgorithmsLock = new Object(); + + @GuardedBy("mImpressionAlgorithmsLock") + private String[] mImpressionAlgorithms; + + private final Handler mHandler; + + private interface Command { + void run(IImpressionAttestationService service) throws RemoteException; + } + + ImpressionAttestationController(Context context) { + mContext = context; + mHandler = new Handler(Looper.getMainLooper()); + } + + String[] getSupportedImpressionAlgorithms() { + // We have a separate lock for the impression algorithm array since it doesn't need to make + // the request through the service connection. Instead, we have a lock to ensure we can + // properly cache the impression algorithms array so we don't need to call into the + // ExtServices process for each request. + synchronized (mImpressionAlgorithmsLock) { + // Already have cached values + if (mImpressionAlgorithms != null) { + return mImpressionAlgorithms; + } + + final ServiceInfo serviceInfo = getServiceInfo(); + if (serviceInfo == null) return null; + + final PackageManager pm = mContext.getPackageManager(); + final Resources res; + try { + res = pm.getResourcesForApplication(serviceInfo.applicationInfo); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Error getting application resources for " + serviceInfo, e); + return null; + } + + final int resourceId = serviceInfo.metaData.getInt( + SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS); + mImpressionAlgorithms = res.getStringArray(resourceId); + + return mImpressionAlgorithms; + } + } + + int verifyImpressionToken(ImpressionToken impressionToken) { + final SyncCommand syncCommand = new SyncCommand(); + Bundle results = syncCommand.run((service, remoteCallback) -> { + try { + service.verifyImpressionToken(impressionToken, remoteCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke verifyImpressionToken command"); + } + }); + + return results.getInt(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS); + } + + ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds, + String hashAlgorithm) { + final SyncCommand syncCommand = new SyncCommand(); + Bundle results = syncCommand.run((service, remoteCallback) -> { + try { + service.generateImpressionToken(screenshot, bounds, hashAlgorithm, remoteCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke generateImpressionToken command", e); + } + }); + + return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN); + } + + /** + * Run a command, starting the service connection if necessary. + */ + private void connectAndRun(@NonNull Command command) { + synchronized (mServiceConnectionLock) { + mHandler.resetTimeoutMessage(); + if (mServiceConnection == null) { + if (DEBUG) Slog.v(TAG, "creating connection"); + + // Create the connection + mServiceConnection = new ImpressionAttestationServiceConnection(); + + final ComponentName component = getServiceComponentName(); + if (DEBUG) Slog.v(TAG, "binding to: " + component); + if (component != null) { + final Intent intent = new Intent(); + intent.setComponent(component); + final long token = Binder.clearCallingIdentity(); + try { + mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE, UserHandle.CURRENT); + if (DEBUG) Slog.v(TAG, "bound"); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + mServiceConnection.runCommandLocked(command); + } + } + + @Nullable + private ServiceInfo getServiceInfo() { + final String packageName = + mContext.getPackageManager().getServicesSystemSharedLibraryPackageName(); + if (packageName == null) { + Slog.w(TAG, "no external services package!"); + return null; + } + + final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE); + intent.setPackage(packageName); + final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + Slog.w(TAG, "No valid components found."); + return null; + } + return resolveInfo.serviceInfo; + } + + @Nullable + private ComponentName getServiceComponentName() { + final ServiceInfo serviceInfo = getServiceInfo(); + if (serviceInfo == null) return null; + + final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); + if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE + .equals(serviceInfo.permission)) { + Slog.w(TAG, name.flattenToShortString() + " requires permission " + + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE); + return null; + } + + if (DEBUG) Slog.v(TAG, "getServiceComponentName(): " + name); + return name; + } + + private class SyncCommand { + private static final int WAIT_TIME_S = 5; + private Bundle mResult; + private final CountDownLatch mCountDownLatch = new CountDownLatch(1); + + public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) { + connectAndRun(service -> { + RemoteCallback callback = new RemoteCallback(result -> { + mResult = result; + mCountDownLatch.countDown(); + }); + func.accept(service, callback); + }); + + try { + mCountDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS); + } catch (Exception e) { + Slog.e(TAG, "Failed to wait for command", e); + } + + return mResult; + } + } + + private class ImpressionAttestationServiceConnection implements ServiceConnection { + @GuardedBy("mServiceConnectionLock") + private IImpressionAttestationService mRemoteService; + + @GuardedBy("mServiceConnectionLock") + private ArrayList<Command> mQueuedCommands; + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name); + synchronized (mServiceConnectionLock) { + mRemoteService = IImpressionAttestationService.Stub.asInterface(service); + if (mQueuedCommands != null) { + final int size = mQueuedCommands.size(); + if (DEBUG) Slog.d(TAG, "running " + size + " queued commands"); + for (int i = 0; i < size; i++) { + final Command queuedCommand = mQueuedCommands.get(i); + try { + if (DEBUG) Slog.v(TAG, "running queued command #" + i); + queuedCommand.run(mRemoteService); + } catch (RemoteException e) { + Slog.w(TAG, "exception calling " + name + ": " + e); + } + } + mQueuedCommands = null; + } else if (DEBUG) { + Slog.d(TAG, "no queued commands"); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Slog.v(TAG, "onServiceDisconnected(): " + name); + synchronized (mServiceConnectionLock) { + mRemoteService = null; + } + } + + @Override + public void onBindingDied(ComponentName name) { + if (DEBUG) Slog.v(TAG, "onBindingDied(): " + name); + synchronized (mServiceConnectionLock) { + mRemoteService = null; + } + } + + @Override + public void onNullBinding(ComponentName name) { + if (DEBUG) Slog.v(TAG, "onNullBinding(): " + name); + synchronized (mServiceConnectionLock) { + mRemoteService = null; + } + } + + /** + * Only call while holding {@link #mServiceConnectionLock} + */ + private void runCommandLocked(Command command) { + if (mRemoteService == null) { + if (DEBUG) Slog.d(TAG, "service is null; queuing command"); + if (mQueuedCommands == null) { + mQueuedCommands = new ArrayList<>(1); + } + mQueuedCommands.add(command); + } else { + try { + if (DEBUG) Slog.v(TAG, "running command right away"); + command.run(mRemoteService); + } catch (RemoteException e) { + Slog.w(TAG, "exception calling service: " + e); + } + } + } + } + + private class Handler extends android.os.Handler { + static final long SERVICE_SHUTDOWN_TIMEOUT_MILLIS = 10000; // 10s + static final int MSG_SERVICE_SHUTDOWN_TIMEOUT = 1; + + Handler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_SERVICE_SHUTDOWN_TIMEOUT) { + if (DEBUG) { + Slog.v(TAG, "Shutting down service"); + } + synchronized (mServiceConnectionLock) { + if (mServiceConnection != null) { + mContext.unbindService(mServiceConnection); + mServiceConnection = null; + } + } + } + } + + /** + * Set a timer for {@link #SERVICE_SHUTDOWN_TIMEOUT_MILLIS} so we can tear down the service + * if it's inactive. The requests will be coming from apps so it's hard to tell how often + * the requests can come in. Therefore, we leave the service running if requests continue + * to come in. Once there's been no activity for 10s, we can shut down the service and + * restart when we get a new request. + */ + void resetTimeoutMessage() { + if (DEBUG) { + Slog.v(TAG, "Reset shutdown message"); + } + removeMessages(MSG_SERVICE_SHUTDOWN_TIMEOUT); + sendEmptyMessageDelayed(MSG_SERVICE_SHUTDOWN_TIMEOUT, SERVICE_SHUTDOWN_TIMEOUT_MILLIS); + } + } + +} diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 457df4e47689..efd9e2a9ddd2 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -73,8 +73,8 @@ import java.util.function.Consumer; final class InputMonitor { private final WindowManagerService mService; - // Current window with input focus for keys and other non-touch events. May be null. - private WindowState mInputFocus; + // Current input focus token for keys and other non-touch events. May be null. + private IBinder mInputFocus = null; // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; @@ -377,31 +377,62 @@ final class InputMonitor { } /** - * Called when the current input focus changes. + * Called when the current input focus changes. Will apply it in next updateInputWindows. * Layer assignment is assumed to be complete by the time this is called. */ - public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { + void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d", newWindow, mDisplayId); + final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null; + if (focus == mInputFocus) { + return; + } - if (newWindow != mInputFocus) { - if (newWindow != null && newWindow.canReceiveKeys()) { - // Displaying a window implicitly causes dispatching to be unpaused. - // This is to protect against bugs if someone pauses dispatching but - // forgets to resume. - newWindow.mToken.paused = false; - } + if (newWindow != null && newWindow.canReceiveKeys()) { + // Displaying a window implicitly causes dispatching to be unpaused. + // This is to protect against bugs if someone pauses dispatching but + // forgets to resume. + newWindow.mToken.paused = false; + } - mInputFocus = newWindow; - setUpdateInputWindowsNeededLw(); + setUpdateInputWindowsNeededLw(); - if (updateInputWindows) { - updateInputWindowsLw(false /*force*/); - } + if (updateInputWindows) { + updateInputWindowsLw(false /*force*/); + } + } + + /** + * Called when the current input focus changes. + */ + private void updateInputFocusRequest() { + final WindowState focus = mDisplayContent.mCurrentFocus; + final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; + + if (focusToken == null) { + mInputFocus = null; + return; + } + + if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) { + Slog.v(TAG_WM, "Focus not requested for window=%" + focus + + " because it has no surface or is not focusable."); + mInputFocus = null; + return; + } + + if (focusToken == mInputFocus) { + return; } + + mInputFocus = focusToken; + mInputTransaction.setFocusedWindow(mInputFocus, mDisplayId); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus, + "reason=UpdateInputWindows"); + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus); } - public void setFocusedAppLw(ActivityRecord newApp) { + void setFocusedAppLw(ActivityRecord newApp) { // Focused app has changed. mService.mInputManager.setFocusedApplication(mDisplayId, newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null); @@ -482,30 +513,6 @@ final class InputMonitor { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - private void updateInputFocusRequest() { - if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) { - return; - } - - final WindowState focus = mDisplayContent.mCurrentFocus; - if (focus == null || focus.mInputChannelToken == null) { - mDisplayContent.mLastRequestedFocus = focus; - return; - } - - if (!focus.mWinAnimator.hasSurface()) { - Slog.v(TAG_WM, "Focus not requested for window=%" + focus - + " because it has no surface."); - return; - } - - mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId); - EventLog.writeEvent(LOGTAG_INPUT_FOCUS, - "Focus request " + focus, "reason=UpdateInputWindows"); - mDisplayContent.mLastRequestedFocus = focus; - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus); - } - @Override public void accept(WindowState w) { final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle; diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 9339f3475684..7a4d13c2d697 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -63,6 +63,10 @@ class InputWindowHandleWrapper { return mHandle.displayId; } + boolean isFocusable() { + return mHandle.focusable; + } + InputApplicationHandle getInputApplicationHandle() { return mHandle.inputApplicationHandle; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 7b600923eb9b..c3b6149482e2 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -26,18 +26,14 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_W import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING; import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES; import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING; @@ -211,11 +207,7 @@ class KeyguardController { 1 /* keyguardGoingAway */, "keyguardGoingAway"); mRootWindowContainer.getDefaultDisplay() - .prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, - false /* alwaysKeepCurrent */, convertTransitFlags(flags), - false /* forceOverride */); - mRootWindowContainer.getDefaultDisplay() - .requestTransitionAndLegacyPrepare(TRANSIT_KEYGUARD_GOING_AWAY, + .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, convertTransitFlags(flags)); updateKeyguardSleepToken(); @@ -363,10 +355,6 @@ class KeyguardController { mService.deferWindowLayout(); try { mRootWindowContainer.getDefaultDisplay() - .prepareAppTransitionOld(resolveOccludeTransit(), - false /* alwaysKeepCurrent */, 0 /* flags */, - true /* forceOverride */); - mRootWindowContainer.getDefaultDisplay() .prepareAppTransition( isDisplayOccluded(DEFAULT_DISPLAY) ? TRANSIT_KEYGUARD_OCCLUDE @@ -397,10 +385,7 @@ class KeyguardController { // we immediately dismiss the Keyguard so the activity gets shown without a flicker. final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); if (mKeyguardShowing && canDismissKeyguard() - && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) { - dc.prepareAppTransitionOld(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, - 0 /* flags */, true /* forceOverride */); - dc.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE); + && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) { mWindowManager.executeAppTransition(); } } @@ -432,28 +417,6 @@ class KeyguardController { || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); } - private int resolveOccludeTransit() { - // TODO(new-app-transition): Remove after migrating to the enw transit system. - final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); - if (mBeforeUnoccludeTransit != TRANSIT_OLD_UNSET - && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE - // TODO(b/113840485): Handle app transition for individual display. - && isDisplayOccluded(DEFAULT_DISPLAY)) { - - // Reuse old transit in case we are occluding Keyguard again, meaning that we never - // actually occclude/unocclude Keyguard, but just run a normal transition. - return mBeforeUnoccludeTransit; - // TODO(b/113840485): Handle app transition for individual display. - } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) { - - // Save transit in case we dismiss/occlude Keyguard shortly after. - mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransitionOld(); - return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; - } else { - return TRANSIT_OLD_KEYGUARD_OCCLUDE; - } - } - private void dismissMultiWindowModeForTaskIfNeeded( @Nullable Task currentTaskControllingOcclusion, boolean turningScreenOn) { // If turningScreenOn is true, it means that the visibility state has changed from diff --git a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java index 250785138355..c8558dda171d 100644 --- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java +++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java @@ -26,10 +26,10 @@ import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.UserHandle; -import android.os.UserManagerInternal; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import com.android.server.policy.WindowManagerPolicy; import com.android.server.utils.UserTokenWatcher; import com.android.server.wm.LockTaskController.LockTaskToken; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6d29a1d4eb93..abee032d042a 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -152,14 +152,14 @@ public class RecentsAnimationController implements DeathRecipient { */ final AppTransitionListener mAppTransitionListener = new AppTransitionListener() { @Override - public int onAppTransitionStartingLocked(int transit, long duration, + public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { continueDeferredCancel(); return 0; } @Override - public void onAppTransitionCancelledLocked(int transit) { + public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) { continueDeferredCancel(); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 5e8067074939..a550e150a7a0 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -39,9 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_NONE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON; @@ -53,11 +51,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; -import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; @@ -66,6 +59,11 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATE import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; +import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; +import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT; import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER; @@ -2181,7 +2179,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Set a transition to ensure that we don't immediately try and update the visibility // of the activity entering PIP - r.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_NONE, false); r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); final boolean singleActivity = task.getChildCount() == 1; @@ -2213,8 +2210,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // to the list of apps being closed, and request its transition to be ran. final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) - && task.getDisplayContent().mAppTransition.getAppTransitionOld() - == TRANSIT_OLD_TASK_TO_BACK) { + && task.getDisplayContent().mAppTransition.containsTransitRequest( + TRANSIT_TO_BACK)) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } @@ -2801,8 +2798,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); r.detachFromProcess(); - r.mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */); + r.mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED); r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED); r.destroyIfPossible("handleAppCrashed"); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 99e17917e0b7..3caf86c24380 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -62,17 +62,12 @@ import static android.view.SurfaceControl.METADATA_TASK_ID; import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; +import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; -import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -85,12 +80,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMA import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN; -import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; -import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; -import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; @@ -113,6 +102,12 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIB import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; +import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; +import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; @@ -208,6 +203,7 @@ import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; +import android.view.WindowManager.TransitionOldType; import android.window.ITaskOrganizer; import com.android.internal.annotations.GuardedBy; @@ -2356,8 +2352,6 @@ class Task extends WindowContainer<WindowContainer> { * Initializes a change transition. See {@link SurfaceFreezer} for more information. */ private void initializeChangeTransition(Rect startBounds) { - mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, - false /* alwaysKeepCurrent */, 0, false /* forceOverride */); mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE); mDisplayContent.mChangingContainers.add(this); @@ -2437,13 +2431,13 @@ class Task extends WindowContainer<WindowContainer> { @VisibleForTesting boolean isInChangeTransition() { - return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit); + return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransitOld(mTransit); } @Override public SurfaceControl getFreezeSnapshotTarget() { - final int transit = mDisplayContent.mAppTransition.getAppTransitionOld(); - if (!AppTransition.isChangeTransit(transit)) { + if (!mDisplayContent.mAppTransition.containsTransitRequest( + TRANSIT_CHANGE_WINDOWING_MODE)) { return null; } // Skip creating snapshot if this transition is controlled by a remote animator which @@ -2452,7 +2446,7 @@ class Task extends WindowContainer<WindowContainer> { activityTypes.add(getActivityType()); final RemoteAnimationAdapter adapter = mDisplayContent.mAppTransitionController.getRemoteAnimationOverride( - this, transit, activityTypes); + this, TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, activityTypes); if (adapter != null && !adapter.getChangeNeedsSnapshot()) { return null; } @@ -3983,7 +3977,7 @@ class Task extends WindowContainer<WindowContainer> { @Override protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter, - int transit, boolean isVoiceInteraction, + @TransitionOldType int transit, boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) { final RecentsAnimationController control = mWmService.getRecentsAnimationController(); if (control != null) { @@ -6151,12 +6145,8 @@ class Task extends WindowContainer<WindowContainer> { "Prepare close transition: prev=" + prev); if (mTaskSupervisor.mNoAnimActivities.contains(prev)) { anim = false; - dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false); dc.prepareAppTransition(TRANSIT_NONE); } else { - dc.prepareAppTransitionOld( - prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_CLOSE - : TRANSIT_OLD_TASK_CLOSE, false); dc.prepareAppTransition(TRANSIT_CLOSE); } prev.setVisibility(false); @@ -6165,24 +6155,18 @@ class Task extends WindowContainer<WindowContainer> { "Prepare open transition: prev=" + prev); if (mTaskSupervisor.mNoAnimActivities.contains(next)) { anim = false; - dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false); dc.prepareAppTransition(TRANSIT_NONE); } else { - dc.prepareAppTransitionOld( - prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_OPEN - : next.mLaunchTaskBehind ? TRANSIT_OLD_TASK_OPEN_BEHIND - : TRANSIT_OLD_TASK_OPEN, /* alwaysKeepCurrent */false); - dc.prepareAppTransition(TRANSIT_OPEN); + dc.prepareAppTransition(TRANSIT_OPEN, + next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); } } } else { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); if (mTaskSupervisor.mNoAnimActivities.contains(next)) { anim = false; - dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false); dc.prepareAppTransition(TRANSIT_NONE); } else { - dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false); dc.prepareAppTransition(TRANSIT_OPEN); } } @@ -6442,7 +6426,6 @@ class Task extends WindowContainer<WindowContainer> { "Prepare open transition: starting " + r); // TODO(shell-transitions): record NO_ANIMATION flag somewhere. if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, keepCurTransition); dc.prepareAppTransition(TRANSIT_NONE); mTaskSupervisor.mNoAnimActivities.add(r); } else { @@ -6462,7 +6445,6 @@ class Task extends WindowContainer<WindowContainer> { transit = TRANSIT_OLD_TASK_OPEN; } } - dc.prepareAppTransitionOld(transit, keepCurTransition); dc.prepareAppTransition(TRANSIT_OPEN); mTaskSupervisor.mNoAnimActivities.remove(r); } @@ -6601,8 +6583,7 @@ class Task extends WindowContainer<WindowContainer> { Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); Task finishedTask = r.getTask(); - mDisplayContent.prepareAppTransitionOld( - TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED); mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED); r.finishIfPossible(reason, false /* oomAdj */); @@ -6834,9 +6815,8 @@ class Task extends WindowContainer<WindowContainer> { forAllActivities(ActivityRecord::removeLaunchTickRunnable); } - private void updateTransitLocked(@WindowManager.TransitionOldType int transit, - @WindowManager.TransitionType int transit2, ActivityOptions options, - boolean forceOverride) { + private void updateTransitLocked(@WindowManager.TransitionType int transit, + ActivityOptions options) { if (options != null) { ActivityRecord r = topRunningActivity(); if (r != null && !r.isState(RESUMED)) { @@ -6845,9 +6825,7 @@ class Task extends WindowContainer<WindowContainer> { ActivityOptions.abort(options); } } - mDisplayContent.prepareAppTransitionOld(transit, false, - 0 /* flags */, forceOverride); - mDisplayContent.prepareAppTransition(transit2); + mDisplayContent.prepareAppTransition(transit); } final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options, @@ -6868,8 +6846,7 @@ class Task extends WindowContainer<WindowContainer> { if (noAnimation) { ActivityOptions.abort(options); } else { - updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT, options, - false /* forceOverride */); + updateTransitLocked(TRANSIT_TO_FRONT, options); } return; } @@ -6904,14 +6881,11 @@ class Task extends WindowContainer<WindowContainer> { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr); if (noAnimation) { - mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_NONE, - false /* alwaysKeepCurrent */); mDisplayContent.prepareAppTransition(TRANSIT_NONE); mTaskSupervisor.mNoAnimActivities.add(top); ActivityOptions.abort(options); } else { - updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT, - options, false /* forceOverride */); + updateTransitLocked(TRANSIT_TO_FRONT, options); } // If a new task is moved to the front, then mark the existing top activity as @@ -6979,8 +6953,7 @@ class Task extends WindowContainer<WindowContainer> { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + tr.mTaskId); - mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_TO_BACK, - false /* alwaysKeepCurrent */); + mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK); mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr); moveToBack("moveTaskToBackLocked", tr); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index e64c0476db1d..009a7ef9e4f2 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -27,7 +27,6 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDO import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.TaskDescription; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -52,7 +51,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Consumer; @@ -538,16 +536,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } mTmpTaskInfo.configuration.unset(); task.fillTaskInfo(mTmpTaskInfo); - boolean changed = lastInfo == null - || mTmpTaskInfo.topActivityType != lastInfo.topActivityType - || mTmpTaskInfo.isResizeable != lastInfo.isResizeable - || !Objects.equals( - mTmpTaskInfo.positionInParent, - lastInfo.positionInParent) - || isLetterboxInfoChanged(lastInfo, mTmpTaskInfo) - || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams - || mTmpTaskInfo.getWindowingMode() != lastInfo.getWindowingMode() - || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription); + boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo); if (!changed) { int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 @@ -582,20 +571,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - private boolean isLetterboxInfoChanged( - final RunningTaskInfo lastInfo, final RunningTaskInfo currentInfo) { - return !Objects.equals( - currentInfo.letterboxActivityBounds, - lastInfo.letterboxActivityBounds) - || !Objects.equals( - currentInfo.getConfiguration().windowConfiguration.getBounds(), - lastInfo.getConfiguration().windowConfiguration.getBounds()) - || !Objects.equals( - currentInfo.getConfiguration().windowConfiguration.getMaxBounds(), - lastInfo.getConfiguration().windowConfiguration.getMaxBounds()) - || !Objects.equals(currentInfo.parentBounds, lastInfo.parentBounds); - } - @Override public WindowContainerToken getImeTarget(int displayId) { enforceTaskPermission("getImeTarget()"); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index f77b6d2a9b1d..6dfcb4192ff2 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -28,7 +28,6 @@ import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.os.Process; import android.os.SystemClock; -import android.os.UserManagerInternal; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; @@ -36,6 +35,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; import java.io.File; diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index ce138674cb93..7d61c1973a2a 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -148,7 +148,7 @@ class WallpaperController { ? w.mActivityRecord.getAnimatingContainer() : null; final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null && animatingContainer.isAnimating(TRANSITION | PARENTS) - && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit) + && AppTransition.isKeyguardGoingAwayTransitOld(animatingContainer.mTransit) && (animatingContainer.mTransitFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 22dca2611302..b25fbc0e18a3 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -81,6 +81,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; import android.view.WindowManager; +import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; import android.window.IWindowContainerToken; import android.window.WindowContainerToken; @@ -250,10 +251,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< boolean mLaunchTaskBehind; /** - * If we are running an animation, this determines the transition type. Must be one of - * {@link AppTransition#TransitionFlags}. + * If we are running an animation, this determines the transition type. */ - int mTransit; + @TransitionOldType int mTransit; /** * If we are running an animation, this determines the flags during this animation. Must be a @@ -2399,8 +2399,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * * @see #getAnimationAdapter */ - boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, - boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) { + boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit, + boolean enter, boolean isVoiceInteraction, + @Nullable ArrayList<WindowContainer> sources) { if (mWmService.mDisableTransitionAnimation) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: transition animation is disabled or skipped. " @@ -2415,6 +2416,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation"); if (okToAnimate()) { + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, + "applyAnimation: transit=%s, enter=%b, wc=%s", + AppTransition.appTransitionOldToString(transit), enter, this); applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources); } else { cancelAnimation(); @@ -2437,7 +2441,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @See LocalAnimationAdapter */ Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp, - int transit, boolean enter, boolean isVoiceInteraction) { + @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) { final Pair<AnimationAdapter, AnimationAdapter> resultAdapters; final int appStackClipMode = getDisplayContent().mAppTransition.getAppStackClipMode(); @@ -2449,7 +2453,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final RemoteAnimationController controller = getDisplayContent().mAppTransition.getRemoteAnimationController(); - final boolean isChanging = AppTransition.isChangeTransit(transit) && enter + final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter && isChangingAppTransition(); // Delaying animation start isn't compatible with remote animations at all. @@ -2497,7 +2501,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< resultAdapters = new Pair<>(adapter, null); mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP - || AppTransition.isClosingTransit(transit); + || AppTransition.isClosingTransitOld(transit); mTransit = transit; mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); } else { @@ -2508,7 +2512,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter, - int transit, boolean isVoiceInteraction, + @TransitionOldType int transit, boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) { final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, transit, enter, isVoiceInteraction); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 872980149246..d082778f50ba 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -115,9 +115,9 @@ public abstract class WindowManagerInternal { /** * Called when a pending app transition gets cancelled. * - * @param transit transition type indicating what kind of transition got cancelled + * @param keyguardGoingAway true if keyguard going away transition transition got cancelled. */ - public void onAppTransitionCancelledLocked(int transit) {} + public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {} /** * Called when an app transition is timed out. @@ -127,8 +127,7 @@ public abstract class WindowManagerInternal { /** * Called when an app transition gets started * - * @param transit transition type indicating what kind of transition gets run, must be one - * of AppTransition.TRANSIT_* values + * @param keyguardGoingAway true if keyguard going away transition is started. * @param duration the total duration of the transition * @param statusBarAnimationStartTime the desired start time for all visual animations in * the status bar caused by this app transition in uptime millis @@ -140,7 +139,7 @@ public abstract class WindowManagerInternal { * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}. */ - public int onAppTransitionStartingLocked(int transit, long duration, + public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { return 0; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f768435d1b7f..7c7dd6ff268c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -79,7 +79,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; @@ -423,16 +422,6 @@ public class WindowManagerService extends IWindowManager.Stub DISABLE_TRIPLE_BUFFERING_PROPERTY, false); /** - * Use new app transit framework. - */ - private static final String USE_NEW_APP_TRANSIT = - "persist.wm.use_new_app_transit"; - /** - * @see #USE_NEW_APP_TRANSIT - */ - static boolean sUseNewAppTransit = SystemProperties.getBoolean(USE_NEW_APP_TRANSIT, false); - - /** * Allows a fullscreen windowing mode activity to launch in its desired orientation directly * when the display has different orientation. */ @@ -1111,7 +1100,7 @@ public class WindowManagerService extends IWindowManager.Stub = new WindowManagerInternal.AppTransitionListener() { @Override - public void onAppTransitionCancelledLocked(int transit) { + public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) { } @Override @@ -1923,8 +1912,6 @@ public class WindowManagerService extends IWindowManager.Stub // animation and piggy-back on existing transition animation infrastructure. final DisplayContent dc = activity.getDisplayContent(); dc.mOpeningApps.add(activity); - dc.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH, - ALWAYS_KEEP_CURRENT, 0 /* flags */, false /* forceOverride */); dc.prepareAppTransition(TRANSIT_RELAUNCH); dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, frame.width(), frame.height()); @@ -1940,8 +1927,6 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent dc = activity.getDisplayContent(); if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) { dc.mOpeningApps.add(activity); - dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */, - false /* forceOverride */); dc.prepareAppTransition(TRANSIT_NONE); dc.executeAppTransition(); } @@ -2827,9 +2812,6 @@ public class WindowManagerService extends IWindowManager.Stub if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - getDefaultDisplayContentLocked().prepareAppTransitionOld(TRANSIT_OLD_NONE, - false /* alwaysKeepCurrent */, - 0 /* flags */, false /* forceOverride */); getDefaultDisplayContentLocked().prepareAppTransition(TRANSIT_NONE); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 20b6b6d135d5..a6466821ff38 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -91,9 +91,6 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.os.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER; -import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER; -import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE; import static android.provider.Settings.Global.PRIVATE_DNS_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import static android.provider.Telephony.Carriers.DPC_URI; @@ -108,6 +105,9 @@ import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS; import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER; import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER; +import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER; +import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE; import android.Manifest.permission; import android.accessibilityservice.AccessibilityServiceInfo; @@ -222,8 +222,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; import android.permission.IPermissionManager; import android.permission.PermissionControllerManager; @@ -293,6 +291,8 @@ import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.RestrictionsSet; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserRestrictionsUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.storage.DeviceStorageMonitorInternal; @@ -4373,7 +4373,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int adminUser = admin.getUserHandle().getIdentifier(); // Password complexity is only taken into account from DO/PO if (isDeviceOwner(adminComponent, adminUser) - || isProfileOwner(adminComponent, adminUser)) { + || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) { maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } } @@ -6216,7 +6216,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getAdminCallerIdentity(comp); + final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); @@ -7487,6 +7487,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return who != null && who.equals(profileOwner); } + private boolean isProfileOwnerUncheckedLocked(ComponentName who, int userId) { + ensureLocked(); + final ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId); + return who != null && who.equals(profileOwner); + } + /** * Returns {@code true} if the provided caller identity is of a profile owner. * @param caller identity of caller. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 9e98fc5ff8b4..8ef69822750f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -30,7 +30,6 @@ import android.os.Environment; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.IndentingPrintWriter; @@ -46,6 +45,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import libcore.io.IoUtils; diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 27b07c76fb0d..21c863dde3f6 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -18,6 +18,7 @@ package com.android.server.pm.test.override import android.content.ComponentName import android.content.Context +import android.content.pm.PackageManager import android.content.pm.parsing.component.ParsedActivity import android.os.Binder import android.os.UserHandle @@ -31,7 +32,6 @@ import com.android.server.pm.UserManagerService import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage -import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType import com.android.server.testutils.TestHandler import com.android.server.testutils.mock @@ -45,11 +45,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized -import org.mockito.Mockito import org.mockito.Mockito.any -import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyInt -import org.mockito.Mockito.anyString import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.intThat import org.mockito.Mockito.never @@ -321,10 +318,6 @@ class PackageManagerComponentLabelIconOverrideTest { whenever(this.exists(intThat(matcher))) { true } whenever(this.isUserUnlockingOrUnlocked(intThat(matcher))) { true } } - val mockPermissionManagerService: PermissionManagerServiceInternal = mockThrowOnUnmocked { - whenever(this.enforceCrossUserPermission(anyInt(), anyInt(), anyBoolean(), anyBoolean(), - anyString())) { } - } val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked { whenever(this.isCallerRecents(anyInt())) { false } } @@ -335,15 +328,19 @@ class PackageManagerComponentLabelIconOverrideTest { val mockContext: Context = mockThrowOnUnmocked { whenever(this.getString( com.android.internal.R.string.config_overrideComponentUiPackage)) { VALID_PKG } + whenever(this.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) { + PackageManager.PERMISSION_GRANTED + } } val mockInjector: PackageManagerService.Injector = mock { whenever(this.lock) { Object() } whenever(this.componentResolver) { mockComponentResolver } whenever(this.userManagerService) { mockUserManagerService } - whenever(this.permissionManagerServiceInternal) { mockPermissionManagerService } whenever(this.settings) { mockSettings } whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) { - mockActivityTaskManager} + mockActivityTaskManager + } whenever(this.appsFilter) { mockAppsFilter } whenever(this.context) { mockContext } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 1cdd873860b8..e43a002806ee 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -66,7 +66,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; - import java.util.ArrayList; import java.util.List; import java.util.function.IntConsumer; @@ -129,6 +128,8 @@ public class FullScreenMagnificationGestureHandlerTest { ScaleChangedListener mMockScaleChangedListener; @Mock MagnificationRequestObserver mMagnificationRequestObserver; + @Mock + WindowMagnificationPromptController mWindowMagnificationPromptController; private OffsettableClock mClock; private FullScreenMagnificationGestureHandler mMgh; @@ -170,7 +171,9 @@ public class FullScreenMagnificationGestureHandlerTest { @After public void tearDown() { + mMgh.onDestroy(); mFullScreenMagnificationController.unregister(DISPLAY_0); + verify(mWindowMagnificationPromptController).onDestroy(); } @NonNull @@ -178,7 +181,8 @@ public class FullScreenMagnificationGestureHandlerTest { boolean detectShortcutTrigger) { FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler( mContext, mFullScreenMagnificationController, mMockScaleChangedListener, - detectTripleTap, detectShortcutTrigger, DISPLAY_0); + detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController, + DISPLAY_0); mHandler = new TestHandler(h.mDetectingState, mClock) { @Override protected String messageToString(Message m) { @@ -434,6 +438,20 @@ public class FullScreenMagnificationGestureHandlerTest { returnToNormalFrom(STATE_PANNING); } + @Test + public void testZoomedWithTripleTap_invokeShowWindowPromptAction() { + goFromStateIdleTo(STATE_ZOOMED); + + verify(mWindowMagnificationPromptController).showNotificationIfNeeded(); + } + + @Test + public void testShortcutTriggered_invokeShowWindowPromptAction() { + goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED); + + verify(mWindowMagnificationPromptController).showNotificationIfNeeded(); + } + private void assertActionsInOrder(List<MotionEvent> actualEvents, List<Integer> expectedActions) { assertTrue(actualEvents.size() == expectedActions.size()); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java new file mode 100644 index 000000000000..5fd28f57c7c3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java @@ -0,0 +1,206 @@ +/* + * 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.accessibility.magnification; + +import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT; + +import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE; +import static com.android.server.accessibility.magnification.WindowMagnificationPromptController.ACTION_TURN_ON_IN_SETTINGS; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.testing.TestableContext; + +import androidx.test.InstrumentationRegistry; + +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.MockitoAnnotations; + +/** + * Tests for {@link WindowMagnificationPromptController}. + */ +public class WindowMagnificationPromptControllerTest { + + private static final int TEST_USER = 0; + + @Mock + private NotificationManager mNotificationManager; + @Mock + private StatusBarManager mStatusBarManager; + @Rule + public A11yTestableContext mTestableContext = new A11yTestableContext( + InstrumentationRegistry.getContext()); + private ContentResolver mResolver = mTestableContext.getContentResolver(); + private WindowMagnificationPromptController mWindowMagnificationPromptController; + private BroadcastReceiver mReceiver; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableContext.addMockSystemService(NotificationManager.class, mNotificationManager); + mTestableContext.addMockSystemService(StatusBarManager.class, mStatusBarManager); + setWindowMagnificationPromptSettings(true); + mWindowMagnificationPromptController = new WindowMagnificationPromptController( + mTestableContext, TEST_USER); + } + + @After + public void tearDown() throws Exception { + mWindowMagnificationPromptController.onDestroy(); + } + + @Test + public void showNotificationIfNeeded_promptSettingsIsOn_showNotification() { + mWindowMagnificationPromptController.showNotificationIfNeeded(); + + verify(mNotificationManager).notify(eq(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE), any( + Notification.class)); + } + + @Test + public void tapTurnOnAction_isShown_cancelNotificationAndLaunchMagnificationSettings() { + showNotificationAndAssert(); + + final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS); + mReceiver.onReceive(mTestableContext, intent); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verifyLaunchMagnificationSettings(); + } + + @Test + public void tapTurnOnAction_isShown_settingsValueIsFalseAndUnregisterReceiver() { + showNotificationAndAssert(); + + final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS); + mReceiver.onReceive(mTestableContext, intent); + + assertThat(Settings.Secure.getInt(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, + -1)).isEqualTo(0); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + @Test + public void tapDismissAction_isShown_cancelNotificationAndUnregisterReceiver() { + showNotificationAndAssert(); + + final Intent intent = new Intent(WindowMagnificationPromptController.ACTION_DISMISS); + mReceiver.onReceive(mTestableContext, intent); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + @Test + public void promptSettingsChangeToFalse_isShown_cancelNotificationAndUnregisterReceiver() { + showNotificationAndAssert(); + + setWindowMagnificationPromptSettings(false); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + @Test + public void onDestroy_isShown_cancelNotificationAndUnregisterReceiver() { + showNotificationAndAssert(); + + mWindowMagnificationPromptController.onDestroy(); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + private void verifyLaunchMagnificationSettings() { + final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass( + UserHandle.class); + verify(mTestableContext.getSpyContext()).startActivityAsUser(intentCaptor.capture(), + bundleCaptor.capture(), userHandleCaptor.capture()); + assertThat(intentCaptor.getValue().getAction()).isEqualTo( + Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER); + verify(mStatusBarManager).collapsePanels(); + } + + private void showNotificationAndAssert() { + mWindowMagnificationPromptController.showNotificationIfNeeded(); + mReceiver = mWindowMagnificationPromptController.mNotificationActionReceiver; + assertThat(mReceiver).isNotNull(); + } + + private void setWindowMagnificationPromptSettings(boolean enable) { + Settings.Secure.putIntForUser(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, + enable ? 1 : 0, TEST_USER); + if (mWindowMagnificationPromptController != null) { + mWindowMagnificationPromptController.onPromptSettingsValueChanged(); + } + } + + private class A11yTestableContext extends TestableContext { + + private Context mSpyContext; + + A11yTestableContext(Context base) { + super(base); + mSpyContext = Mockito.mock(Context.class); + } + + @Override + public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { + mSpyContext.startActivityAsUser(intent, options, user); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler) { + return mSpyContext.registerReceiver(receiver, filter, broadcastPermission, scheduler); + } + + @Override + public void unregisterReceiver(BroadcastReceiver receiver) { + mSpyContext.unregisterReceiver(receiver); + } + + Context getSpyContext() { + return mSpyContext; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index f45cc3bf462c..74c6a7e36c9d 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -71,7 +71,6 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.platform.test.annotations.Presubmit; import android.util.Log; @@ -80,6 +79,7 @@ import androidx.test.filters.SmallTest; import com.android.server.FgThread; import com.android.server.am.UserState.KeyEvictedCallback; +import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.wm.WindowManagerService; diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java new file mode 100644 index 000000000000..efdbda38c01c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -0,0 +1,125 @@ +/* + * 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.face.aidl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.face.SensorProps; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class FaceProviderTest { + + private static final String TAG = "FaceProviderTest"; + + @Mock + private Context mContext; + @Mock + private UserManager mUserManager; + + private SensorProps[] mSensorProps; + private LockoutResetDispatcher mLockoutResetDispatcher; + private FaceProvider mFaceProvider; + + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); + + final SensorProps sensor1 = new SensorProps(); + sensor1.commonProps = new CommonProps(); + sensor1.commonProps.sensorId = 0; + final SensorProps sensor2 = new SensorProps(); + sensor2.commonProps = new CommonProps(); + sensor2.commonProps.sensorId = 1; + + mSensorProps = new SensorProps[] {sensor1, sensor2}; + + mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); + + mFaceProvider = new FaceProvider(mContext, mSensorProps, TAG, + mLockoutResetDispatcher); + } + + @SuppressWarnings("rawtypes") + @Test + public void halServiceDied_resetsAllSchedulers() { + assertEquals(mSensorProps.length, mFaceProvider.getSensorProperties().size()); + + // Schedule N operations on each sensor + final int numFakeOperations = 10; + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + for (int i = 0; i < numFakeOperations; i++) { + final ClientMonitor testMonitor = mock(ClientMonitor.class); + when(testMonitor.getFreshDaemon()).thenReturn(new Object()); + scheduler.scheduleClientMonitor(testMonitor); + } + } + + waitForIdle(); + // The right amount of pending and current operations are scheduled + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount()); + assertNotNull(scheduler.getCurrentClient()); + } + + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFaceProvider.binderDied(); + waitForIdle(); + + // No pending operations, no current operation. + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertNull(scheduler.getCurrentClient()); + assertEquals(0, scheduler.getCurrentPendingCount()); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java index 35fc7f09c057..99aab5c7a6af 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.server.biometrics.sensors.face; +package com.android.server.biometrics.sensors.face.hidl; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -28,8 +29,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.LockoutResetDispatcher; -import com.android.server.biometrics.sensors.face.hidl.Face10; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,8 @@ public class Face10Test { private Context mContext; @Mock private UserManager mUserManager; + @Mock + private BiometricScheduler mScheduler; private LockoutResetDispatcher mLockoutResetDispatcher; private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10; @@ -68,7 +71,7 @@ public class Face10Test { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); mFace10 = new Face10(mContext, SENSOR_ID, BiometricManager.Authenticators.BIOMETRIC_STRONG, mLockoutResetDispatcher, false /* supportsSelfIllumination */, - 1 /* maxTemplatesAllowed */); + 1 /* maxTemplatesAllowed */, mScheduler); mBinder = new Binder(); } @@ -78,4 +81,13 @@ public class Face10Test { 0 /* challenge */); waitForIdle(); } + + @Test + public void halServiceDied_resetsScheduler() { + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFace10.serviceDied(0 /* cookie */); + waitForIdle(); + verify(mScheduler).reset(); + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java new file mode 100644 index 000000000000..624775b775fc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -0,0 +1,128 @@ +/* + * 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.aidl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class FingerprintProviderTest { + + private static final String TAG = "FingerprintProviderTest"; + + @Mock + private Context mContext; + @Mock + private UserManager mUserManager; + @Mock + private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; + + private SensorProps[] mSensorProps; + private LockoutResetDispatcher mLockoutResetDispatcher; + private FingerprintProvider mFingerprintProvider; + + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); + + final SensorProps sensor1 = new SensorProps(); + sensor1.commonProps = new CommonProps(); + sensor1.commonProps.sensorId = 0; + final SensorProps sensor2 = new SensorProps(); + sensor2.commonProps = new CommonProps(); + sensor2.commonProps.sensorId = 1; + + mSensorProps = new SensorProps[] {sensor1, sensor2}; + + mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); + + mFingerprintProvider = new FingerprintProvider(mContext, mSensorProps, TAG, + mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + } + + @SuppressWarnings("rawtypes") + @Test + public void halServiceDied_resetsAllSchedulers() { + assertEquals(mSensorProps.length, mFingerprintProvider.getSensorProperties().size()); + + // Schedule N operations on each sensor + final int numFakeOperations = 10; + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + for (int i = 0; i < numFakeOperations; i++) { + final ClientMonitor testMonitor = mock(ClientMonitor.class); + when(testMonitor.getFreshDaemon()).thenReturn(new Object()); + scheduler.scheduleClientMonitor(testMonitor); + } + } + + waitForIdle(); + // The right amount of pending and current operations are scheduled + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount()); + assertNotNull(scheduler.getCurrentClient()); + } + + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFingerprintProvider.binderDied(); + waitForIdle(); + + // No pending operations, no current operation. + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertNull(scheduler.getCurrentClient()); + assertEquals(0, scheduler.getCurrentPendingCount()); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java new file mode 100644 index 000000000000..b2aeb33039f5 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java @@ -0,0 +1,95 @@ +/* + * 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.hidl; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.biometrics.BiometricManager; +import android.os.Handler; +import android.os.Looper; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.internal.R; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class Fingerprint21Test { + + private static final String TAG = "Fingerprint21Test"; + private static final int SENSOR_ID = 1; + + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private UserManager mUserManager; + @Mock + Fingerprint21.HalResultController mHalResultController; + @Mock + private BiometricScheduler mScheduler; + + private LockoutResetDispatcher mLockoutResetDispatcher; + private Fingerprint21 mFingerprint21; + + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getInteger(eq(R.integer.config_fingerprintMaxTemplatesPerUser))) + .thenReturn(5); + + mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); + mFingerprint21 = new Fingerprint21(mContext, mScheduler, + new Handler(Looper.getMainLooper()), SENSOR_ID, + BiometricManager.Authenticators.BIOMETRIC_WEAK, mLockoutResetDispatcher, + mHalResultController); + } + + @Test + public void halServiceDied_resetsScheduler() { + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFingerprint21.serviceDied(0 /* cookie */); + waitForIdle(); + verify(mScheduler).reset(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index cbe49eb12f95..904e93b7d8cf 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -36,7 +36,6 @@ import android.os.Looper; import android.os.PowerManagerInternal; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.permission.IPermissionManager; import android.security.KeyChain; import android.telephony.TelephonyManager; @@ -51,6 +50,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.pm.UserManagerInternal; import java.io.File; import java.io.IOException; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index cb49a519d10a..1d2dcaecf978 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -31,7 +31,6 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.test.mock.MockContext; import android.util.ArrayMap; import android.util.ExceptionUtils; @@ -39,6 +38,7 @@ import android.util.ExceptionUtils; import androidx.annotation.NonNull; import com.android.internal.util.FunctionalUtils; +import com.android.server.pm.UserManagerInternal; import org.junit.Assert; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 431cc27a6635..34313b888e48 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -54,7 +54,6 @@ import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.permission.IPermissionManager; import android.provider.Settings; import android.security.KeyChain; @@ -70,6 +69,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.File; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java index 6f62014f0141..649626492448 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java @@ -22,6 +22,7 @@ import static com.android.server.hdmi.HdmiUtils.buildMessage; import static com.google.common.truth.Truth.assertThat; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.platform.test.annotations.Presubmit; @@ -103,7 +104,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicTv_1_4() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_1_4, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -113,7 +114,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicPlayback_1_4() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1, - Constants.VERSION_1_4, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -123,7 +124,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicPlaybackAudioSystem_1_4() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1, - Constants.VERSION_1_4, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -134,7 +135,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicTv_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -144,7 +145,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_remoteControlTv_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList()); @@ -154,7 +155,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_remoteControlPlayback_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE, Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU, Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList()); @@ -165,7 +166,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_deviceFeaturesTv_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN)); @@ -176,7 +177,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_deviceFeaturesPlayback_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE, Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU, Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 2e4bed97dbec..3bfaf7024486 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -470,7 +470,7 @@ public class HdmiControlServiceTest { mTestLooper.dispatchAll(); HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures( - Constants.ADDR_PLAYBACK_1, Constants.VERSION_2_0, + Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0, Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM), mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(), mMyPlaybackDevice.getDeviceFeatures()); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index d44d37e4e2a1..679d6900e47b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -44,7 +44,6 @@ import android.os.FileUtils; import android.os.IProgressListener; import android.os.RemoteException; import android.os.UserManager; -import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.security.KeyStore; @@ -57,6 +56,7 @@ import com.android.internal.widget.LockSettingsInternal; import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.After; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 4e1454bd0962..73191dca6093 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -27,7 +27,6 @@ import android.os.Handler; import android.os.Parcel; import android.os.Process; import android.os.RemoteException; -import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.security.KeyStore; import android.security.keystore.KeyPermanentlyInvalidatedException; @@ -35,6 +34,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException; import com.android.internal.widget.LockscreenCredential; import com.android.server.ServiceThread; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; +import com.android.server.pm.UserManagerInternal; import java.io.FileNotFoundException; diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index c4d185cfc7f2..e46ab6b01f0a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -84,7 +84,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import android.test.InstrumentationTestCase; import android.test.mock.MockContext; import android.util.ArrayMap; @@ -587,7 +586,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { - return true; + return mInjectCheckAccessShortcutsPermission; } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 95c881e1d927..4b90a5c4a167 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -47,7 +47,6 @@ import android.os.BaseBundle; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; 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 1ca3c7488f32..fcbb5ed1140c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -46,7 +46,6 @@ import android.content.pm.parsing.ParsingPackage; import android.content.res.TypedArray; import android.os.Environment; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.platform.test.annotations.Presubmit; import android.util.Pair; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index c7a05ba68e1e..194ae055e01f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -490,6 +490,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.pushDynamicShortcut(s8); assertEquals(4, getCallerShortcut("s8").getRank()); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s8"), HANDLE_USER_0, CACHE_OWNER_0); }); @@ -1456,6 +1457,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Cache 1 and 2 runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), @@ -1538,6 +1540,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Cache some, but non long lived shortcuts will be ignored. runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"), @@ -1597,6 +1600,48 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { "s2"); } + public void testCachedShortcuts_accessShortcutsPermission() { + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), + makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"), + makeLongLivedShortcut("s4")))); + }); + + // s1 is not long lived and will be ignored. + runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = false; + assertExpectException( + SecurityException.class, "Caller can't access shortcut information", () -> { + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"), + HANDLE_USER_0, CACHE_OWNER_0); + }); + // Give ACCESS_SHORTCUTS permission to LAUNCHER_1 + mInjectCheckAccessShortcutsPermission = true; + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"), + HANDLE_USER_0, CACHE_OWNER_0); + }); + + setCaller(CALLING_PACKAGE_1); + + // Get cached shortcuts + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s2", "s3"); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = false; + assertExpectException( + SecurityException.class, "Caller can't access shortcut information", () -> { + mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"), + HANDLE_USER_0, CACHE_OWNER_0); + }); + // Give ACCESS_SHORTCUTS permission to LAUNCHER_1 + mInjectCheckAccessShortcutsPermission = true; + mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"), + HANDLE_USER_0, CACHE_OWNER_0); + }); + + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3"); + } + public void testCachedShortcuts_canPassShortcutLimit() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4"); @@ -1609,6 +1654,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Cache All runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"), HANDLE_USER_0, CACHE_OWNER_0); }); @@ -1808,6 +1854,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { setCaller(LAUNCHER_1); // Cache some shortcuts. Only long lived shortcuts can get cached. + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), getCallingUser(), CACHE_OWNER_0); mLauncherApps.cacheShortcuts(CALLING_PACKAGE_3, list("s3"), getCallingUser(), @@ -2009,6 +2056,53 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } + public void testGetShortcuts_personsFlag() { + ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id") + .setShortLabel("label") + .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setPerson(makePerson("person", "personKey", "personUri")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .build(); + + setCaller(CALLING_PACKAGE_1); + assertTrue(mManager.setDynamicShortcuts(list(s))); + + setCaller(LAUNCHER_1); + + assertNull(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, + ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), + getCallingUser()).get(0).getPersons()); + + assertNull(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, + ShortcutQuery.FLAG_MATCH_DYNAMIC), + getCallingUser()).get(0).getPersons()); + + // Using FLAG_GET_PERSONS_DATA should fail without permission + mInjectCheckAccessShortcutsPermission = false; + assertExpectException( + SecurityException.class, "Caller can't access shortcut information", () -> { + mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, + ShortcutQuery.FLAG_MATCH_DYNAMIC + | ShortcutQuery.FLAG_GET_PERSONS_DATA), + getCallingUser()); + }); + + mInjectCheckAccessShortcutsPermission = true; + assertEquals("person", mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, + ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_PERSONS_DATA), + getCallingUser()).get(0).getPersons()[0].getName()); + + assertNull(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, + ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_PERSONS_DATA + | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), + getCallingUser()).get(0).getPersons()); + } + // TODO resource public void testGetShortcutInfo() { // Create shortcuts. @@ -8740,6 +8834,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s3", USER_0, filter_any)); + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java index 6a2b8e0da2d2..c8a405284468 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java @@ -117,6 +117,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { runWithCaller(LAUNCHER_1, USER_0, () -> { mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0); + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0, CACHE_OWNER_0); }); @@ -216,6 +217,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { runWithCaller(LAUNCHER_1, USER_0, () -> { mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL, mTestLooper.getNewExecutor()); + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0, @@ -242,6 +244,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL, @@ -274,6 +277,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL, @@ -301,6 +305,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { }); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0); @@ -500,6 +505,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL, @@ -559,6 +565,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); @@ -596,6 +603,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { }); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); @@ -664,6 +672,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); @@ -731,6 +740,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); @@ -799,6 +809,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest { ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class); runWithCaller(LAUNCHER_1, USER_0, () -> { + mInjectCheckAccessShortcutsPermission = true; mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0, CACHE_OWNER_0); mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java index 44b202d60644..35c513f3de8e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java @@ -27,7 +27,6 @@ import android.os.Looper; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java index 5846fc110d15..b0423bfd19af 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.fail; import android.app.PropertyInvalidatedCache; import android.content.pm.UserInfo; import android.os.Looper; -import android.os.UserManagerInternal; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 2250185cf3d7..4fac9dc391e3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -44,7 +44,6 @@ import android.content.pm.UserInfo.UserInfoFlag; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.text.TextUtils; import androidx.test.InstrumentationRegistry; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java index 8e94544707a8..0b44c59a3e15 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java @@ -43,7 +43,6 @@ import android.content.pm.UserInfo; import android.os.Looper; import android.os.SystemProperties; import android.os.UserManager; -import android.os.UserManagerInternal; import android.support.test.uiautomator.UiDevice; import android.util.ArrayMap; import android.util.ArraySet; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java index 36ac5d5a111d..ce6939a9beeb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java @@ -89,7 +89,7 @@ public class BadgeExtractorTest extends UiServiceTestCase { when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel); Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( - PendingIntent.getActivity(mContext, 0, new Intent(), 0), + PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE), Icon.createWithResource("", 0)).build(); int flags = metadata.getFlags(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 338ed066e1e4..f1dc098fccf6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.timeout; import android.app.ActivityOptions; @@ -53,6 +54,7 @@ import org.mockito.ArgumentMatcher; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import java.util.function.ToIntFunction; /** * Tests for the {@link ActivityMetricsLaunchObserver} class. @@ -158,6 +160,41 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { verifyNoMoreInteractions(mLaunchObserver); } + @Test + public void testLaunchState() { + final ToIntFunction<Boolean> launchTemplate = doRelaunch -> { + clearInvocations(mLaunchObserver); + onActivityLaunched(mTopActivity); + notifyTransitionStarting(mTopActivity); + if (doRelaunch) { + mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity); + } + final ActivityMetricsLogger.TransitionInfoSnapshot info = + notifyWindowsDrawn(mTopActivity); + verifyOnActivityLaunchFinished(mTopActivity); + return info.getLaunchState(); + }; + + final WindowProcessController app = mTopActivity.app; + // Assume that the process is started (ActivityBuilder has mocked the returned value of + // ATMS#getProcessController) but the activity has not attached process. + mTopActivity.app = null; + assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_WARM); + + mTopActivity.app = app; + assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_HOT); + + assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH); + + mTopActivity.app = null; + doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid); + assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_COLD); + } + private void onActivityLaunched(ActivityRecord activity) { onIntentStarted(activity.intent); notifyActivityLaunched(START_SUCCESS, activity); @@ -168,15 +205,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testOnActivityLaunchFinished() { - // Assume that the process is started (ActivityBuilder has mocked the returned value of - // ATMS#getProcessController) but the activity has not attached process. - mTopActivity.app = null; onActivityLaunched(mTopActivity); notifyTransitionStarting(mTopActivity); - final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity); - assertWithMessage("Warm launch").that(info.getLaunchState()) - .isEqualTo(WaitResult.LAUNCH_STATE_WARM); + notifyWindowsDrawn(mTopActivity); verifyOnActivityLaunchFinished(mTopActivity); verifyNoMoreInteractions(mLaunchObserver); @@ -231,8 +263,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { assertWithMessage("Record start source").that(info.sourceType) .isEqualTo(SourceInfo.TYPE_LAUNCHER); assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10); - assertWithMessage("Hot launch").that(info.getLaunchState()) - .isEqualTo(WaitResult.LAUNCH_STATE_HOT); verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong()); verifyOnActivityLaunchFinished(mTopActivity); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index bb526695edfb..53ade0ea64be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -16,10 +16,8 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; @@ -32,7 +30,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_CLOSE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; @@ -125,61 +123,65 @@ import org.mockito.invocation.InvocationOnMock; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityRecordTests extends WindowTestsBase { - private Task mStack; - private Task mTask; - private ActivityRecord mActivity; @Before public void setUp() throws Exception { - mTask = new TaskBuilder(mSupervisor) - .setCreateParentTask(true).setCreateActivity(true).build(); - mStack = mTask.getRootTask(); - mActivity = mTask.getTopNonFinishingActivity(); - setBooted(mAtm); } @Test public void testStackCleanupOnClearingTask() { - mActivity.onParentChanged(null /*newParent*/, mActivity.getTask()); - verify(mStack, times(1)).cleanUpActivityReferences(any()); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + activity.onParentChanged(null /*newParent*/, task); + verify(rootTask, times(1)).cleanUpActivityReferences(any()); } @Test public void testStackCleanupOnActivityRemoval() { - mTask.removeChild(mActivity); - verify(mStack, times(1)).cleanUpActivityReferences(any()); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + task.removeChild(activity); + verify(rootTask, times(1)).cleanUpActivityReferences(any()); } @Test public void testStackCleanupOnTaskRemoval() { - mStack.removeChild(mTask, null /*reason*/); - // Stack should be gone on task removal. - assertNull(mAtm.mRootWindowContainer.getStack(mStack.mTaskId)); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + rootTask.removeChild(task, null /*reason*/); + // parentTask should be gone on task removal. + assertNull(mAtm.mRootWindowContainer.getStack(rootTask.mTaskId)); } @Test public void testRemoveChildWithOverlayActivity() { - final ActivityRecord overlayActivity = - new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task).build(); overlayActivity.setTaskOverlay(true); - final ActivityRecord overlayActivity2 = - new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord overlayActivity2 = new ActivityBuilder(mAtm).setTask(task).build(); overlayActivity2.setTaskOverlay(true); - mTask.removeChild(overlayActivity2, "test"); + task.removeChild(overlayActivity2, "test"); verify(mSupervisor, never()).removeTask(any(), anyBoolean(), anyBoolean(), any()); } @Test public void testNoCleanupMovingActivityInSameStack() { - final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build(); - mActivity.reparent(newTask, 0, null /*reason*/); - verify(mStack, times(0)).cleanUpActivityReferences(any()); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task rootTask = activity.getRootTask(); + final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build(); + activity.reparent(newTask, 0, null /*reason*/); + verify(rootTask, times(0)).cleanUpActivityReferences(any()); } @Test public void testPausingWhenVisibleFromStopped() throws Exception { + final ActivityRecord activity = createActivityWithTask(); final MutableBoolean pauseFound = new MutableBoolean(false); doAnswer((InvocationOnMock invocationOnMock) -> { final ClientTransaction transaction = invocationOnMock.getArgument(0); @@ -187,49 +189,50 @@ public class ActivityRecordTests extends WindowTestsBase { pauseFound.value = true; } return null; - }).when(mActivity.app.getThread()).scheduleTransaction(any()); + }).when(activity.app.getThread()).scheduleTransaction(any()); - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); // The activity is in the focused stack so it should be resumed. - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(RESUMED)); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(RESUMED)); assertFalse(pauseFound.value); // Make the activity non focusable - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); - doReturn(false).when(mActivity).isFocusable(); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); + doReturn(false).when(activity).isFocusable(); // If the activity is not focusable, it should move to paused. - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(PAUSING)); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(PAUSING)); assertTrue(pauseFound.value); // Make sure that the state does not change for current non-stopping states. - mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); - doReturn(true).when(mActivity).isFocusable(); + activity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); + doReturn(true).when(activity).isFocusable(); - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(INITIALIZING)); + assertTrue(activity.isState(INITIALIZING)); // Make sure the state does not change if we are not the current top activity. - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - mStack.mTranslucentActivityWaiting = topActivity; - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(STARTED)); + final Task task = activity.getTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); + task.mTranslucentActivityWaiting = topActivity; + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(STARTED)); - mStack.mTranslucentActivityWaiting = null; + task.mTranslucentActivityWaiting = null; topActivity.setOccludesParent(false); - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque"); - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(STARTED)); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque"); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(STARTED)); } - private void ensureActivityConfiguration() { - mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); + private void ensureActivityConfiguration(ActivityRecord activity) { + activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } @Test @@ -245,136 +248,145 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testsApplyOptionsLocked() { + final ActivityRecord activity = createActivityWithTask(); ActivityOptions activityOptions = ActivityOptions.makeBasic(); // Set and apply options for ActivityRecord. Pending options should be cleared - mActivity.updateOptionsLocked(activityOptions); - mActivity.applyOptionsLocked(); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(activityOptions); + activity.applyOptionsLocked(); + assertNull(activity.pendingOptions); // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options. // Pending options should be cleared for both ActivityRecords - ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(mTask).build(); + ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(activity.getTask()).build(); activity2.updateOptionsLocked(activityOptions); - mActivity.updateOptionsLocked(activityOptions); - mActivity.applyOptionsLocked(); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(activityOptions); + activity.applyOptionsLocked(); + assertNull(activity.pendingOptions); assertNull(activity2.pendingOptions); // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options. // Pending options should be cleared for only ActivityRecord that was applied - Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build(); - activity2 = new ActivityBuilder(mAtm).setTask(task2).build(); + activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity2.updateOptionsLocked(activityOptions); - mActivity.updateOptionsLocked(activityOptions); - mActivity.applyOptionsLocked(); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(activityOptions); + activity.applyOptionsLocked(); + assertNull(activity.pendingOptions); assertNotNull(activity2.pendingOptions); } @Test public void testNewOverrideConfigurationIncrementsSeq() { + final ActivityRecord activity = createActivityWithTask(); final Configuration newConfig = new Configuration(); - final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; - mActivity.onRequestedOverrideConfigurationChanged(newConfig); - assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq); + final int prevSeq = activity.getMergedOverrideConfiguration().seq; + activity.onRequestedOverrideConfigurationChanged(newConfig); + assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq); } @Test public void testNewParentConfigurationIncrementsSeq() { + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); final Configuration newConfig = new Configuration( - mTask.getRequestedOverrideConfiguration()); + task.getRequestedOverrideConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; - mTask.onRequestedOverrideConfigurationChanged(newConfig); - assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq); + final int prevSeq = activity.getMergedOverrideConfiguration().seq; + task.onRequestedOverrideConfigurationChanged(newConfig); + assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq); } @Test public void testSetsRelaunchReason_NotDragResizing() { - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - mActivity.info.configChanges &= ~CONFIG_ORIENTATION; - final Configuration newConfig = new Configuration(mTask.getConfiguration()); + activity.info.configChanges &= ~CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - mTask.onRequestedOverrideConfigurationChanged(newConfig); + task.onRequestedOverrideConfigurationChanged(newConfig); - mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; + activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; - ensureActivityConfiguration(); + ensureActivityConfiguration(activity); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE, - mActivity.mRelaunchReason); + activity.mRelaunchReason); } @Test public void testSetsRelaunchReason_DragResizing() { - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - mActivity.info.configChanges &= ~CONFIG_ORIENTATION; - final Configuration newConfig = new Configuration(mTask.getConfiguration()); + activity.info.configChanges &= ~CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - mTask.onRequestedOverrideConfigurationChanged(newConfig); + task.onRequestedOverrideConfigurationChanged(newConfig); - doReturn(true).when(mTask).isDragResizing(); + doReturn(true).when(task).isDragResizing(); - mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; + activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; - ensureActivityConfiguration(); + ensureActivityConfiguration(activity); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE, - mActivity.mRelaunchReason); + activity.mRelaunchReason); } @Test public void testSetsRelaunchReason_NonResizeConfigChanges() { - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE; - final Configuration newConfig = new Configuration(mTask.getConfiguration()); + activity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE; + final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.fontScale = 5; - mTask.onRequestedOverrideConfigurationChanged(newConfig); + task.onRequestedOverrideConfigurationChanged(newConfig); - mActivity.mRelaunchReason = + activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; - ensureActivityConfiguration(); + ensureActivityConfiguration(activity); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE, - mActivity.mRelaunchReason); + activity.mRelaunchReason); } @Test public void testSetRequestedOrientationUpdatesConfiguration() throws Exception { - mActivity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true) .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT) .build(); - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - final Configuration newConfig = new Configuration(mActivity.getConfiguration()); + final Configuration newConfig = new Configuration(activity.getConfiguration()); final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp); final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp); if (newConfig.orientation == ORIENTATION_PORTRAIT) { @@ -388,7 +400,7 @@ public class ActivityRecordTests extends WindowTestsBase { } // Mimic the behavior that display doesn't handle app's requested orientation. - final DisplayContent dc = mTask.getDisplayContent(); + final DisplayContent dc = activity.getTask().getDisplayContent(); doReturn(false).when(dc).onDescendantOrientationChanged(any(), any()); doReturn(false).when(dc).handlesOrientationChangeFromDescendant(); @@ -404,24 +416,26 @@ public class ActivityRecordTests extends WindowTestsBase { throw new IllegalStateException("Orientation in new config should be either" + "landscape or portrait."); } - mActivity.setRequestedOrientation(requestedOrientation); + activity.setRequestedOrientation(requestedOrientation); final ActivityConfigurationChangeItem expected = ActivityConfigurationChangeItem.obtain(newConfig); - verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()), - eq(mActivity.appToken), eq(expected)); + verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()), + eq(activity.appToken), eq(expected)); } @Test public void ignoreRequestedOrientationInFreeformWindows() { - mStack.setWindowingMode(WINDOWING_MODE_FREEFORM); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FREEFORM); final Rect stableRect = new Rect(); - mStack.mDisplayContent.getStableRect(stableRect); + task.mDisplayContent.getStableRect(stableRect); // Carve out non-decor insets from stableRect final Rect insets = new Rect(); - final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo(); - final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy(); + final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo(); + final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, insets); policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation); @@ -440,27 +454,30 @@ public class ActivityRecordTests extends WindowTestsBase { bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2; bounds.right = bounds.left + newWidth; } - mTask.setBounds(bounds); + task.setBounds(bounds); // Requests orientation that's different from its bounds. - mActivity.setRequestedOrientation( + activity.setRequestedOrientation( isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); // Asserts it has orientation derived from bounds. assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, - mActivity.getConfiguration().orientation); + activity.getConfiguration().orientation); } @Test public void ignoreRequestedOrientationInSplitWindows() { - mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + rootTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final Rect stableRect = new Rect(); - mStack.mDisplayContent.getStableRect(stableRect); + rootTask.mDisplayContent.getStableRect(stableRect); // Carve out non-decor insets from stableRect final Rect insets = new Rect(); - final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo(); - final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy(); + final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo(); + final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, insets); policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation); @@ -479,85 +496,91 @@ public class ActivityRecordTests extends WindowTestsBase { bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2; bounds.right = bounds.left + newWidth; } - mTask.setBounds(bounds); + task.setBounds(bounds); // Requests orientation that's different from its bounds. - mActivity.setRequestedOrientation( + activity.setRequestedOrientation( isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); // Asserts it has orientation derived from bounds. assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, - mActivity.getConfiguration().orientation); + activity.getConfiguration().orientation); } @Test public void testShouldMakeActive_deferredResume() { - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); mSupervisor.beginDeferResume(); - assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */)); + assertEquals(false, activity.shouldMakeActive(null /* activeActivity */)); mSupervisor.endDeferResume(); - assertEquals(true, mActivity.shouldMakeActive(null /* activeActivity */)); + assertEquals(true, activity.shouldMakeActive(null /* activeActivity */)); } @Test public void testShouldMakeActive_nonTopVisible() { - ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build(); finishingActivity.finishing = true; - ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); + ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); - assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */)); + assertEquals(false, activity.shouldMakeActive(null /* activeActivity */)); } @Test public void testShouldResume_stackVisibility() { - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - spyOn(mStack); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); - doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null); - assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */)); + doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null); + assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */)); - doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null); - assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); + doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null); + assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */)); - doReturn(TASK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null); - assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); + doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null); + assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */)); } @Test public void testShouldResumeOrPauseWithResults() { - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - spyOn(mStack); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); - ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - mActivity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent()); + ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); + activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent()); topActivity.finishing = true; - doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null); - assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */)); - assertEquals(false, mActivity.shouldPauseActivity(null /*activeActivity */)); + doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null); + assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */)); + assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */)); } @Test public void testPushConfigurationWhenLaunchTaskBehind() throws Exception { - mActivity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true) .setLaunchTaskBehind(true) .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT) .build(); - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); try { doReturn(false).when(stack).isTranslucent(any()); - assertTrue(mStack.shouldBeVisible(null /* starting */)); + assertTrue(task.shouldBeVisible(null /* starting */)); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - final Configuration newConfig = new Configuration(mActivity.getConfiguration()); + final Configuration newConfig = new Configuration(activity.getConfiguration()); final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp); final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp); if (newConfig.orientation == ORIENTATION_PORTRAIT) { @@ -570,15 +593,15 @@ public class ActivityRecordTests extends WindowTestsBase { newConfig.screenHeightDp = longSide; } - mTask.onConfigurationChanged(newConfig); + task.onConfigurationChanged(newConfig); - mActivity.ensureActivityConfiguration(0 /* globalChanges */, + activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */, true /* ignoreStopState */); final ActivityConfigurationChangeItem expected = ActivityConfigurationChangeItem.obtain(newConfig); verify(mAtm.getLifecycleManager()).scheduleTransaction( - eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected)); + eq(activity.app.getThread()), eq(activity.appToken), eq(expected)); } finally { stack.getDisplayArea().removeChild(stack); } @@ -586,16 +609,18 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testShouldStartWhenMakeClientActive() { - ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build(); topActivity.setOccludesParent(false); - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - mActivity.setVisibility(true); - mActivity.makeActiveIfNeeded(null /* activeActivity */); - assertEquals(STARTED, mActivity.getState()); + activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setVisibility(true); + activity.makeActiveIfNeeded(null /* activeActivity */); + assertEquals(STARTED, activity.getState()); } @Test public void testTakeOptions() { + final ActivityRecord activity = createActivityWithTask(); ActivityOptions opts = ActivityOptions.makeRemoteAnimation( new RemoteAnimationAdapter(new Stub() { @@ -611,13 +636,13 @@ public class ActivityRecordTests extends WindowTestsBase { } }, 0, 0)); - mActivity.updateOptionsLocked(opts); - assertNotNull(mActivity.takeOptionsLocked(true /* fromClient */)); - assertNotNull(mActivity.pendingOptions); + activity.updateOptionsLocked(opts); + assertNotNull(activity.takeOptionsLocked(true /* fromClient */)); + assertNotNull(activity.pendingOptions); - mActivity.updateOptionsLocked(ActivityOptions.makeBasic()); - assertNotNull(mActivity.takeOptionsLocked(false /* fromClient */)); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(ActivityOptions.makeBasic()); + assertNotNull(activity.takeOptionsLocked(false /* fromClient */)); + assertNull(activity.pendingOptions); } @Test @@ -626,7 +651,7 @@ public class ActivityRecordTests extends WindowTestsBase { Resources.getSystem().getString(R.string.config_chooserActivity)); ActivityRecord chooserActivity = new ActivityBuilder(mAtm).setComponent( chooserComponent).build(); - assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue(); + assertThat(chooserActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue(); } /** @@ -635,49 +660,52 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testHasSavedState() { - assertTrue(mActivity.hasSavedState()); + final ActivityRecord activity = createActivityWithTask(); + assertTrue(activity.hasSavedState()); - ActivityRecord.activityResumedLocked(mActivity.appToken); - assertFalse(mActivity.hasSavedState()); - assertNull(mActivity.getSavedState()); + ActivityRecord.activityResumedLocked(activity.appToken); + assertFalse(activity.hasSavedState()); + assertNull(activity.getSavedState()); } /** Verify the behavior of {@link ActivityRecord#setSavedState(Bundle)}. */ @Test public void testUpdateSavedState() { - mActivity.setSavedState(null /* savedState */); - assertFalse(mActivity.hasSavedState()); - assertNull(mActivity.getSavedState()); + final ActivityRecord activity = createActivityWithTask(); + activity.setSavedState(null /* savedState */); + assertFalse(activity.hasSavedState()); + assertNull(activity.getSavedState()); final Bundle savedState = new Bundle(); savedState.putString("test", "string"); - mActivity.setSavedState(savedState); - assertTrue(mActivity.hasSavedState()); - assertEquals(savedState, mActivity.getSavedState()); + activity.setSavedState(savedState); + assertTrue(activity.hasSavedState()); + assertEquals(savedState, activity.getSavedState()); } /** Verify the correct updates of saved state when activity client reports stop. */ @Test public void testUpdateSavedState_activityStopped() { + final ActivityRecord activity = createActivityWithTask(); final Bundle savedState = new Bundle(); savedState.putString("test", "string"); final PersistableBundle persistentSavedState = new PersistableBundle(); persistentSavedState.putString("persist", "string"); // Set state to STOPPING, or ActivityRecord#activityStoppedLocked() call will be ignored. - mActivity.setState(STOPPING, "test"); - mActivity.activityStopped(savedState, persistentSavedState, "desc"); - assertTrue(mActivity.hasSavedState()); - assertEquals(savedState, mActivity.getSavedState()); - assertEquals(persistentSavedState, mActivity.getPersistentSavedState()); + activity.setState(STOPPING, "test"); + activity.activityStopped(savedState, persistentSavedState, "desc"); + assertTrue(activity.hasSavedState()); + assertEquals(savedState, activity.getSavedState()); + assertEquals(persistentSavedState, activity.getPersistentSavedState()); // Sending 'null' for saved state can only happen due to timeout, so previously stored saved // states should not be overridden. - mActivity.setState(STOPPING, "test"); - mActivity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc"); - assertTrue(mActivity.hasSavedState()); - assertEquals(savedState, mActivity.getSavedState()); - assertEquals(persistentSavedState, mActivity.getPersistentSavedState()); + activity.setState(STOPPING, "test"); + activity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc"); + assertTrue(activity.hasSavedState()); + assertEquals(savedState, activity.getSavedState()); + assertEquals(persistentSavedState, activity.getPersistentSavedState()); } /** @@ -686,19 +714,20 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_cancelled() { + final ActivityRecord activity = createActivityWithTask(); // Mark activity as finishing - mActivity.finishing = true; + activity.finishing = true; assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertTrue(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertTrue(activity.isInStackLocked()); // Remove activity from task - mActivity.finishing = false; - mActivity.onParentChanged(null /*newParent*/, mActivity.getTask()); + activity.finishing = false; + activity.onParentChanged(null /*newParent*/, activity.getTask()); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertFalse(mActivity.finishing); + activity.finishIfPossible("test", false /* oomAdj */)); + assertFalse(activity.finishing); } /** @@ -707,20 +736,21 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_requested() { - mActivity.finishing = false; + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertTrue(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertTrue(activity.isInStackLocked()); // First request to finish activity must schedule a "destroy" request to the client. // Activity must be removed from history after the client reports back or after timeout. - mActivity.finishing = false; - mActivity.setState(STOPPED, "test"); + activity.finishing = false; + activity.setState(STOPPED, "test"); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertTrue(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertTrue(activity.isInStackLocked()); } /** @@ -728,26 +758,28 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_removed() { + final ActivityRecord activity = createActivityWithTask(); // Prepare the activity record to be ready for immediate removal. It should be invisible and // have no process. Otherwise, request to finish it will send a message to client first. - mActivity.setState(STOPPED, "test"); - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; + activity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() - // this will cause NPE when updating task's process. - mActivity.app = null; + activity.app = null; // Put a visible activity on top, so the finishing activity doesn't have to wait until the // next activity reports idle to destroy it. - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.setState(RESUMED, "test"); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertFalse(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertFalse(activity.isInStackLocked()); } /** @@ -756,24 +788,26 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_adjustStackOrder() { - // Prepare the stacks with order (top to bottom): mStack, stack1, stack2. - final Task stack1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); - mStack.moveToFront("test"); - // The stack2 is needed here for moving back to simulate the + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + // Prepare the tasks with order (top to bottom): task, task1, task2. + final Task task1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); + task.moveToFront("test"); + // The task2 is needed here for moving back to simulate the // {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so // {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible - // stacks. Then when mActivity is finishing, its stack will be invisible (no running - // activities in the stack) that is the key condition to verify. - final Task stack2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); - stack2.moveToBack("test", stack2.getBottomMostTask()); + // tasks. Then when mActivity is finishing, its task will be invisible (no running + // activities in the task) that is the key condition to verify. + final Task task2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); + task2.moveToBack("test", task2.getBottomMostTask()); - assertTrue(mStack.isTopStackInDisplayArea()); + assertTrue(task.isTopStackInDisplayArea()); - mActivity.setState(RESUMED, "test"); - mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, + activity.setState(RESUMED, "test"); + activity.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); - assertTrue(stack1.isTopStackInDisplayArea()); + assertTrue(task1.isTopStackInDisplayArea()); } /** @@ -783,22 +817,25 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFinishActivityIfPossible_adjustStackOrderOrganizedRoot() { // Make mStack be a the root task that created by task organizer - mStack.mCreatedByOrganizer = true; + final Task rootableTask = new TaskBuilder(mSupervisor) + .setCreateParentTask(true).setCreateActivity(true).build(); + final Task rootTask = rootableTask.getRootTask(); + rootTask.mCreatedByOrganizer = true; - // Have two tasks (topRootableTask and mTask) as the children of mStack. - ActivityRecord topActivity = new ActivityBuilder(mActivity.mAtmService) + // Have two tasks (topRootableTask and rootableTask) as the children of rootTask. + ActivityRecord topActivity = new ActivityBuilder(mAtm) .setCreateTask(true) - .setParentTask(mStack) + .setParentTask(rootTask) .build(); Task topRootableTask = topActivity.getTask(); topRootableTask.moveToFront("test"); - assertTrue(mStack.isTopStackInDisplayArea()); + assertTrue(rootTask.isTopStackInDisplayArea()); // Finish top activity and verify the next focusable rootable task has adjusted to top. topActivity.setState(RESUMED, "test"); topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); - assertEquals(mTask, mStack.getTopMostTask()); + assertEquals(rootableTask, rootTask.getTopMostTask()); } /** @@ -808,6 +845,8 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_PreferredTopStackChanged() { + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); final ActivityRecord topActivityOnNonTopDisplay = createActivityOnDisplay(true /* defaultDisplay */, null /* process */); Task topRootableTask = topActivityOnNonTopDisplay.getRootTask(); @@ -830,8 +869,8 @@ public class ActivityRecordTests extends WindowTestsBase { topActivityOnNonTopDisplay.setState(RESUMED, "test"); topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); - assertEquals(mTask, mStack.getTopMostTask()); - assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack); + assertEquals(task, task.getTopMostTask()); + assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableStack); } /** @@ -839,15 +878,14 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_resumedStartsPausing() { - mActivity.finishing = false; - mActivity.setState(RESUMED, "test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.setState(RESUMED, "test"); assertEquals("Currently resumed activity must be paused before removal", - FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */)); - assertEquals(PAUSING, mActivity.getState()); - verify(mActivity).setVisibility(eq(false)); - verify(mActivity.mDisplayContent) - .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE), - eq(false) /* alwaysKeepCurrent */); + FINISH_RESULT_REQUESTED, activity.finishIfPossible("test", false /* oomAdj */)); + assertEquals(PAUSING, activity.getState()); + verify(activity).setVisibility(eq(false)); + verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); } /** @@ -855,14 +893,15 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() { + final ActivityRecord activity = createActivityWithTask(); final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED}; for (ActivityState state : states) { - mActivity.finishing = false; - mActivity.setState(state, "test"); - reset(mActivity); + activity.finishing = false; + activity.setState(state, "test"); + reset(activity); assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - verify(mActivity).completeFinishing(anyString()); + activity.finishIfPossible("test", false /* oomAdj */)); + verify(activity).completeFinishing(anyString()); } } @@ -871,11 +910,12 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_pausing() { - mActivity.finishing = false; - mActivity.setState(PAUSING, "test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.setState(PAUSING, "test"); assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - verify(mActivity, never()).completeFinishing(anyString()); + activity.finishIfPossible("test", false /* oomAdj */)); + verify(activity, never()).completeFinishing(anyString()); } /** @@ -884,16 +924,16 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() { - mActivity.finishing = false; - mActivity.mVisibleRequested = true; - mActivity.setState(RESUMED, "test"); - mActivity.finishIfPossible("test", false /* oomAdj */); + final ActivityRecord activity = createActivityWithTask(); + clearInvocations(activity.mDisplayContent); + activity.finishing = false; + activity.mVisibleRequested = true; + activity.setState(RESUMED, "test"); + activity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity).setVisibility(eq(false)); - verify(mActivity.mDisplayContent) - .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE), - eq(false) /* alwaysKeepCurrent */); - verify(mActivity.mDisplayContent, never()).executeAppTransition(); + verify(activity).setVisibility(eq(false)); + verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); + verify(activity.mDisplayContent, never()).executeAppTransition(); } /** @@ -901,16 +941,16 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() { - mActivity.finishing = false; - mActivity.mVisibleRequested = true; - mActivity.setState(PAUSED, "test"); - mActivity.finishIfPossible("test", false /* oomAdj */); + final ActivityRecord activity = createActivityWithTask(); + clearInvocations(activity.mDisplayContent); + activity.finishing = false; + activity.mVisibleRequested = true; + activity.setState(PAUSED, "test"); + activity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity, atLeast(1)).setVisibility(eq(false)); - verify(mActivity.mDisplayContent) - .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE), - eq(false) /* alwaysKeepCurrent */); - verify(mActivity.mDisplayContent).executeAppTransition(); + verify(activity, atLeast(1)).setVisibility(eq(false)); + verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); + verify(activity.mDisplayContent).executeAppTransition(); } /** @@ -918,17 +958,16 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_nonVisibleNoAppTransition() { + final ActivityRecord activity = createActivityWithTask(); // Put an activity on top of test activity to make it invisible and prevent us from // accidentally resuming the topmost one again. new ActivityBuilder(mAtm).build(); - mActivity.mVisibleRequested = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.setState(STOPPED, "test"); - mActivity.finishIfPossible("test", false /* oomAdj */); + activity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity.mDisplayContent, never()) - .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE), - eq(false) /* alwaysKeepCurrent */); + verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE)); } /** @@ -936,8 +975,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test(expected = IllegalArgumentException.class) public void testCompleteFinishing_failNotFinishing() { - mActivity.finishing = false; - mActivity.completeFinishing("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.completeFinishing("test"); } /** @@ -945,8 +985,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test(expected = IllegalArgumentException.class) public void testCompleteFinishing_failResumed() { - mActivity.setState(RESUMED, "test"); - mActivity.completeFinishing("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(RESUMED, "test"); + activity.completeFinishing("test"); } /** @@ -955,13 +996,14 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_pausing() { - mActivity.setState(PAUSING, "test"); - mActivity.finishing = true; + final ActivityRecord activity = createActivityWithTask(); + activity.setState(PAUSING, "test"); + activity.finishing = true; assertEquals("Activity must not be removed immediately - waiting for paused", - mActivity, mActivity.completeFinishing("test")); - assertEquals(PAUSING, mActivity.getState()); - verify(mActivity, never()).destroyIfPossible(anyString()); + activity, activity.completeFinishing("test")); + assertEquals(PAUSING, activity.getState()); + verify(activity, never()).destroyIfPossible(anyString()); } /** @@ -973,7 +1015,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_keepStateOfNextInvisible() { - final ActivityRecord currentTop = mActivity; + final ActivityRecord currentTop = createActivityWithTask(); + final Task task = currentTop.getTask(); + currentTop.mVisibleRequested = currentTop.nowVisible = true; // Simulates that {@code currentTop} starts an existing activity from background (so its @@ -982,7 +1026,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity(); nextTop.setState(STOPPED, "test"); - mStack.mPausingActivity = currentTop; + task.mPausingActivity = currentTop; currentTop.finishing = true; currentTop.setState(PAUSED, "test"); currentTop.completeFinishing("completePauseLocked"); @@ -999,16 +1043,18 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_waitForNextVisible() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as not visible, so that we will wait for it before removing // the top one. - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; + activity.setState(STOPPED, "test"); assertEquals("Activity must not be removed immediately - waiting for next visible", topActivity, topActivity.completeFinishing("test")); @@ -1025,23 +1071,24 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_sleeping() { + final ActivityRecord activity = createActivityWithTask(); // Create a top activity on a new task - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord topActivity = createActivityWithTask(); mDisplayContent.setIsSleeping(true); - doReturn(true).when(mActivity).shouldBeVisible(); + doReturn(true).when(activity).shouldBeVisible(); topActivity.mVisibleRequested = false; topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the activity behind (on a separate task) as not visible - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; + activity.setState(STOPPED, "test"); - clearInvocations(mActivity); + clearInvocations(activity); topActivity.completeFinishing("test"); - verify(mActivity).setState(eq(RESUMED), any()); + verify(activity).setState(eq(RESUMED), any()); verify(topActivity).destroyIfPossible(anyString()); } @@ -1050,16 +1097,18 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = false; topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the bottom activity as not visible, so that we would wait for it before removing // the top one. - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; + activity.setState(STOPPED, "test"); topActivity.completeFinishing("test"); @@ -1072,15 +1121,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_waitForIdle() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - mActivity.mVisibleRequested = true; - mActivity.nowVisible = true; - mActivity.setState(RESUMED, "test"); + activity.mVisibleRequested = true; + activity.nowVisible = true; + activity.setState(RESUMED, "test"); topActivity.completeFinishing("test"); @@ -1093,15 +1144,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_stopped() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = false; topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - mActivity.mVisibleRequested = true; - mActivity.nowVisible = true; - mActivity.setState(RESUMED, "test"); + activity.mVisibleRequested = true; + activity.nowVisible = true; + activity.setState(RESUMED, "test"); topActivity.completeFinishing("test"); @@ -1114,15 +1167,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - mActivity.mVisibleRequested = true; - mActivity.nowVisible = true; - mActivity.setState(RESUMED, "test"); + activity.mVisibleRequested = true; + activity.nowVisible = true; + activity.setState(RESUMED, "test"); // Add another stack to become focused and make the activity there visible. This way it // simulates finishing in non-focused stack in split-screen. @@ -1144,10 +1199,12 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_showWhenLocked() { + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); // Make keyguard locked and set the top activity show-when-locked. - KeyguardController keyguardController = mActivity.mTaskSupervisor.getKeyguardController(); + KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController(); doReturn(true).when(keyguardController).isKeyguardLocked(); - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.setState(RESUMED, "true"); @@ -1158,7 +1215,7 @@ public class ActivityRecordTests extends WindowTestsBase { topActivity.setShowWhenLocked(true); // Verify the stack-top activity is occluded keyguard. - assertEquals(topActivity, mStack.topRunningActivity()); + assertEquals(topActivity, task.topRunningActivity()); assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); // Finish the top activity @@ -1167,7 +1224,7 @@ public class ActivityRecordTests extends WindowTestsBase { topActivity.completeFinishing("test"); // Verify new top activity does not occlude keyguard. - assertEquals(mActivity, mStack.topRunningActivity()); + assertEquals(activity, task.topRunningActivity()); assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); } @@ -1177,18 +1234,19 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_ensureActivitiesVisible() { - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); firstActivity.mVisibleRequested = false; firstActivity.nowVisible = false; firstActivity.setState(STOPPED, "true"); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); secondActivity.mVisibleRequested = true; secondActivity.nowVisible = true; secondActivity.setState(PAUSED, "true"); - final ActivityRecord translucentActivity = - new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).setTask(task).build(); translucentActivity.mVisibleRequested = true; translucentActivity.nowVisible = true; translucentActivity.setState(RESUMED, "true"); @@ -1216,13 +1274,13 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyIfPossible() { + final ActivityRecord activity = createActivityWithTask(); doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); - spyOn(mStack); - mActivity.destroyIfPossible("test"); + activity.destroyIfPossible("test"); - assertEquals(DESTROYING, mActivity.getState()); - assertTrue(mActivity.finishing); - verify(mActivity).destroyImmediately(anyString()); + assertEquals(DESTROYING, activity.getState()); + assertTrue(activity.finishing); + verify(activity).destroyImmediately(anyString()); } /** @@ -1232,23 +1290,23 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() { + final ActivityRecord activity = createActivityWithTask(); // Empty the home stack. - final Task homeStack = mActivity.getDisplayArea().getRootHomeTask(); + final Task homeStack = activity.getDisplayArea().getRootHomeTask(); homeStack.forAllLeafTasks((t) -> { homeStack.removeChild(t, "test"); }, true /* traverseTopToBottom */); - mActivity.finishing = true; + activity.finishing = true; doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); - spyOn(mStack); // Try to destroy the last activity above the home stack. - mActivity.destroyIfPossible("test"); + activity.destroyIfPossible("test"); // Verify that the activity was not actually destroyed, but waits for next one to come up // instead. - verify(mActivity, never()).destroyImmediately(anyString()); - assertEquals(FINISHING, mActivity.getState()); - assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity)); + verify(activity, never()).destroyImmediately(anyString()); + assertEquals(FINISHING, activity.getState()); + assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity)); } /** @@ -1258,22 +1316,23 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() { + final ActivityRecord activity = createActivityWithTask(); // Empty the home root task. - final Task homeRootTask = mActivity.getDisplayArea().getRootHomeTask(); + final Task homeRootTask = activity.getDisplayArea().getRootHomeTask(); homeRootTask.forAllLeafTasks((t) -> { homeRootTask.removeChild(t, "test"); }, true /* traverseTopToBottom */); - mActivity.finishing = true; - mActivity.mVisibleRequested = true; - spyOn(mStack); + activity.setState(STARTED, "test"); + activity.finishing = true; + activity.mVisibleRequested = true; // Try to finish the last activity above the home stack. - mActivity.completeFinishing("test"); + activity.completeFinishing("test"); // Verify that the activity is not destroyed immediately, but waits for next one to come up. - verify(mActivity, never()).destroyImmediately(anyString()); - assertEquals(FINISHING, mActivity.getState()); - assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity)); + verify(activity, never()).destroyImmediately(anyString()); + assertEquals(FINISHING, activity.getState()); + assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity)); } /** @@ -1282,10 +1341,11 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_hadApp_finishing() { - mActivity.finishing = true; - mActivity.destroyImmediately("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = true; + activity.destroyImmediately("test"); - assertEquals(DESTROYING, mActivity.getState()); + assertEquals(DESTROYING, activity.getState()); } /** @@ -1294,10 +1354,11 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_hadApp_notFinishing() { - mActivity.finishing = false; - mActivity.destroyImmediately("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.destroyImmediately("test"); - assertEquals(DESTROYED, mActivity.getState()); + assertEquals(DESTROYED, activity.getState()); } /** @@ -1306,14 +1367,15 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_noApp_finishing() { - mActivity.app = null; - mActivity.finishing = true; - final Task task = mActivity.getTask(); + final ActivityRecord activity = createActivityWithTask(); + activity.app = null; + activity.finishing = true; + final Task task = activity.getTask(); - mActivity.destroyImmediately("test"); + activity.destroyImmediately("test"); - assertEquals(DESTROYED, mActivity.getState()); - assertNull(mActivity.getTask()); + assertEquals(DESTROYED, activity.getState()); + assertNull(activity.getTask()); assertEquals(0, task.getChildCount()); } @@ -1323,14 +1385,15 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_noApp_notFinishing() { - mActivity.app = null; - mActivity.finishing = false; - final Task task = mActivity.getTask(); + final ActivityRecord activity = createActivityWithTask(); + activity.app = null; + activity.finishing = false; + final Task task = activity.getTask(); - mActivity.destroyImmediately("test"); + activity.destroyImmediately("test"); - assertEquals(DESTROYED, mActivity.getState()); - assertEquals(task, mActivity.getTask()); + assertEquals(DESTROYED, activity.getState()); + assertEquals(task, activity.getTask()); assertEquals(1, task.getChildCount()); } @@ -1339,11 +1402,12 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testSafelyDestroy_nonDestroyable() { - doReturn(false).when(mActivity).isDestroyable(); + final ActivityRecord activity = createActivityWithTask(); + doReturn(false).when(activity).isDestroyable(); - mActivity.safelyDestroy("test"); + activity.safelyDestroy("test"); - verify(mActivity, never()).destroyImmediately(anyString()); + verify(activity, never()).destroyImmediately(anyString()); } /** @@ -1351,29 +1415,31 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testSafelyDestroy_destroyable() { - doReturn(true).when(mActivity).isDestroyable(); + final ActivityRecord activity = createActivityWithTask(); + doReturn(true).when(activity).isDestroyable(); - mActivity.safelyDestroy("test"); + activity.safelyDestroy("test"); - verify(mActivity).destroyImmediately(anyString()); + verify(activity).destroyImmediately(anyString()); } @Test public void testRemoveFromHistory() { - final Task stack = mActivity.getRootTask(); - final Task task = mActivity.getTask(); - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final Task rootTask = activity.getRootTask(); + final Task task = activity.getTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.hasActivities()); - mActivity.removeFromHistory("test"); + activity.removeFromHistory("test"); - assertEquals(DESTROYED, mActivity.getState()); - assertNull(mActivity.app); - assertNull(mActivity.getTask()); + assertEquals(DESTROYED, activity.getState()); + assertNull(activity.app); + assertNull(activity.getTask()); assertFalse(wpc.hasActivities()); assertEquals(0, task.getChildCount()); assertEquals(task.getRootTask(), task); - assertEquals(0, stack.getChildCount()); + assertEquals(0, rootTask.getChildCount()); } /** @@ -1382,8 +1448,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test(expected = IllegalStateException.class) public void testDestroyed_notDestroying() { - mActivity.setState(STOPPED, "test"); - mActivity.destroyed("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(STOPPED, "test"); + activity.destroyed("test"); } /** @@ -1391,10 +1458,11 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyed_destroying() { - mActivity.setState(DESTROYING, "test"); - mActivity.destroyed("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(DESTROYING, "test"); + activity.destroyed("test"); - verify(mActivity).removeFromHistory(anyString()); + verify(activity).removeFromHistory(anyString()); } /** @@ -1402,15 +1470,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyed_destroyed() { - mActivity.setState(DESTROYED, "test"); - mActivity.destroyed("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(DESTROYED, "test"); + activity.destroyed("test"); - verify(mActivity).removeFromHistory(anyString()); + verify(activity).removeFromHistory(anyString()); } @Test public void testActivityOverridesProcessConfig() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); assertFalse(wpc.registeredForDisplayConfigChanges()); @@ -1418,18 +1488,19 @@ public class ActivityRecordTests extends WindowTestsBase { createActivityOnDisplay(false /* defaultDisplay */, null /* process */); assertTrue(wpc.registeredForActivityConfigChanges()); - assertEquals(0, mActivity.getMergedOverrideConfiguration() + assertEquals(0, activity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); - assertNotEquals(mActivity.getConfiguration(), + assertNotEquals(activity.getConfiguration(), secondaryDisplayActivity.getConfiguration()); } @Test public void testActivityOverridesProcessConfig_TwoActivities() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); - final Task firstTaskRecord = mActivity.getTask(); + final Task firstTaskRecord = activity.getTask(); final ActivityRecord secondActivityRecord = new ActivityBuilder(mAtm).setTask(firstTaskRecord).setUseProcess(wpc).build(); @@ -1440,11 +1511,12 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); final ActivityRecord secondActivityRecord = - new ActivityBuilder(mAtm).setTask(mTask).setUseProcess(wpc).build(); + new ActivityBuilder(mAtm).setTask(activity.getTask()).setUseProcess(wpc).build(); assertTrue(wpc.registeredForActivityConfigChanges()); assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration() @@ -1453,7 +1525,8 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); final ActivityRecord secondActivityRecord = @@ -1466,10 +1539,11 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityOnCancelFixedRotationTransform() { - final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation(); + final ActivityRecord activity = createActivityWithTask(); + final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation(); spyOn(displayRotation); - final DisplayContent display = mActivity.mDisplayContent; + final DisplayContent display = activity.mDisplayContent; final int originalRotation = display.getRotation(); // Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately. @@ -1477,17 +1551,17 @@ public class ActivityRecordTests extends WindowTestsBase { doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation( anyInt() /* orientation */, anyInt() /* lastRotation */); // Set to visible so the activity can freeze the screen. - mActivity.setVisibility(true); + activity.setVisibility(true); - display.rotateInDifferentOrientationIfNeeded(mActivity); - display.setFixedRotationLaunchingAppUnchecked(mActivity); + display.rotateInDifferentOrientationIfNeeded(activity); + display.setFixedRotationLaunchingAppUnchecked(activity); displayRotation.updateRotationUnchecked(true /* forceUpdate */); assertTrue(displayRotation.isRotatingSeamlessly()); // The launching rotated app should not be cleared when waiting for remote rotation. display.continueUpdateOrientationForDiffOrienLaunchingApp(); - assertTrue(display.isFixedRotationLaunchingApp(mActivity)); + assertTrue(display.isFixedRotationLaunchingApp(activity)); // Simulate the rotation has been updated to previous one, e.g. sensor updates before the // remote rotation is completed. @@ -1495,16 +1569,16 @@ public class ActivityRecordTests extends WindowTestsBase { anyInt() /* orientation */, anyInt() /* lastRotation */); display.updateOrientation(); - final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo(); - mActivity.finishFixedRotationTransform(); + final DisplayInfo rotatedInfo = activity.getFixedRotationTransformDisplayInfo(); + activity.finishFixedRotationTransform(); final ScreenRotationAnimation rotationAnim = display.getRotationAnimation(); assertNotNull(rotationAnim); rotationAnim.setRotation(display.getPendingTransaction(), originalRotation); // Because the display doesn't rotate, the rotated activity needs to cancel the fixed // rotation. There should be a rotation animation to cover the change of activity. - verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation); - assertTrue(mActivity.isFreezingScreen()); + verify(activity).onCancelFixedRotationTransform(rotatedInfo.rotation); + assertTrue(activity.isFreezingScreen()); assertFalse(displayRotation.isRotatingSeamlessly()); assertTrue(rotationAnim.isRotating()); @@ -1512,44 +1586,46 @@ public class ActivityRecordTests extends WindowTestsBase { // the rotated activity should also be restored by clearing the transform. displayRotation.updateRotationUnchecked(true /* forceUpdate */); doReturn(false).when(displayRotation).isWaitingForRemoteRotation(); - clearInvocations(mActivity); - display.setFixedRotationLaunchingAppUnchecked(mActivity); + clearInvocations(activity); + display.setFixedRotationLaunchingAppUnchecked(activity); display.sendNewConfiguration(); assertFalse(display.hasTopFixedRotationLaunchingApp()); - assertFalse(mActivity.hasFixedRotationTransform()); + assertFalse(activity.hasFixedRotationTransform()); } @Test public void testIsSnapshotCompatible() { + final ActivityRecord activity = createActivityWithTask(); final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() - .setRotation(mActivity.getWindowConfiguration().getRotation()) + .setRotation(activity.getWindowConfiguration().getRotation()) .build(); - assertTrue(mActivity.isSnapshotCompatible(snapshot)); + assertTrue(activity.isSnapshotCompatible(snapshot)); - setRotatedScreenOrientationSilently(mActivity); + setRotatedScreenOrientationSilently(activity); - assertFalse(mActivity.isSnapshotCompatible(snapshot)); + assertFalse(activity.isSnapshotCompatible(snapshot)); } @Test public void testFixedRotationSnapshotStartingWindow() { + final ActivityRecord activity = createActivityWithTask(); // TaskSnapshotSurface requires a fullscreen opaque window. final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT; final TestWindowState w = new TestWindowState( - mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity); - mActivity.addWindow(w); + mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, activity); + activity.addWindow(w); // Assume the activity is launching in different rotation, and there was an available // snapshot accepted by {@link Activity#isSnapshotCompatible}. final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() - .setRotation((mActivity.getWindowConfiguration().getRotation() + 1) % 4) + .setRotation((activity.getWindowConfiguration().getRotation() + 1) % 4) .build(); - setRotatedScreenOrientationSilently(mActivity); - mActivity.setVisible(false); + setRotatedScreenOrientationSilently(activity); + activity.setVisible(false); final IWindowSession session = WindowManagerGlobal.getWindowSession(); spyOn(session); @@ -1561,7 +1637,7 @@ public class ActivityRecordTests extends WindowTestsBase { any() /* requestedVisibility */, any() /* outFrame */, any() /* outDisplayCutout */, any() /* outInputChannel */, any() /* outInsetsState */, any() /* outActiveControls */); - TaskSnapshotSurface.create(mAtm.mWindowManager, mActivity, snapshot); + TaskSnapshotSurface.create(mAtm.mWindowManager, activity, snapshot); } catch (RemoteException ignored) { } finally { reset(session); @@ -1570,8 +1646,8 @@ public class ActivityRecordTests extends WindowTestsBase { // Because the rotation of snapshot and the corresponding top activity are different, fixed // rotation should be applied when creating snapshot surface if the display rotation may be // changed according to the activity orientation. - assertTrue(mActivity.hasFixedRotationTransform()); - assertTrue(mActivity.mDisplayContent.isFixedRotationLaunchingApp(mActivity)); + assertTrue(activity.hasFixedRotationTransform()); + assertTrue(activity.mDisplayContent.isFixedRotationLaunchingApp(activity)); } /** @@ -1604,11 +1680,12 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityReparentChangesProcessOverride() { - final WindowProcessController wpc = mActivity.app; - final Task initialTask = mActivity.getTask(); + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; + final Task initialTask = activity.getTask(); final Configuration initialConf = - new Configuration(mActivity.getMergedOverrideConfiguration()); - assertEquals(0, mActivity.getMergedOverrideConfiguration() + new Configuration(activity.getMergedOverrideConfiguration()); + assertEquals(0, activity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); assertTrue(wpc.registeredForActivityConfigChanges()); @@ -1621,22 +1698,23 @@ public class ActivityRecordTests extends WindowTestsBase { assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi); // Reparent the activity and verify that config override changed. - mActivity.reparent(newTask, 0 /* top */, "test"); - assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi); - assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi); + activity.reparent(newTask, 0 /* top */, "test"); + assertEquals(activity.getConfiguration().densityDpi, newConfig.densityDpi); + assertEquals(activity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi); assertTrue(wpc.registeredForActivityConfigChanges()); assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); - assertEquals(0, mActivity.getMergedOverrideConfiguration() + assertEquals(0, activity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); } @Test public void testActivityReparentDoesntClearProcessOverride_TwoActivities() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; final Configuration initialConf = - new Configuration(mActivity.getMergedOverrideConfiguration()); - final Task initialTask = mActivity.getTask(); + new Configuration(activity.getMergedOverrideConfiguration()); + final Task initialTask = activity.getTask(); final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(initialTask) .setUseProcess(wpc).build(); @@ -1660,7 +1738,7 @@ public class ActivityRecordTests extends WindowTestsBase { assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); // Reparent the first activity and verify that config override didn't change. - mActivity.reparent(newTask, 1 /* top */, "test"); + activity.reparent(newTask, 1 /* top */, "test"); assertTrue(wpc.registeredForActivityConfigChanges()); assertEquals(0, secondActivity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); @@ -1703,67 +1781,90 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFullscreenWindowCanTurnScreenOn() { - mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - doReturn(true).when(mActivity).getTurnScreenOnFlag(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + doReturn(true).when(activity).getTurnScreenOnFlag(); - assertTrue(mActivity.canTurnScreenOn()); + assertTrue(activity.canTurnScreenOn()); } @Test public void testFreeformWindowCanTurnScreenOn() { - mStack.setWindowingMode(WINDOWING_MODE_FREEFORM); - doReturn(true).when(mActivity).getTurnScreenOnFlag(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FREEFORM); + doReturn(true).when(activity).getTurnScreenOnFlag(); - assertTrue(mActivity.canTurnScreenOn()); + assertTrue(activity.canTurnScreenOn()); } @Test public void testGetLockTaskLaunchMode() { + final ActivityRecord activity = createActivityWithTask(); final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, options)); + ActivityRecord.getLockTaskLaunchMode(activity.info, options)); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); - mActivity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; + activity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; assertEquals(LOCK_TASK_LAUNCH_MODE_ALWAYS, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; assertEquals(LOCK_TASK_LAUNCH_MODE_NEVER, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); } @Test public void testProcessInfoUpdateWhenSetState() { - spyOn(mActivity.app); - verifyProcessInfoUpdate(RESUMED, true /* shouldUpdate */, true /* activityChange */); - verifyProcessInfoUpdate(PAUSED, false /* shouldUpdate */, false /* activityChange */); - verifyProcessInfoUpdate(STOPPED, false /* shouldUpdate */, false /* activityChange */); - verifyProcessInfoUpdate(STARTED, true /* shouldUpdate */, true /* activityChange */); - - mActivity.app.removeActivity(mActivity, true /* keepAssociation */); - verifyProcessInfoUpdate(DESTROYING, true /* shouldUpdate */, false /* activityChange */); - verifyProcessInfoUpdate(DESTROYED, true /* shouldUpdate */, false /* activityChange */); - } - - private void verifyProcessInfoUpdate(ActivityState state, boolean shouldUpdate, - boolean activityChange) { - reset(mActivity.app); - mActivity.setState(state, "test"); - verify(mActivity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(), + final ActivityRecord activity = createActivityWithTask(); + activity.setState(INITIALIZING, "test"); + spyOn(activity.app); + verifyProcessInfoUpdate(activity, RESUMED, + true /* shouldUpdate */, true /* activityChange */); + verifyProcessInfoUpdate(activity, PAUSED, + false /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(activity, STOPPED, + false /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(activity, STARTED, + true /* shouldUpdate */, true /* activityChange */); + + activity.app.removeActivity(activity, true /* keepAssociation */); + verifyProcessInfoUpdate(activity, DESTROYING, + true /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(activity, DESTROYED, + true /* shouldUpdate */, false /* activityChange */); + } + + private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state, + boolean shouldUpdate, boolean activityChange) { + reset(activity.app); + activity.setState(state, "test"); + verify(activity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(), eq(activityChange), anyBoolean(), anyBoolean()); } + private ActivityRecord createActivityWithTask() { + return new ActivityBuilder(mAtm).setCreateTask(true).setOnTop(true).build(); + } + + private ActivityRecord createActivityWith2LevelTask() { + final Task task = new TaskBuilder(mSupervisor) + .setCreateParentTask(true).setCreateActivity(true).build(); + return task.getTopNonFinishingActivity(); + } + /** * Creates an activity on display. For non-default display request it will also create a new * display with custom DisplayInfo. @@ -1777,9 +1878,7 @@ public class ActivityRecordTests extends WindowTestsBase { display = new TestDisplayContent.Builder(mAtm, 2000, 1000).setDensityDpi(300) .setPosition(DisplayContent.POSITION_TOP).build(); } - final Task stack = display.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); return new ActivityBuilder(mAtm).setTask(task).setUseProcess(process).build(); } } 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 d872eb856a50..8ccbb8fe62ad 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -43,6 +43,7 @@ import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; +import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; @@ -91,59 +92,58 @@ import java.util.function.Consumer; @RunWith(WindowTestRunner.class) public class ActivityStackTests extends WindowTestsBase { private TaskDisplayArea mDefaultTaskDisplayArea; - private Task mStack; - private Task mTask; @Before public void setUp() throws Exception { mDefaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - spyOn(mStack); - mTask = new TaskBuilder(mSupervisor).setParentTask(mStack).build(); } @Test public void testResumedActivity() { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); - assertNull(mStack.getResumedActivity()); + final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = r.getTask(); + assertNull(task.getResumedActivity()); r.setState(RESUMED, "testResumedActivity"); - assertEquals(r, mStack.getResumedActivity()); + assertEquals(r, task.getResumedActivity()); r.setState(PAUSING, "testResumedActivity"); - assertNull(mStack.getResumedActivity()); + assertNull(task.getResumedActivity()); } @Test public void testResumedActivityFromTaskReparenting() { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + final ActivityRecord r = new ActivityBuilder(mAtm) + .setCreateTask(true).setParentTask(parentTask).build(); + final Task task = r.getTask(); // Ensure moving task between two stacks updates resumed activity r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); - assertEquals(r, mStack.getResumedActivity()); + assertEquals(r, parentTask.getResumedActivity()); - final Task destStack = mDefaultTaskDisplayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - - mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT, + final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build(); + task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT, false /* animate */, true /* deferResume*/, "testResumedActivityFromTaskReparenting"); - assertNull(mStack.getResumedActivity()); + assertNull(parentTask.getResumedActivity()); assertEquals(r, destStack.getResumedActivity()); } @Test public void testResumedActivityFromActivityReparenting() { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + final ActivityRecord r = new ActivityBuilder(mAtm) + .setCreateTask(true).setParentTask(parentTask).build(); + final Task task = r.getTask(); // Ensure moving task between two stacks updates resumed activity r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); - assertEquals(r, mStack.getResumedActivity()); + assertEquals(r, parentTask.getResumedActivity()); - final Task destStack = mDefaultTaskDisplayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false, false, + final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build(); + task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, + false /* animate */, false /* deferResume*/, "testResumedActivityFromActivityReparenting"); - assertNull(mStack.getResumedActivity()); + assertNull(parentTask.getResumedActivity()); assertEquals(r, destStack.getResumedActivity()); } @@ -292,7 +292,7 @@ public class ActivityStackTests extends WindowTestsBase { public void testStopActivityWhenActivityDestroyed() { final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); r.info.flags |= ActivityInfo.FLAG_NO_HISTORY; - mStack.moveToFront("testStopActivityWithDestroy"); + r.getTask().moveToFront("testStopActivityWithDestroy"); r.stopIfPossible(); // Mostly testing to make sure there is a crash in the call part, so if we get here we are // good-to-go! @@ -327,7 +327,8 @@ public class ActivityStackTests extends WindowTestsBase { targetActivity); final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity); - final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build(); + final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build(); + final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build(); task.origActivity = alias; task.realActivity = target; new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity( @@ -337,14 +338,14 @@ public class ActivityStackTests extends WindowTestsBase { final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent( target).setTargetActivity(targetActivity).build(); RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.process(r1, mStack); + result.process(r1, parentTask); assertThat(result.mRecord).isNotNull(); // Using alias activity to find task. final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent( alias).setTargetActivity(targetActivity).build(); result = new RootWindowContainer.FindTaskResult(); - result.process(r2, mStack); + result.process(r2, parentTask); assertThat(result.mRecord).isNotNull(); } @@ -735,8 +736,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack = createStackForShouldBeVisibleTest( @@ -755,8 +754,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack = createStackForShouldBeVisibleTest( @@ -775,8 +772,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task fullscreenStack = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -795,8 +790,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack1 = createStackForShouldBeVisibleTest( @@ -822,8 +815,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack1 = createStackForShouldBeVisibleTest( @@ -846,8 +837,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindStack_BehindHomeStack() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -869,8 +858,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindStack() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -1019,11 +1006,12 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testFinishDisabledPackageActivities_FinishAliveActivities() { - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); firstActivity.setState(STOPPED, "testFinishDisabledPackageActivities"); secondActivity.setState(RESUMED, "testFinishDisabledPackageActivities"); - mStack.mResumedActivity = secondActivity; + task.mResumedActivity = secondActivity; // Note the activities have non-null ActivityRecord.app, so it won't remove directly. mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process( @@ -1039,10 +1027,11 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testFinishDisabledPackageActivities_RemoveNonAliveActivities() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); // The overlay activity is not in the disabled package but it is in the same task. - final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(mTask) + final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task) .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build(); // If the task only remains overlay activity, the task should also be removed. // See {@link ActivityStack#removeFromHistory}. @@ -1053,7 +1042,7 @@ public class ActivityStackTests extends WindowTestsBase { activity.app = null; overlayActivity.app = null; - assertEquals(2, mTask.getChildCount()); + assertEquals(2, task.getChildCount()); mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process( activity.packageName, null /* filterByClasses */, true /* doit */, @@ -1062,14 +1051,14 @@ public class ActivityStackTests extends WindowTestsBase { // Although the overlay activity is in another package, the non-overlay activities are // removed from the task. Since the overlay activity should be removed as well, the task // should be empty. - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testHandleAppDied() { - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); // Making the first activity a task overlay means it will be removed from the task's // activities as well once second activity is removed as handleAppDied processes the @@ -1080,17 +1069,17 @@ public class ActivityStackTests extends WindowTestsBase { // second activity will be immediately removed as it has no state. secondActivity.setSavedState(null /* savedState */); - assertEquals(2, mTask.getChildCount()); + assertEquals(2, task.getChildCount()); secondActivity.app.handleAppDied(); - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; activity.launchCount = 1; @@ -1098,13 +1087,13 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertEquals(1, mTask.getChildCount()); - assertEquals(1, mStack.getChildCount()); + assertEquals(1, task.getChildCount()); } @Test public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; activity.launchCount = 3; @@ -1112,13 +1101,13 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; activity.launchCount = 1; @@ -1126,13 +1115,13 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertEquals(1, mTask.getChildCount()); - assertEquals(1, mStack.getChildCount()); + assertEquals(1, task.getChildCount()); } @Test public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; activity.launchCount = 3; @@ -1140,22 +1129,22 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testCompletePauseOnResumeWhilePausingActivity() { - final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build(); doReturn(true).when(bottomActivity).attachedToProcess(); - mStack.mPausingActivity = null; - mStack.mResumedActivity = bottomActivity; - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + task.mPausingActivity = null; + task.mResumedActivity = bottomActivity; + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING; - mStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity, + task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity, "test"); - verify(mStack).completePauseLocked(anyBoolean(), eq(topActivity)); + verify(task).completePauseLocked(anyBoolean(), eq(topActivity)); } @Test @@ -1234,10 +1223,11 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOrderChangedOnRemoveStack() { + final Task task = new TaskBuilder(mSupervisor).build(); StackOrderChangedListener listener = new StackOrderChangedListener(); mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); try { - mDefaultTaskDisplayArea.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(task); } finally { mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } @@ -1246,13 +1236,14 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOrderChangedOnAddPositionStack() { - mDefaultTaskDisplayArea.removeStack(mStack); + final Task task = new TaskBuilder(mSupervisor).build(); + mDefaultTaskDisplayArea.removeStack(task); StackOrderChangedListener listener = new StackOrderChangedListener(); mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); try { - mStack.mReparenting = true; - mDefaultTaskDisplayArea.addChild(mStack, 0); + task.mReparenting = true; + mDefaultTaskDisplayArea.addChild(task, 0); } finally { mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } @@ -1284,20 +1275,21 @@ public class ActivityStackTests extends WindowTestsBase { spyOn(starter); doReturn(ActivityManager.START_SUCCESS).when(starter).execute(); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask) + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task) .setUid(firstActivity.getUid() + 1).build(); doReturn(starter).when(controller).obtainStarter(eq(firstActivity.intent), anyString()); final IApplicationThread thread = secondActivity.app.getThread(); secondActivity.app.setThread(null); // This should do nothing from a non-attached caller. - assertFalse(mStack.navigateUpTo(secondActivity /* source record */, + assertFalse(task.navigateUpTo(secondActivity /* source record */, firstActivity.intent /* destIntent */, null /* destGrants */, 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); secondActivity.app.setThread(thread); - assertTrue(mStack.navigateUpTo(secondActivity /* source record */, + assertTrue(task.navigateUpTo(secondActivity /* source record */, firstActivity.intent /* destIntent */, null /* destGrants */, 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); // The firstActivity uses default launch mode, so the activities between it and itself will @@ -1313,9 +1305,10 @@ public class ActivityStackTests extends WindowTestsBase { final String affinity = "affinity"; final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity) .setUid(Binder.getCallingUid()).setCreateTask(true).build(); - activity.getTask().affinity = activity.taskAffinity; + final Task task = activity.getTask(); + task.affinity = activity.taskAffinity; - assertFalse(mStack.shouldUpRecreateTaskLocked(activity, affinity)); + assertFalse(task.shouldUpRecreateTaskLocked(activity, affinity)); } @Test @@ -1323,21 +1316,23 @@ public class ActivityStackTests extends WindowTestsBase { final String affinity = "affinity"; final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity) .setUid(Binder.getCallingUid()).setCreateTask(true).build(); - activity.getTask().affinity = activity.taskAffinity; + final Task task = activity.getTask(); + task.affinity = activity.taskAffinity; final String fakeAffinity = activity.getUid() + activity.taskAffinity; - assertTrue(mStack.shouldUpRecreateTaskLocked(activity, fakeAffinity)); + assertTrue(task.shouldUpRecreateTaskLocked(activity, fakeAffinity)); } @Test public void testResetTaskWithFinishingActivities() { - final ActivityRecord taskTop = new ActivityBuilder(mAtm).setTask(mStack).build(); + final ActivityRecord taskTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = taskTop.getTask(); // Make all activities in the task are finishing to simulate Task#getTopActivity // returns null. taskTop.finishing = true; final ActivityRecord newR = new ActivityBuilder(mAtm).build(); - final ActivityRecord result = mStack.resetTaskIfNeeded(taskTop, newR); + final ActivityRecord result = task.resetTaskIfNeeded(taskTop, newR); assertThat(result).isEqualTo(taskTop); } @@ -1345,14 +1340,15 @@ public class ActivityStackTests extends WindowTestsBase { public void testIterateOccludedActivity() { final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>(); final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add; - final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); // Top activity occludes bottom activity. - doReturn(true).when(mStack).shouldBeVisible(any()); + doReturn(true).when(task).shouldBeVisible(any()); assertTrue(topActivity.shouldBeVisible()); assertFalse(bottomActivity.shouldBeVisible()); - mStack.forAllOccludedActivities(handleOccludedActivity); + task.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).containsExactly(bottomActivity); // Top activity doesn't occlude parent, so the bottom activity is not occluded. @@ -1360,18 +1356,18 @@ public class ActivityStackTests extends WindowTestsBase { assertTrue(bottomActivity.shouldBeVisible()); occludedActivities.clear(); - mStack.forAllOccludedActivities(handleOccludedActivity); + task.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).isEmpty(); // A finishing activity should not occlude other activities behind. - final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build(); finishingActivity.finishing = true; doCallRealMethod().when(finishingActivity).occludesParent(); assertTrue(topActivity.shouldBeVisible()); assertTrue(bottomActivity.shouldBeVisible()); occludedActivities.clear(); - mStack.forAllOccludedActivities(handleOccludedActivity); + task.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).isEmpty(); } @@ -1385,8 +1381,9 @@ public class ActivityStackTests extends WindowTestsBase { // Start 2 activities that their processes have not yet started. final ActivityRecord[] activities = new ActivityRecord[2]; mSupervisor.beginDeferResume(); + final Task task = new TaskBuilder(mSupervisor).build(); for (int i = 0; i < activities.length; i++) { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); activities[i] = r; doReturn(null).when(mAtm).getProcessController( eq(r.processName), eq(r.info.applicationInfo.uid)); @@ -1405,7 +1402,7 @@ public class ActivityStackTests extends WindowTestsBase { // Assume the top activity is going to resume and // {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown // visibility records that are occluded. - mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); + task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); // Assume the top activity relayouted, just remove it directly. unknownAppVisibilityController.appRemovedOrHidden(activities[1]); // All unresolved records should be removed. @@ -1414,15 +1411,16 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testNonTopVisibleActivityNotResume() { + final Task task = new TaskBuilder(mSupervisor).build(); final ActivityRecord nonTopVisibleActivity = - new ActivityBuilder(mAtm).setTask(mTask).build(); - new ActivityBuilder(mAtm).setTask(mTask).build(); + new ActivityBuilder(mAtm).setTask(task).build(); + new ActivityBuilder(mAtm).setTask(task).build(); doReturn(false).when(nonTopVisibleActivity).attachedToProcess(); doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked(); doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(), anyBoolean()); - mStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, false /* preserveWindows */); verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */, anyBoolean()); @@ -1436,16 +1434,17 @@ public class ActivityStackTests extends WindowTestsBase { private void verifyShouldSleepActivities(boolean focusedStack, boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, boolean expected) { + final Task task = new TaskBuilder(mSupervisor).build(); final DisplayContent display = mock(DisplayContent.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); display.isDefaultDisplay = isDefaultDisplay; - mStack.mDisplayContent = display; + task.mDisplayContent = display; doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); doReturn(displaySleeping).when(display).isSleeping(); - doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay(); + doReturn(focusedStack).when(task).isFocusedStackOnDisplay(); - assertEquals(expected, mStack.shouldSleepActivities()); + assertEquals(expected, task.shouldSleepActivities()); } private static class StackOrderChangedListener 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 f607448c09a0..f745ffbc680c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -126,6 +126,7 @@ public class ActivityStarterTests extends WindowTestsBase { private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude"; private static final int UNIMPORTANT_UID = 12345; private static final int UNIMPORTANT_UID2 = 12346; + private static final int CURRENT_IME_UID = 12347; @Before public void setUp() throws Exception { @@ -315,6 +316,12 @@ public class ActivityStarterTests extends WindowTestsBase { return prepareStarter(launchFlags, mockGetLaunchStack, LAUNCH_MULTIPLE); } + private void setupImeWindow() { + final WindowState imeWindow = createWindow(null, W_INPUT_METHOD, + "mImeWindow", CURRENT_IME_UID); + mDisplayContent.mInputMethodWindow = imeWindow; + } + /** * Creates a {@link ActivityStarter} with default parameters and necessary mocks. * @@ -654,6 +661,14 @@ public class ActivityStarterTests extends WindowTestsBase { UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, false, false, false, false, true); + + setupImeWindow(); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_callingPackageNameIsIme_notAborted", false, + CURRENT_IME_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false, false, false, false); + } private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index f61253c77a8d..017ed883e2bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -19,8 +19,6 @@ package com.android.server.wm; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -44,7 +42,6 @@ import android.view.Display; import androidx.test.filters.MediumTest; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,13 +55,6 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityTaskSupervisorTests extends WindowTestsBase { - private Task mFullscreenTask; - - @Before - public void setUp() throws Exception { - mFullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - } /** * Ensures that an activity is removed from the stopping activities list once it is resumed. @@ -72,7 +62,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { @Test public void testStoppingActivityRemovedWhenResumed() { final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(mFullscreenTask).build(); + .setCreateTask(true).build(); mSupervisor.mStoppingActivities.add(firstActivity); firstActivity.completeResumeLocked(); @@ -86,7 +76,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { @Test public void testReportWaitingActivityLaunchedIfNeeded() { final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(mFullscreenTask).build(); + .setCreateTask(true).build(); final WaitResult taskToFrontWait = new WaitResult(); mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait); @@ -153,7 +143,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { @Test public void testNotifyTaskFocusChanged() { final ActivityRecord fullScreenActivityA = new ActivityBuilder(mAtm).setCreateTask(true) - .setParentTask(mFullscreenTask).build(); + .build(); final Task taskA = fullScreenActivityA.getTask(); final TaskChangeNotificationController taskChangeNotifier = @@ -166,7 +156,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { reset(taskChangeNotifier); final ActivityRecord fullScreenActivityB = new ActivityBuilder(mAtm).setCreateTask(true) - .setParentTask(mFullscreenTask).build(); + .build(); final Task taskB = fullScreenActivityB.getTask(); mAtm.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB"); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 87a5985d507d..91b9449eddb0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -53,14 +53,12 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class AppChangeTransitionTests extends WindowTestsBase { - private Task mStack; private Task mTask; private ActivityRecord mActivity; public void setUpOnDisplay(DisplayContent dc) { mActivity = createActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); mTask = mActivity.getTask(); - mStack = mTask.getRootTask(); // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests. RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); @@ -143,7 +141,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { // Reparenting to a display with different windowing mode may trigger // a change transition internally, but it should be cleaned-up once // the display change is complete. - mStack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true); + mTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true); assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index bc4f16eccb0a..8cad56a45518 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -20,10 +20,12 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE; +import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_OPEN; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -74,11 +76,14 @@ public class AppTransitionControllerTest extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); translucentOpening.setOccludesParent(false); translucentOpening.setVisible(false); + mDisplayContent.prepareAppTransition(TRANSIT_OPEN); mDisplayContent.mOpeningApps.add(behind); mDisplayContent.mOpeningApps.add(translucentOpening); + assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN, - mAppTransitionController.maybeUpdateTransitToTranslucentAnim( - TRANSIT_OLD_TASK_OPEN)); + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null, null)); } @Test @@ -89,10 +94,12 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord translucentClosing = createActivityRecord(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); translucentClosing.setOccludesParent(false); + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); mDisplayContent.mClosingApps.add(translucentClosing); assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, - mAppTransitionController.maybeUpdateTransitToTranslucentAnim( - TRANSIT_OLD_TASK_CLOSE)); + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null, null)); } @Test @@ -104,11 +111,13 @@ public class AppTransitionControllerTest extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); translucentOpening.setOccludesParent(false); translucentOpening.setVisible(false); + mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE); mDisplayContent.mOpeningApps.add(behind); mDisplayContent.mOpeningApps.add(translucentOpening); assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, - mAppTransitionController.maybeUpdateTransitToTranslucentAnim( - TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE)); + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null, null)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 850e72e8e94b..6ca69bf974d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -18,11 +18,12 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_OPEN; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -71,40 +72,58 @@ public class AppTransitionTests extends WindowTestsBase { @Test public void testKeyguardOverride() { - mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld()); + final DisplayContent dc = createNewDisplay(Display.STATE_ON); + final ActivityRecord activity = createActivityRecord(dc); + + mDc.prepareAppTransition(TRANSIT_OPEN); + mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY); + mDc.mOpeningApps.add(activity); + assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, + AppTransitionController.getTransitCompatType(mDc.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */)); } @Test public void testKeyguardKeep() { - mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld()); - } - - @Test - public void testForceOverride() { - mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); - mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); - assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransitionOld()); + final DisplayContent dc = createNewDisplay(Display.STATE_ON); + final ActivityRecord activity = createActivityRecord(dc); + + mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY); + mDc.prepareAppTransition(TRANSIT_OPEN); + mDc.mOpeningApps.add(activity); + assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, + AppTransitionController.getTransitCompatType(mDc.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */)); } @Test public void testCrashing() { - mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransitionOld()); + final DisplayContent dc = createNewDisplay(Display.STATE_ON); + final ActivityRecord activity = createActivityRecord(dc); + + mDc.prepareAppTransition(TRANSIT_OPEN); + mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED); + mDc.mClosingApps.add(activity); + assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, + AppTransitionController.getTransitCompatType(mDc.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */)); } @Test public void testKeepKeyguard_withCrashing() { - mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld()); + final DisplayContent dc = createNewDisplay(Display.STATE_ON); + final ActivityRecord activity = createActivityRecord(dc); + + mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY); + mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED); + mDc.mClosingApps.add(activity); + assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, + AppTransitionController.getTransitCompatType(mDc.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */)); } @Test @@ -123,12 +142,8 @@ public class AppTransitionTests extends WindowTestsBase { // Simulate activity resume / finish flows to prepare app transition & set visibility, // make sure transition is set as expected for each display. - dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld()); - dc2.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransitionOld()); + dc1.prepareAppTransition(TRANSIT_OPEN); + dc2.prepareAppTransition(TRANSIT_CLOSE); // One activity window is visible for resuming & the other activity window is invisible // for finishing in different display. activity1.setVisibility(true, false); @@ -156,9 +171,8 @@ public class AppTransitionTests extends WindowTestsBase { dc1.mClosingApps.add(activity1); assertTrue(dc1.mClosingApps.size() > 0); - dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld()); + dc1.prepareAppTransition(TRANSIT_OPEN); + assertTrue(dc1.mAppTransition.containsTransitRequest(TRANSIT_OPEN)); assertTrue(dc1.mAppTransition.isTransitionSet()); dc1.mOpeningApps.add(activity1); @@ -199,9 +213,8 @@ public class AppTransitionTests extends WindowTestsBase { // Simulate activity finish flows to prepare app transition & set visibility, // make sure transition is set as expected. - dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransitionOld()); + dc.prepareAppTransition(TRANSIT_CLOSE); + assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_CLOSE)); dc.mAppTransition.overridePendingAppTransitionRemote(adapter); exitingActivity.setVisibility(false, false); assertTrue(dc.mClosingApps.size() > 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index f77454d440f9..d899ebe4bc1f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -65,7 +65,6 @@ import android.view.WindowManager; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,94 +81,87 @@ import java.util.ArrayList; @RunWith(WindowTestRunner.class) public class AppWindowTokenTests extends WindowTestsBase { - Task mStack; - Task mTask; - ActivityRecord mActivity; - private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); - @Before - public void setUp() throws Exception { - mStack = createTaskStackOnDisplay(mDisplayContent); - mTask = createTaskInStack(mStack, 0 /* userId */); - mActivity = createNonAttachedActivityRecord(mDisplayContent); - - mTask.addChild(mActivity, 0); - } - @Test @Presubmit public void testAddWindow_Order() { - assertEquals(0, mActivity.getChildCount()); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + assertEquals(0, activity.getChildCount()); - final WindowState win1 = createWindow(null, TYPE_APPLICATION, mActivity, "win1"); - final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mActivity, + final WindowState win1 = createWindow(null, TYPE_APPLICATION, activity, "win1"); + final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity, "startingWin"); - final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "baseWin"); - final WindowState win4 = createWindow(null, TYPE_APPLICATION, mActivity, "win4"); + final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "baseWin"); + final WindowState win4 = createWindow(null, TYPE_APPLICATION, activity, "win4"); // Should not contain the windows that were added above. - assertEquals(4, mActivity.getChildCount()); - assertTrue(mActivity.mChildren.contains(win1)); - assertTrue(mActivity.mChildren.contains(startingWin)); - assertTrue(mActivity.mChildren.contains(baseWin)); - assertTrue(mActivity.mChildren.contains(win4)); + assertEquals(4, activity.getChildCount()); + assertTrue(activity.mChildren.contains(win1)); + assertTrue(activity.mChildren.contains(startingWin)); + assertTrue(activity.mChildren.contains(baseWin)); + assertTrue(activity.mChildren.contains(win4)); // The starting window should be on-top of all other windows. - assertEquals(startingWin, mActivity.mChildren.peekLast()); + assertEquals(startingWin, activity.mChildren.peekLast()); // The base application window should be below all other windows. - assertEquals(baseWin, mActivity.mChildren.peekFirst()); - mActivity.removeImmediately(); + assertEquals(baseWin, activity.mChildren.peekFirst()); + activity.removeImmediately(); } @Test @Presubmit public void testFindMainWindow() { - assertNull(mActivity.findMainWindow()); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + assertNull(activity.findMainWindow()); - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1"); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window11"); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window12"); - assertEquals(window1, mActivity.findMainWindow()); + final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1"); + final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window11"); + final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window12"); + assertEquals(window1, activity.findMainWindow()); window1.mAnimatingExit = true; - assertEquals(window1, mActivity.findMainWindow()); - final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mActivity, + assertEquals(window1, activity.findMainWindow()); + final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, activity, "window2"); - assertEquals(window2, mActivity.findMainWindow()); - mActivity.removeImmediately(); + assertEquals(window2, activity.findMainWindow()); + activity.removeImmediately(); } @Test @Presubmit public void testGetTopFullscreenOpaqueWindow() { - assertNull(mActivity.getTopFullscreenOpaqueWindow()); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + assertNull(activity.getTopFullscreenOpaqueWindow()); - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1"); - final WindowState window11 = createWindow(null, TYPE_APPLICATION, mActivity, "window11"); - final WindowState window12 = createWindow(null, TYPE_APPLICATION, mActivity, "window12"); - assertEquals(window12, mActivity.getTopFullscreenOpaqueWindow()); + final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1"); + final WindowState window11 = createWindow(null, TYPE_APPLICATION, activity, "window11"); + final WindowState window12 = createWindow(null, TYPE_APPLICATION, activity, "window12"); + assertEquals(window12, activity.getTopFullscreenOpaqueWindow()); window12.mAttrs.width = 500; - assertEquals(window11, mActivity.getTopFullscreenOpaqueWindow()); + assertEquals(window11, activity.getTopFullscreenOpaqueWindow()); window11.mAttrs.width = 500; - assertEquals(window1, mActivity.getTopFullscreenOpaqueWindow()); + assertEquals(window1, activity.getTopFullscreenOpaqueWindow()); window1.mAttrs.alpha = 0f; - assertNull(mActivity.getTopFullscreenOpaqueWindow()); - mActivity.removeImmediately(); + assertNull(activity.getTopFullscreenOpaqueWindow()); + activity.removeImmediately(); } @UseTestDisplay(addWindows = W_ACTIVITY) @Test @FlakyTest(bugId = 131005232) public void testLandscapeSeascapeRotationByApp() { + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = createWindowState(attrs, mActivity); - mActivity.addWindow(appWindow); + final TestWindowState appWindow = createWindowState(attrs, activity); + activity.addWindow(appWindow); // Set initial orientation and update. - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); mDisplayContent.updateOrientation( mDisplayContent.getRequestedOverrideConfiguration(), null /* freezeThisOneIfNeeded */, false /* forceUpdate */); @@ -177,7 +169,7 @@ public class AppWindowTokenTests extends WindowTestsBase { appWindow.mResizeReported = false; // Update the orientation to perform 180 degree rotation and check that resize was reported. - mActivity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + activity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); mDisplayContent.updateOrientation( mDisplayContent.getRequestedOverrideConfiguration(), null /* freezeThisOneIfNeeded */, false /* forceUpdate */); @@ -192,14 +184,17 @@ public class AppWindowTokenTests extends WindowTestsBase { @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testLandscapeSeascapeRotationByPolicy() { + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); // This instance has been spied in {@link TestDisplayContent}. final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("RotationByPolicy"); - final TestWindowState appWindow = createWindowState(attrs, mActivity); - mActivity.addWindow(appWindow); + final TestWindowState appWindow = createWindowState(attrs, activity); + activity.addWindow(appWindow); // Set initial orientation and update. performRotation(displayRotation, Surface.ROTATION_90); @@ -220,48 +215,53 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test @Presubmit public void testGetOrientation() { - mActivity.setVisible(true); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.setVisible(true); - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - mActivity.setOccludesParent(false); + activity.setOccludesParent(false); // Can specify orientation if app doesn't occludes parent. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation()); - mActivity.setOccludesParent(true); - mActivity.setVisible(false); + activity.setOccludesParent(true); + activity.setVisible(false); // Can not specify orientation if app isn't visible even though it occludes parent. - assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Can specify orientation if the current orientation candidate is orientation behind. assertEquals(SCREEN_ORIENTATION_LANDSCAPE, - mActivity.getOrientation(SCREEN_ORIENTATION_BEHIND)); + activity.getOrientation(SCREEN_ORIENTATION_BEHIND)); } @Test @Presubmit public void testKeyguardFlagsDuringRelaunch() { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD; attrs.setTitle("AppWindow"); - final TestWindowState appWindow = createWindowState(attrs, mActivity); + final TestWindowState appWindow = createWindowState(attrs, activity); // Add window with show when locked flag - mActivity.addWindow(appWindow); - assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow()); + activity.addWindow(appWindow); + assertTrue(activity.containsShowWhenLockedWindow() + && activity.containsDismissKeyguardWindow()); // Start relaunching - mActivity.startRelaunching(); - assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow()); + activity.startRelaunching(); + assertTrue(activity.containsShowWhenLockedWindow() + && activity.containsDismissKeyguardWindow()); // Remove window and make sure that we still report back flag - mActivity.removeChild(appWindow); - assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow()); + activity.removeChild(appWindow); + assertTrue(activity.containsShowWhenLockedWindow() + && activity.containsDismissKeyguardWindow()); // Finish relaunching and ensure flag is now not reported - mActivity.finishRelaunching(); - assertFalse( - mActivity.containsShowWhenLockedWindow() || mActivity.containsDismissKeyguardWindow()); + activity.finishRelaunching(); + assertFalse(activity.containsShowWhenLockedWindow() + || activity.containsDismissKeyguardWindow()); } @Test @@ -281,17 +281,18 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testSetOrientation() { - mActivity.setVisible(true); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.setVisible(true); // Assert orientation is unspecified to start. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.getOrientation()); - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation()); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation()); - mDisplayContent.removeAppToken(mActivity.token); + mDisplayContent.removeAppToken(activity.token); // Assert orientation is unset to after container is removed. - assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Reset display frozen state mWm.mDisplayFrozen = false; @@ -300,14 +301,15 @@ public class AppWindowTokenTests extends WindowTestsBase { @UseTestDisplay @Test public void testRespectTopFullscreenOrientation() { - final Configuration displayConfig = mActivity.mDisplayContent.getConfiguration(); - final Configuration activityConfig = mActivity.getConfiguration(); - mActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Configuration displayConfig = activity.mDisplayContent.getConfiguration(); + final Configuration activityConfig = activity.getConfiguration(); + activity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation); assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation); - final ActivityRecord topActivity = createActivityRecord(mTask); + final ActivityRecord topActivity = createActivityRecord(activity.getTask()); topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertEquals(Configuration.ORIENTATION_LANDSCAPE, displayConfig.orientation); @@ -322,32 +324,36 @@ public class AppWindowTokenTests extends WindowTestsBase { @UseTestDisplay @Test public void testReportOrientationChange() { - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); mDisplayContent.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); - reset(mTask); - mActivity.reportDescendantOrientationChangeIfNeeded(); - verify(mTask).onConfigurationChanged(any(Configuration.class)); + reset(task); + activity.reportDescendantOrientationChangeIfNeeded(); + verify(task).onConfigurationChanged(any(Configuration.class)); } @Test @FlakyTest(bugId = 131176283) public void testCreateRemoveStartingWindow() { - mActivity.addStartingWindow(mPackageName, + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); waitUntilHandlersIdle(); - assertHasStartingWindow(mActivity); - mActivity.removeStartingWindow(); + assertHasStartingWindow(activity); + activity.removeStartingWindow(); waitUntilHandlersIdle(); - assertNoStartingWindow(mActivity); + assertNoStartingWindow(activity); } @Test public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows - final ActivityRecord appToken = createIsolatedTestActivityRecord(); + final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build(); for (int i = 0; i < 1000; i++) { appToken.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, @@ -360,8 +366,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindow() { - final ActivityRecord activity1 = createIsolatedTestActivityRecord(); - final ActivityRecord activity2 = createIsolatedTestActivityRecord(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); @@ -376,8 +382,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowWhileCreating() { - final ActivityRecord activity1 = createIsolatedTestActivityRecord(); - final ActivityRecord activity2 = createIsolatedTestActivityRecord(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen( () -> { // Surprise, ...! Transfer window in the middle of the creation flow. @@ -396,8 +402,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowCanAnimate() { - final ActivityRecord activity1 = createIsolatedTestActivityRecord(); - final ActivityRecord activity2 = createIsolatedTestActivityRecord(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); @@ -419,34 +425,34 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowFromFinishingActivity() { - mActivity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */, + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity.getTask(); + activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */, "Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */, null /* transferFrom */, true /* newTask */, true /* taskSwitch */, false /* processRunning */, false /* allowTaskSnapshot */, false /* activityCreate */); waitUntilHandlersIdle(); - assertHasStartingWindow(mActivity); - mActivity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN; + assertHasStartingWindow(activity); + activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN; - doCallRealMethod().when(mStack).startActivityLocked( + doCallRealMethod().when(task).startActivityLocked( any(), any(), anyBoolean(), anyBoolean(), any()); // Make mVisibleSetFromTransferredStartingWindow true. - final ActivityRecord middle = new ActivityBuilder(mWm.mAtmService) - .setTask(mTask).build(); - mStack.startActivityLocked(middle, null /* focusedTopActivity */, + final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build(); + task.startActivityLocked(middle, null /* focusedTopActivity */, false /* newTask */, false /* keepCurTransition */, null /* options */); middle.makeFinishingLocked(); - assertNull(mActivity.mStartingWindow); + assertNull(activity.mStartingWindow); assertHasStartingWindow(middle); - final ActivityRecord top = new ActivityBuilder(mWm.mAtmService) - .setTask(mTask).build(); + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build(); // Expect the visibility should be updated to true when transferring starting window from // a visible activity. top.setVisible(false); // The finishing middle should be able to transfer starting window to top. - mStack.startActivityLocked(top, null /* focusedTopActivity */, + task.startActivityLocked(top, null /* focusedTopActivity */, false /* newTask */, false /* keepCurTransition */, null /* options */); assertNull(middle.mStartingWindow); @@ -459,10 +465,12 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowSetFixedRotation() { - final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity.getTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.setVisible(false); - mTask.positionChildAt(topActivity, POSITION_TOP); - mActivity.addStartingWindow(mPackageName, + task.positionChildAt(topActivity, POSITION_TOP); + activity.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); waitUntilHandlersIdle(); @@ -470,38 +478,25 @@ public class AppWindowTokenTests extends WindowTestsBase { // Make activities to have different rotation from it display and set fixed rotation // transform to activity1. int rotation = (mDisplayContent.getRotation() + 1) % 4; - mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation); + mDisplayContent.setFixedRotationLaunchingApp(activity, rotation); doReturn(rotation).when(mDisplayContent) .rotationForActivityInDifferentOrientation(topActivity); // Make sure the fixed rotation transform linked to activity2 when adding starting window // on activity2. topActivity.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(), false, false, false, true, false); waitUntilHandlersIdle(); assertTrue(topActivity.hasFixedRotationTransform()); } - private ActivityRecord createIsolatedTestActivityRecord() { - final Task taskStack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(taskStack, 0 /* userId */); - return createTestActivityRecordForGivenTask(task); - } - - private ActivityRecord createTestActivityRecordForGivenTask(Task task) { - final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); - task.addChild(activity, 0); - waitUntilHandlersIdle(); - return activity; - } - @Test public void testTryTransferStartingWindowFromHiddenAboveToken() { // Add two tasks on top of each other. - final ActivityRecord activityTop = createIsolatedTestActivityRecord(); - final ActivityRecord activityBottom = - createTestActivityRecordForGivenTask(activityTop.getTask()); + final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build(); + activityTop.getTask().addChild(activityBottom, 0); // Add a starting window. activityTop.addStartingWindow(mPackageName, @@ -523,53 +518,58 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransitionAnimationBounds() { removeGlobalMinSizeRestriction(); + final Task task = new TaskBuilder(mSupervisor) + .setCreateParentTask(true).setCreateActivity(true).build(); + final Task rootTask = task.getRootTask(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); final Rect stackBounds = new Rect(0, 0, 1000, 600); final Rect taskBounds = new Rect(100, 400, 600, 800); // Set the bounds and windowing mode to window configuration directly, otherwise the // testing setups may be discarded by configuration resolving. - mStack.getWindowConfiguration().setBounds(stackBounds); - mTask.getWindowConfiguration().setBounds(taskBounds); - mActivity.getWindowConfiguration().setBounds(taskBounds); + rootTask.getWindowConfiguration().setBounds(stackBounds); + task.getWindowConfiguration().setBounds(taskBounds); + activity.getWindowConfiguration().setBounds(taskBounds); // Check that anim bounds for freeform window match task bounds - mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_NONE)); + task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_NONE)); // STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by // bounds animation layer. - mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); + task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); // Even the activity is smaller than task and it is not aligned to the top-left corner of // task, the animation bounds the same as task and position should be zero because in real // case the letterbox will fill the remaining area in task. final Rect halfBounds = new Rect(taskBounds); halfBounds.scale(0.5f); - mActivity.getWindowConfiguration().setBounds(halfBounds); + activity.getWindowConfiguration().setBounds(halfBounds); final Point animationPosition = new Point(); - mActivity.getAnimationPosition(animationPosition); + activity.getAnimationPosition(animationPosition); - assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); + assertEquals(taskBounds, activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); assertEquals(new Point(0, 0), animationPosition); // STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later. - mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - assertEquals(mStack.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM)); + task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + assertEquals(rootTask.getBounds(), activity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM)); } @Test public void testHasStartingWindow() { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING); - final TestWindowState startingWindow = createWindowState(attrs, mActivity); - mActivity.startingDisplayed = true; - mActivity.addWindow(startingWindow); - assertTrue("Starting window should be present", mActivity.hasStartingWindow()); - mActivity.startingDisplayed = false; - assertTrue("Starting window should be present", mActivity.hasStartingWindow()); - - mActivity.removeChild(startingWindow); - assertFalse("Starting window should not be present", mActivity.hasStartingWindow()); + final TestWindowState startingWindow = createWindowState(attrs, activity); + activity.startingDisplayed = true; + activity.addWindow(startingWindow); + assertTrue("Starting window should be present", activity.hasStartingWindow()); + activity.startingDisplayed = false; + assertTrue("Starting window should be present", activity.hasStartingWindow()); + + activity.removeChild(startingWindow); + assertFalse("Starting window should not be present", activity.hasStartingWindow()); } private void assertHasStartingWindow(ActivityRecord atoken) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index cfe956f77bd3..06a6882dc698 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -52,8 +50,6 @@ public class DisplayAreaGroupTest extends WindowTestsBase { private DisplayAreaGroup mDisplayAreaGroup; private TaskDisplayArea mTaskDisplayArea; - private Task mStack; - private ActivityRecord mActivity; @Before public void setUp() { @@ -65,9 +61,6 @@ public class DisplayAreaGroupTest extends WindowTestsBase { mTaskDisplayArea = new TaskDisplayArea( mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1); mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP); - mStack = mTaskDisplayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mActivity = new ActivityBuilder(mAtm).setTask(mStack).build(); mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea); } @@ -91,28 +84,31 @@ public class DisplayAreaGroupTest extends WindowTestsBase { @Test public void testGetRequestedOrientationForDisplay() { + final Task task = new TaskBuilder(mSupervisor) + .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any()); - mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); // Display is portrait, DisplayAreaGroup inherits that mDisplayContent.setBounds(0, 0, 600, 900); assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); - assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */)) + assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */)) .isEqualTo(ORIENTATION_PORTRAIT); // DisplayAreaGroup is landscape, different from Display mDisplayAreaGroup.setBounds(0, 0, 600, 450); assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); - assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */)) + assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */)) .isEqualTo(ORIENTATION_LANDSCAPE); // DisplayAreaGroup is portrait, same as Display mDisplayAreaGroup.setBounds(0, 0, 300, 900); assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); - assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */)) + assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */)) .isEqualTo(ORIENTATION_PORTRAIT); } 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 0f7a254ececf..d9217188582f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1186,8 +1186,7 @@ public class DisplayContentTests extends WindowTestsBase { final ActivityRecord app = mAppWindow.mActivityRecord; app.setVisible(false); - mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */); + mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); mDisplayContent.mOpeningApps.add(app); final int newOrientation = getRotatedOrientation(mDisplayContent); app.setRequestedOrientation(newOrientation); @@ -1370,8 +1369,7 @@ public class DisplayContentTests extends WindowTestsBase { final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); app.setVisible(false); app.setState(Task.ActivityState.RESUMED, "test"); - mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */); + mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); mDisplayContent.mOpeningApps.add(app); final int newOrientation = getRotatedOrientation(mDisplayContent); app.setRequestedOrientation(newOrientation); diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 5641fe28a52f..70d47a580801 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -46,7 +46,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.platform.test.annotations.Presubmit; import android.view.DragEvent; import android.view.IWindowSessionCallback; @@ -59,6 +58,7 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import org.junit.After; import org.junit.AfterClass; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index cc92dddb6de1..40f73b12f805 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -252,7 +252,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertNull(mController.mRecentScreenshotAnimator); // Simulate the app transition finishing - mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0); + mController.mAppTransitionListener.onAppTransitionStartingLocked(false, 0, 0, 0); verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index ddc29f5dd516..a4bf5948c6a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -72,13 +72,11 @@ import java.util.ArrayList; @Presubmit @RunWith(WindowTestRunner.class) public class SizeCompatTests extends WindowTestsBase { - private Task mStack; private Task mTask; private ActivityRecord mActivity; private void setUpApp(DisplayContent display) { - mStack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); - mTask = mStack.getBottomMostTask(); + mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); mActivity = mTask.getTopNonFinishingActivity(); } @@ -97,7 +95,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); - resizeDisplay(mStack.mDisplayContent, 600, 1200); + resizeDisplay(mTask.mDisplayContent, 600, 1200); // The visible activity should recompute configuration according to the last parent bounds. mAtm.restartActivityProcessIfVisible(mActivity.appToken); @@ -196,7 +194,7 @@ public class SizeCompatTests extends WindowTestsBase { final int originalDpi = mActivity.getConfiguration().densityDpi; // Move the non-resizable activity to the new display. - mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); + mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); assertEquals(originalBounds.width(), mActivity.getBounds().width()); assertEquals(originalBounds.height(), mActivity.getBounds().height()); @@ -321,7 +319,7 @@ public class SizeCompatTests extends WindowTestsBase { .setCanRotate(false).setNotch(notchHeight).build(); // Move the non-resizable activity to the new display. - mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); + mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); // The configuration bounds [820, 0 - 1820, 2500] should keep the same. assertEquals(origWidth, configBounds.width()); assertEquals(origHeight, configBounds.height()); @@ -363,7 +361,7 @@ public class SizeCompatTests extends WindowTestsBase { // Although the activity is fixed orientation, force rotate the display. rotateDisplay(mActivity.mDisplayContent, ROTATION_270); - assertEquals(ROTATION_270, mStack.getWindowConfiguration().getRotation()); + assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation()); assertEquals(origBounds.width(), currentBounds.width()); // The notch is on horizontal side, so current height changes from 1460 to 1400. @@ -436,7 +434,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testResetNonVisibleActivity() { setUpDisplaySizeWithApp(1000, 2500); prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED); - final DisplayContent display = mStack.mDisplayContent; + final DisplayContent display = mTask.mDisplayContent; // Resize the display so the activity is in size compatibility mode. resizeDisplay(display, 900, 1800); @@ -488,7 +486,7 @@ public class SizeCompatTests extends WindowTestsBase { }); // Resize the display so that the activity exercises size-compat mode. - resizeDisplay(mStack.mDisplayContent, 1000, 2500); + resizeDisplay(mTask.mDisplayContent, 1000, 2500); // Expect the exact token when the activity is in size compatibility mode. assertEquals(1, compatTokens.size()); @@ -501,7 +499,7 @@ public class SizeCompatTests extends WindowTestsBase { activity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode"); - mStack.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity); + mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity); // Expect null token when switching to non-size-compat mode activity. assertEquals(1, compatTokens.size()); @@ -525,13 +523,13 @@ public class SizeCompatTests extends WindowTestsBase { // The non-resizable activity should not be size compat because it is on a resizable task // in multi-window mode. - mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); assertFalse(activity.shouldUseSizeCompatMode()); // The non-resizable activity should not be size compat because the display support // changing windowing mode from fullscreen to freeform. - mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); - mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); + mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); assertFalse(activity.shouldUseSizeCompatMode()); } @@ -544,8 +542,7 @@ public class SizeCompatTests extends WindowTestsBase { addStatusBar(mActivity.mDisplayContent); mActivity.setVisible(false); - mActivity.mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */); + mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); mActivity.mDisplayContent.mOpeningApps.add(mActivity); final float maxAspect = 1.8f; prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE); @@ -784,7 +781,7 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); final Rect activityBounds = new Rect(mActivity.getBounds()); - mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); + mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); // App still in size compat, and the bounds don't change. verify(mActivity, never()).clearSizeCompatMode(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 4bd8edd44f5c..28ba710797c9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -70,23 +70,22 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class TaskDisplayAreaTests extends WindowTestsBase { - private Task mPinnedStack; + private Task mPinnedTask; @Before public void setUp() throws Exception { - mPinnedStack = createTaskStackOnDisplay( + mPinnedTask = createTaskStackOnDisplay( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent); // Stack should contain visible app window to be considered visible. - final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); - assertFalse(mPinnedStack.isVisible()); + assertFalse(mPinnedTask.isVisible()); final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent); - pinnedTask.addChild(pinnedApp, 0 /* addPos */); - assertTrue(mPinnedStack.isVisible()); + mPinnedTask.addChild(pinnedApp, 0 /* addPos */); + assertTrue(mPinnedTask.isVisible()); } @After public void tearDown() throws Exception { - mPinnedStack.removeImmediately(); + mPinnedTask.removeImmediately(); } @Test @@ -118,19 +117,19 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask); assertThat(pinnedStackPos).isGreaterThan(stack2Pos); assertThat(stack2Pos).isGreaterThan(stack1Pos); - taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false); + taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false); assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); - taskStackContainer.positionChildAt(1, mPinnedStack, false); + taskStackContainer.positionChildAt(1, mPinnedTask, false); assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); } @Test @@ -141,16 +140,16 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final WindowContainer taskStackContainer = stack1.getParent(); final int stackPos = taskStackContainer.mChildren.indexOf(stack1); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask); assertThat(pinnedStackPos).isGreaterThan(stackPos); taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false); assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 0b0341a1c8b7..fcd46a3b4024 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -41,10 +41,10 @@ import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.UserManager; -import android.os.UserManagerInternal; import android.view.Surface; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import org.junit.After; import org.junit.AfterClass; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index e0c72fb00060..3d8adbd215bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; @@ -919,7 +920,7 @@ public class WindowContainerTests extends WindowTestsBase { } }, 0, 0, false); adapter.setCallingPidUid(123, 456); - wc.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_TASK_OPEN, false); + wc.getDisplayContent().prepareAppTransition(TRANSIT_OPEN); wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter); spyOn(wc); doReturn(true).when(wc).okToAnimate(); @@ -943,8 +944,7 @@ public class WindowContainerTests extends WindowTestsBase { // Make sure animation finish callback will be received and reset animating state after // animation finish. - wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act, - mDisplayContent.mOpeningApps); + wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act); verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any()); assertFalse(wc.isAnimating()); assertFalse(act.isAnimating(PARENTS)); 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 ee1034c561ab..ebbbc29b035c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -840,10 +840,13 @@ class WindowTestsBase extends SystemServiceTestsBase { // to set it somewhere else since we can't mock resources. doReturn(true).when(activity).occludesParent(); doReturn(true).when(activity).fillsParent(); + mTask.addChild(activity); if (mOnTop) { + // Move the task to front after activity added. + // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks + // (e.g. home stack). mTask.moveToFront("createActivity"); } - mTask.addChild(activity); // Make visible by default... activity.setVisible(true); } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 632ad4c33e36..4bf93a26ec03 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -60,7 +60,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.UUID; -import java.util.function.BiFunction; /** * Helper for {@link SoundTrigger} APIs. Supports two types of models: @@ -118,8 +117,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private PowerSaveModeListener mPowerSaveModeListener; - private final BiFunction<Integer, SoundTrigger.StatusListener, SoundTriggerModule> - mModuleProvider; + private final SoundTriggerModuleProvider mModuleProvider; // Handler to process call state changes will delay to allow time for the audio // and sound trigger HALs to process the end of call notifications @@ -128,12 +126,32 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private static final int MSG_CALL_STATE_CHANGED = 0; private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000; - SoundTriggerHelper(Context context, - @NonNull BiFunction<Integer, SoundTrigger.StatusListener, - SoundTriggerModule> moduleProvider) { + /** + * Provider interface for retrieving SoundTriggerModule instances + */ + public interface SoundTriggerModuleProvider { + /** + * Populate module properties for all available modules + * + * @param modules List of ModuleProperties to be populated + * @return Status int 0 on success. + */ + int listModuleProperties(@NonNull ArrayList<SoundTrigger.ModuleProperties> modules); + + /** + * Get SoundTriggerModule based on {@link SoundTrigger.ModuleProperties#getId()} + * + * @param moduleId Module ID + * @param statusListener Client listener to be associated with the returned module + * @return Module associated with moduleId + */ + SoundTriggerModule getModule(int moduleId, SoundTrigger.StatusListener statusListener); + } + + SoundTriggerHelper(Context context, SoundTriggerModuleProvider moduleProvider) { ArrayList <ModuleProperties> modules = new ArrayList<>(); mModuleProvider = moduleProvider; - int status = SoundTrigger.listModules(modules); + int status = mModuleProvider.listModuleProperties(modules); mContext = context; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); @@ -272,7 +290,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private int prepareForRecognition(ModelData modelData) { if (mModule == null) { - mModule = mModuleProvider.apply(mModuleProperties.getId(), this); + mModule = mModuleProvider.getModule(mModuleProperties.getId(), this); if (mModule == null) { Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module"); return STATUS_ERROR; diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 2a5bfce9bb33..bd678fd54063 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -51,6 +51,7 @@ import android.hardware.soundtrigger.SoundTrigger.ModelParamRange; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.hardware.soundtrigger.SoundTrigger.SoundModel; +import android.hardware.soundtrigger.SoundTriggerModule; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioRecord; @@ -219,9 +220,20 @@ public class SoundTriggerService extends SystemService { Identity originatorIdentity = IdentityContext.getNonNull(); return new SoundTriggerHelper(mContext, - (moduleId, listener) -> SoundTrigger.attachModuleAsMiddleman(moduleId, listener, - null, - middlemanIdentity, originatorIdentity)); + new SoundTriggerHelper.SoundTriggerModuleProvider() { + @Override + public int listModuleProperties(ArrayList<ModuleProperties> modules) { + return SoundTrigger.listModulesAsMiddleman(modules, middlemanIdentity, + originatorIdentity); + } + + @Override + public SoundTriggerModule getModule(int moduleId, + SoundTrigger.StatusListener statusListener) { + return SoundTrigger.attachModuleAsMiddleman(moduleId, statusListener, null, + middlemanIdentity, originatorIdentity); + } + }); } class SoundTriggerServiceStub extends ISoundTriggerService.Stub { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 657a7dd84bdf..06c23de84ac3 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -23,7 +23,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.ActivityThread; import android.app.AppGlobals; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; @@ -49,7 +48,6 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.ModelParamRange; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; -import android.media.permission.ClearCallingIdentityContext; import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.permission.PermissionUtil; @@ -66,7 +64,6 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.Trace; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; @@ -94,6 +91,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.soundtrigger.SoundTriggerInternal; import com.android.server.utils.TimingsTraceAndSlog; diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 964cf76d17f6..572aed3e6a11 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -447,6 +447,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -561,6 +564,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -962,6 +968,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -1220,6 +1229,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -1419,6 +1431,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -2298,7 +2313,10 @@ public final class SmsManager { RESULT_RIL_OPERATION_NOT_ALLOWED, RESULT_RIL_NO_RESOURCES, RESULT_RIL_CANCELLED, - RESULT_RIL_SIM_ABSENT + RESULT_RIL_SIM_ABSENT, + RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED, + RESULT_RIL_ACCESS_BARRED, + RESULT_RIL_BLOCKED_DUE_TO_CALL }) @Retention(RetentionPolicy.SOURCE) public @interface Result {} @@ -2563,6 +2581,21 @@ public final class SmsManager { */ public static final int RESULT_RIL_SIM_ABSENT = 120; + /** + * 1X voice and SMS are not allowed simulteneously. + */ + public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; + + /** + * Access is barred. + */ + public static final int RESULT_RIL_ACCESS_BARRED = 122; + + /** + * SMS is blocked due to call control, e.g., resource unavailable in the SMR entity. + */ + public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; + // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION} /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 83e63ef29e2c..904232b54b8f 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -791,6 +791,13 @@ public class SubscriptionManager { public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED; /** + * Determines if the user has enabled cross SIM calling for this subscription. + * + * @hide + */ + public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED; + + /** * TelephonyProvider column name for whether a subscription is opportunistic, that is, * whether the network it connects to is limited in functionality or coverage. * For example, CBRS. diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl new file mode 100644 index 000000000000..46bc4abde159 --- /dev/null +++ b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable ApnThrottleStatus; diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ApnThrottleStatus.java new file mode 100644 index 000000000000..51461d17690a --- /dev/null +++ b/telephony/java/android/telephony/data/ApnThrottleStatus.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.telephony.AccessNetworkConstants; +import android.telephony.Annotation; + +import java.util.Objects; + +/** + * Status information regarding the throttle status of an APN type. + * + * @hide + */ +@SystemApi +public final class ApnThrottleStatus implements Parcelable { + /** + * The APN type is not throttled. + */ + public static final int THROTTLE_TYPE_NONE = 1; + + /** + * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()} + * has reached {@link ApnThrottleStatus#getThrottleExpiryTimeMillis} + */ + public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; + + /** {@hide} */ + @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = { + ApnThrottleStatus.THROTTLE_TYPE_NONE, + ApnThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME, + }) + public @interface ThrottleType { + } + + /** + * The framework will not retry the APN type. + */ + public static final int RETRY_TYPE_NONE = 1; + + /** + * The next time the framework retries, it will attempt to establish a new connection. + */ + public static final int RETRY_TYPE_NEW_CONNECTION = 2; + + /** + * The next time the framework retires, it will retry to handover. + */ + public static final int RETRY_TYPE_HANDOVER = 3; + + /** {@hide} */ + @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = { + ApnThrottleStatus.RETRY_TYPE_NONE, + ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION, + ApnThrottleStatus.RETRY_TYPE_HANDOVER, + }) + public @interface RetryType { + } + + private final int mSlotIndex; + private final @AccessNetworkConstants.TransportType int mTransportType; + private final @Annotation.ApnType int mApnType; + private final long mThrottleExpiryTimeMillis; + private final @RetryType int mRetryType; + private final @ThrottleType int mThrottleType; + + /** + * The slot index that the status applies to. + * + * @return the slot index + */ + public int getSlotIndex() { + return mSlotIndex; + } + + /** + * The type of transport that the status applies to. + * + * @return the transport type + */ + @AccessNetworkConstants.TransportType + public int getTransportType() { + return mTransportType; + } + + /** + * The APN type that the status applies to. + * + * @return the apn type + */ + @Annotation.ApnType + public int getApnType() { + return mApnType; + } + + /** + * The type of throttle applied to the APN type. + * + * @return the throttle type + */ + @ThrottleType + public int getThrottleType() { + return mThrottleType; + } + + /** + * Indicates the type of request that the framework will make the next time it retries + * to call {@link IDataService#setupDataCall}. + * + * @return the retry type + */ + @RetryType + public int getRetryType() { + return mRetryType; + } + + /** + * Gets the time at which the throttle expires. The value is based off of + * {@link SystemClock#elapsedRealtime}. + * + * This value only applies when the throttle type is set to + * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}. + * + * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely. + * + * @return the time at which the throttle expires + */ + @ElapsedRealtimeLong + public long getThrottleExpiryTimeMillis() { + return mThrottleExpiryTimeMillis; + } + + private ApnThrottleStatus(int slotIndex, + @AccessNetworkConstants.TransportType int transportType, + @Annotation.ApnType int apnTypes, + @ThrottleType int throttleType, + long throttleExpiryTimeMillis, + @RetryType int retryType) { + mSlotIndex = slotIndex; + mTransportType = transportType; + mApnType = apnTypes; + mThrottleType = throttleType; + mThrottleExpiryTimeMillis = throttleExpiryTimeMillis; + mRetryType = retryType; + } + + private ApnThrottleStatus(@NonNull Parcel source) { + mSlotIndex = source.readInt(); + mTransportType = source.readInt(); + mApnType = source.readInt(); + mThrottleExpiryTimeMillis = source.readLong(); + mRetryType = source.readInt(); + mThrottleType = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSlotIndex); + dest.writeInt(mTransportType); + dest.writeInt(mApnType); + dest.writeLong(mThrottleExpiryTimeMillis); + dest.writeInt(mRetryType); + dest.writeInt(mThrottleType); + } + + public static final @NonNull Parcelable.Creator<ApnThrottleStatus> CREATOR = + new Parcelable.Creator<ApnThrottleStatus>() { + @Override + public ApnThrottleStatus createFromParcel(@NonNull Parcel source) { + return new ApnThrottleStatus(source); + } + + @Override + public ApnThrottleStatus[] newArray(int size) { + return new ApnThrottleStatus[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mSlotIndex, mApnType, mRetryType, mThrottleType, + mThrottleExpiryTimeMillis, mTransportType); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (obj instanceof ApnThrottleStatus) { + ApnThrottleStatus other = (ApnThrottleStatus) obj; + return this.mSlotIndex == other.mSlotIndex + && this.mApnType == other.mApnType + && this.mRetryType == other.mRetryType + && this.mThrottleType == other.mThrottleType + && this.mThrottleExpiryTimeMillis == other.mThrottleExpiryTimeMillis + && this.mTransportType == other.mTransportType; + } else { + return false; + } + } + + @Override + public String toString() { + return "ApnThrottleStatus{" + + "mSlotIndex=" + mSlotIndex + + ", mTransportType=" + mTransportType + + ", mApnType=" + ApnSetting.getApnTypeString(mApnType) + + ", mThrottleExpiryTimeMillis=" + mThrottleExpiryTimeMillis + + ", mRetryType=" + mRetryType + + ", mThrottleType=" + mThrottleType + + '}'; + } + + /** + * Provides a convenient way to set the fields of an {@link ApnThrottleStatus} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code ApnThrottleStatus}: + * + * <pre><code> + * + * DataCallResponseApnThrottleStatus = new ApnThrottleStatus.Builder() + * .setSlotIndex(1) + * .setApnType({@link ApnSetting#TYPE_EMERGENCY}) + * .setNoThrottle() + * .setRetryType({@link ApnThrottleStatus#RETRY_TYPE_NEW_CONNECTION}) + * .build(); + * </code></pre> + */ + public static final class Builder { + private int mSlotIndex; + private @AccessNetworkConstants.TransportType int mTransportType; + private @Annotation.ApnType int mApnType; + private long mThrottleExpiryTimeMillis; + private @RetryType int mRetryType; + private @ThrottleType int mThrottleType; + public static final long NO_THROTTLE_EXPIRY_TIME = + DataCallResponse.RETRY_DURATION_UNDEFINED; + + /** + * Default constructor for the Builder. + */ + public Builder() { + } + + /** + * Set the slot index. + * + * @param slotIndex the slot index. + * @return The same instance of the builder. + */ + @NonNull + public Builder setSlotIndex(int slotIndex) { + this.mSlotIndex = slotIndex; + return this; + } + + /** + * Set the transport type. + * + * @param transportType the transport type. + * @return The same instance of the builder. + */ + @NonNull + public Builder setTransportType(@AccessNetworkConstants.TransportType + int transportType) { + this.mTransportType = transportType; + return this; + } + + /** + * Set the APN type. + * + * @param apnType the APN type. + * @return The same instance of the builder. + */ + @NonNull + public Builder setApnType(@Annotation.ApnType int apnType) { + this.mApnType = apnType; + return this; + } + + /** + * Sets the time at which the throttle will expire. The value is based off of + * {@link SystemClock#elapsedRealtime}. + * + * When setting this value, the throttle type is set to + * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}. + * + * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely. + * + * @param throttleExpiryTimeMillis The elapsed time at which the throttle expires. + * Throws {@link IllegalArgumentException} for values less + * than 0. + * @return The same instance of the builder. + */ + @NonNull + public Builder setThrottleExpiryTimeMillis( + @ElapsedRealtimeLong long throttleExpiryTimeMillis) { + if (throttleExpiryTimeMillis >= 0) { + this.mThrottleExpiryTimeMillis = throttleExpiryTimeMillis; + this.mThrottleType = THROTTLE_TYPE_ELAPSED_TIME; + } else { + throw new IllegalArgumentException("throttleExpiryTimeMillis must be greater than " + + "or equal to 0"); + } + return this; + } + + /** + * Sets the status of the APN type as not being throttled. + * + * When setting this value, the throttle type is set to + * {@link ApnThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to + * {@link Builder#NO_THROTTLE_EXPIRY_TIME}. + * + * @return The same instance of the builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setNoThrottle() { + mThrottleType = THROTTLE_TYPE_NONE; + mThrottleExpiryTimeMillis = NO_THROTTLE_EXPIRY_TIME; + return this; + } + + /** + * Set the type of request that the framework will make the next time it retries + * to call {@link IDataService#setupDataCall}. + * + * @param retryType the type of request + * @return The same instance of the builder. + */ + @NonNull + public Builder setRetryType(@RetryType int retryType) { + this.mRetryType = retryType; + return this; + } + + /** + * Build the {@link ApnThrottleStatus} + * + * @return the {@link ApnThrottleStatus} object + */ + @NonNull + public ApnThrottleStatus build() { + return new ApnThrottleStatus( + mSlotIndex, + mTransportType, + mApnType, + mThrottleType, + mThrottleExpiryTimeMillis, + mRetryType); + } + } +} diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index aae77135cc58..623c4e20b3e1 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -135,6 +135,8 @@ public final class DataCallResponse implements Parcelable { private final int mMtuV6; private final @HandoverFailureMode int mHandoverFailureMode; private final int mPduSessionId; + private final Qos mDefaultQos; + private final List<QosSession> mQosSessions; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -183,6 +185,8 @@ public final class DataCallResponse implements Parcelable { mMtu = mMtuV4 = mMtuV6 = mtu; mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; mPduSessionId = PDU_SESSION_ID_NOT_SET; + mDefaultQos = null; + mQosSessions = new ArrayList<>(); } private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @@ -190,7 +194,8 @@ public final class DataCallResponse implements Parcelable { @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, - @HandoverFailureMode int handoverFailureMode, int pduSessionId) { + @HandoverFailureMode int handoverFailureMode, int pduSessionId, + @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -210,6 +215,8 @@ public final class DataCallResponse implements Parcelable { mMtuV6 = mtuV6; mHandoverFailureMode = handoverFailureMode; mPduSessionId = pduSessionId; + mDefaultQos = defaultQos; + mQosSessions = qosSessions; } /** @hide */ @@ -234,6 +241,9 @@ public final class DataCallResponse implements Parcelable { mMtuV6 = source.readInt(); mHandoverFailureMode = source.readInt(); mPduSessionId = source.readInt(); + mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); + mQosSessions = new ArrayList<>(); + source.readList(mQosSessions, QosSession.class.getClassLoader()); } /** @@ -357,6 +367,28 @@ public final class DataCallResponse implements Parcelable { return mPduSessionId; } + /** + * @return default QOS of the data call received from the network + * + * @hide + */ + + @Nullable + public Qos getDefaultQos() { + return mDefaultQos; + } + + /** + * @return All the dedicated bearer QOS sessions of the data call received from the network + * + * @hide + */ + + @NonNull + public List<QosSession> getQosSessions() { + return mQosSessions; + } + @NonNull @Override public String toString() { @@ -377,6 +409,8 @@ public final class DataCallResponse implements Parcelable { .append(" mtuV6=").append(getMtuV6()) .append(" handoverFailureMode=").append(getHandoverFailureMode()) .append(" pduSessionId=").append(getPduSessionId()) + .append(" defaultQos=").append(mDefaultQos) + .append(" qosSessions=").append(mQosSessions) .append("}"); return sb.toString(); } @@ -390,12 +424,22 @@ public final class DataCallResponse implements Parcelable { } DataCallResponse other = (DataCallResponse) o; - return this.mCause == other.mCause - && this.mSuggestedRetryTime == other.mSuggestedRetryTime - && this.mId == other.mId - && this.mLinkStatus == other.mLinkStatus - && this.mProtocolType == other.mProtocolType - && this.mInterfaceName.equals(other.mInterfaceName) + + final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ? + mDefaultQos == other.mDefaultQos : + mDefaultQos.equals(other.mDefaultQos); + + final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ? + mQosSessions == other.mQosSessions : + mQosSessions.size() == other.mQosSessions.size() + && mQosSessions.containsAll(other.mQosSessions); + + return mCause == other.mCause + && mSuggestedRetryTime == other.mSuggestedRetryTime + && mId == other.mId + && mLinkStatus == other.mLinkStatus + && mProtocolType == other.mProtocolType + && mInterfaceName.equals(other.mInterfaceName) && mAddresses.size() == other.mAddresses.size() && mAddresses.containsAll(other.mAddresses) && mDnsAddresses.size() == other.mDnsAddresses.size() @@ -408,14 +452,17 @@ public final class DataCallResponse implements Parcelable { && mMtuV4 == other.mMtuV4 && mMtuV6 == other.mMtuV6 && mHandoverFailureMode == other.mHandoverFailureMode - && mPduSessionId == other.mPduSessionId; + && mPduSessionId == other.mPduSessionId + && isQosSame + && isQosSessionsSame; } @Override public int hashCode() { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, - mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId); + mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, + mQosSessions); } @Override @@ -440,6 +487,12 @@ public final class DataCallResponse implements Parcelable { dest.writeInt(mMtuV6); dest.writeInt(mHandoverFailureMode); dest.writeInt(mPduSessionId); + if (mDefaultQos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos)mDefaultQos, flags); + } else { + dest.writeParcelable((NrQos)mDefaultQos, flags); + } + dest.writeList(mQosSessions); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -519,6 +572,10 @@ public final class DataCallResponse implements Parcelable { private int mPduSessionId = PDU_SESSION_ID_NOT_SET; + private Qos mDefaultQos; + + private List<QosSession> mQosSessions = new ArrayList<>(); + /** * Default constructor for Builder. */ @@ -713,6 +770,35 @@ public final class DataCallResponse implements Parcelable { } /** + * Set the default QOS for this data connection. + * + * @param defaultQos QOS (Quality Of Service) received from network. + * + * @return The same instance of the builder. + * + * @hide + */ + public @NonNull Builder setDefaultQos(@Nullable Qos defaultQos) { + mDefaultQos = defaultQos; + return this; + } + + /** + * Set the dedicated bearer QOS sessions for this data connection. + * + * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received + * from network. + * + * @return The same instance of the builder. + * + * @hide + */ + public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) { + mQosSessions = qosSessions; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -720,7 +806,8 @@ public final class DataCallResponse implements Parcelable { public @NonNull DataCallResponse build() { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId); + mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, + mDefaultQos, mQosSessions); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 77685971c138..2ec965101930 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -107,6 +107,9 @@ public abstract class DataService extends Service { private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11; private static final int DATA_SERVICE_REQUEST_START_HANDOVER = 12; private static final int DATA_SERVICE_REQUEST_CANCEL_HANDOVER = 13; + private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED = 14; + private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED = 15; + private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED = 16; private final HandlerThread mHandlerThread; @@ -129,6 +132,8 @@ public abstract class DataService extends Service { private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>(); + private final List<IDataServiceCallback> mApnUnthrottledCallbacks = new ArrayList<>(); + /** * Constructor * @param slotIndex SIM slot index the data service provider associated with. @@ -326,6 +331,19 @@ public abstract class DataService extends Service { } } + private void registerForApnUnthrottled(IDataServiceCallback callback) { + synchronized (mApnUnthrottledCallbacks) { + mApnUnthrottledCallbacks.add(callback); + } + } + + private void unregisterForApnUnthrottled(IDataServiceCallback callback) { + synchronized (mApnUnthrottledCallbacks) { + mApnUnthrottledCallbacks.remove(callback); + } + } + + /** * Notify the system that current data call list changed. Data service must invoke this * method whenever there is any data call status changed. @@ -343,6 +361,21 @@ public abstract class DataService extends Service { } /** + * Notify the system that a given APN was unthrottled. + * + * @param apn Access Point Name defined by the carrier. + */ + public final void notifyApnUnthrottled(@NonNull String apn) { + synchronized (mApnUnthrottledCallbacks) { + for (IDataServiceCallback callback : mApnUnthrottledCallbacks) { + mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED, + mSlotIndex, 0, new ApnUnthrottledIndication(apn, + callback)).sendToTarget(); + } + } + } + + /** * Called when the instance of data service is destroyed (e.g. got unbind or binder died) * or when the data service provider is removed. The extended class should implement this * method to perform cleanup works. @@ -429,6 +462,16 @@ public abstract class DataService extends Service { } } + private static final class ApnUnthrottledIndication { + public final String apn; + public final IDataServiceCallback callback; + ApnUnthrottledIndication(String apn, + IDataServiceCallback callback) { + this.apn = apn; + this.callback = callback; + } + } + private class DataServiceHandler extends Handler { DataServiceHandler(Looper looper) { @@ -544,6 +587,26 @@ public abstract class DataService extends Service { (cReq.callback != null) ? new DataServiceCallback(cReq.callback) : null); break; + case DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED: + if (serviceProvider == null) break; + serviceProvider.registerForApnUnthrottled((IDataServiceCallback) message.obj); + break; + case DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED: + if (serviceProvider == null) break; + callback = (IDataServiceCallback) message.obj; + serviceProvider.unregisterForApnUnthrottled(callback); + break; + case DATA_SERVICE_INDICATION_APN_UNTHROTTLED: + if (serviceProvider == null) break; + ApnUnthrottledIndication apnUnthrottledIndication = + (ApnUnthrottledIndication) message.obj; + try { + apnUnthrottledIndication.callback + .onApnUnthrottled(apnUnthrottledIndication.apn); + } catch (RemoteException e) { + loge("Failed to call onApnUnthrottled. " + e); + } + break; } } } @@ -695,6 +758,26 @@ public abstract class DataService extends Service { mHandler.obtainMessage(DATA_SERVICE_REQUEST_CANCEL_HANDOVER, slotIndex, 0, req).sendToTarget(); } + + @Override + public void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback) { + if (callback == null) { + loge("registerForUnthrottleApn: callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED, slotIndex, + 0, callback).sendToTarget(); + } + + @Override + public void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback) { + if (callback == null) { + loge("uregisterForUnthrottleApn: callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED, + slotIndex, 0, callback).sendToTarget(); + } } private void log(String s) { diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java index eef0e017f998..52bf15fd16c3 100644 --- a/telephony/java/android/telephony/data/DataServiceCallback.java +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -233,7 +233,7 @@ public class DataServiceCallback { */ @NonNull public static String resultCodeToString(@DataServiceCallback.ResultCode int resultCode) { - switch(resultCode) { + switch (resultCode) { case RESULT_SUCCESS: return "RESULT_SUCCESS"; case RESULT_ERROR_UNSUPPORTED: @@ -248,4 +248,22 @@ public class DataServiceCallback { return "Missing case for result code=" + resultCode; } } + + /** + * Indicates that the specified APN is no longer throttled. + * + * @param apn Access Point Name defined by the carrier. + */ + public void onApnUnthrottled(@NonNull String apn) { + if (mCallback != null) { + try { + if (DBG) Rlog.d(TAG, "onApnUnthrottled"); + mCallback.onApnUnthrottled(apn); + } catch (RemoteException e) { + Rlog.e(TAG, "onApnUnthrottled: remote exception", e); + } + } else { + Rlog.e(TAG, "onApnUnthrottled: callback is null!"); + } + } } diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java new file mode 100644 index 000000000000..ad43068b2f11 --- /dev/null +++ b/telephony/java/android/telephony/data/EpsQos.java @@ -0,0 +1,105 @@ +/** + * 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.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + + +/** + * Class that stores information specific to NR QOS. + * + * @hide + */ +public final class EpsQos extends Qos implements Parcelable { + + int qosClassId; + + public EpsQos() { + super(Qos.QOS_TYPE_EPS, + new android.hardware.radio.V1_6.QosBandwidth(), + new android.hardware.radio.V1_6.QosBandwidth()); + } + + public EpsQos(@NonNull android.hardware.radio.V1_6.EpsQos qos) { + super(Qos.QOS_TYPE_EPS, qos.downlink, qos.uplink); + qosClassId = qos.qci; + } + + private EpsQos(Parcel source) { + super(source); + qosClassId = source.readInt(); + } + + public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) { + return new EpsQos(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(Qos.QOS_TYPE_EPS, dest, flags); + dest.writeInt(qosClassId); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), qosClassId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof EpsQos)) { + return false; + } + + EpsQos other = (EpsQos) o; + + return this.qosClassId == other.qosClassId + && super.equals(other); + } + + @Override + public String toString() { + return "EpsQos {" + + " qosClassId=" + qosClassId + + " downlink=" + downlink + + " uplink=" + uplink + "}"; + } + + public static final @NonNull Parcelable.Creator<EpsQos> CREATOR = + new Parcelable.Creator<EpsQos>() { + @Override + public EpsQos createFromParcel(Parcel source) { + return new EpsQos(source); + } + + @Override + public EpsQos[] newArray(int size) { + return new EpsQos[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 33226feb0e35..3f1f033d6f11 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -40,4 +40,6 @@ oneway interface IDataService void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback); void startHandover(int slotId, int cid, IDataServiceCallback callback); void cancelHandover(int slotId, int cid, IDataServiceCallback callback); + void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback); + void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback); } diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl index d296e7b19be8..9cc2feac331a 100644 --- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl +++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl @@ -32,4 +32,5 @@ oneway interface IDataServiceCallback void onDataCallListChanged(in List<DataCallResponse> dataCallList); void onHandoverStarted(int result); void onHandoverCancelled(int result); + void onApnUnthrottled(in String apn); } diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl index 3bf09bc19788..2904082616e7 100644 --- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl +++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl @@ -17,6 +17,7 @@ package android.telephony.data; import android.telephony.data.IQualifiedNetworksServiceCallback; +import android.telephony.data.ApnThrottleStatus; /** * {@hide} @@ -25,4 +26,5 @@ interface IQualifiedNetworksService { oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback); oneway void removeNetworkAvailabilityProvider(int slotId); + oneway void reportApnThrottleStatusChanged(int slotId, in List<ApnThrottleStatus> statuses); } diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java new file mode 100644 index 000000000000..2011eed26977 --- /dev/null +++ b/telephony/java/android/telephony/data/NrQos.java @@ -0,0 +1,112 @@ +/** + * 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.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Class that stores information specific to NR QOS. + * + * @hide + */ +public final class NrQos extends Qos implements Parcelable { + int qosFlowId; + int fiveQi; + int averagingWindowMs; + + public NrQos(@NonNull android.hardware.radio.V1_6.NrQos qos) { + super(Qos.QOS_TYPE_NR, qos.downlink, qos.uplink); + fiveQi = qos.fiveQi; + qosFlowId = qos.qfi; + averagingWindowMs = qos.averagingWindowMs; + } + + private NrQos(Parcel source) { + super(source); + this.qosFlowId = source.readInt(); + this.fiveQi = source.readInt(); + this.averagingWindowMs = source.readInt(); + } + + public static @NonNull NrQos createFromParcelBody(@NonNull Parcel in) { + return new NrQos(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags); + dest.writeInt(qosFlowId); + dest.writeInt(fiveQi); + dest.writeInt(averagingWindowMs); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), qosFlowId, fiveQi, averagingWindowMs); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof NrQos)) { + return false; + } + + NrQos other = (NrQos) o; + + if (!super.equals(other)) { + return false; + } + + return this.qosFlowId == other.qosFlowId + && this.fiveQi == other.fiveQi + && this.averagingWindowMs == other.averagingWindowMs; + } + + @Override + public String toString() { + return "NrQos {" + + " fiveQi=" + fiveQi + + " downlink=" + downlink + + " uplink=" + uplink + + " qosFlowId=" + qosFlowId + + " averagingWindowMs=" + averagingWindowMs + "}"; + } + + public static final @NonNull Parcelable.Creator<NrQos> CREATOR = + new Parcelable.Creator<NrQos>() { + @Override + public NrQos createFromParcel(Parcel source) { + return new NrQos(source); + } + + @Override + public NrQos[] newArray(int size) { + return new NrQos[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java new file mode 100644 index 000000000000..c8bb91e28bf2 --- /dev/null +++ b/telephony/java/android/telephony/data/Qos.java @@ -0,0 +1,175 @@ +/** + * 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.telephony.data; + +import android.annotation.CallSuper; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Class that stores information specific to QOS. + * + * @hide + */ +public abstract class Qos { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_TYPE_", + value = {QOS_TYPE_EPS, QOS_TYPE_NR}) + public @interface QosType {} + + @QosType + final int type; + + static final int QOS_TYPE_EPS = 1; + static final int QOS_TYPE_NR = 2; + + final QosBandwidth downlink; + final QosBandwidth uplink; + + Qos(int type, + @NonNull android.hardware.radio.V1_6.QosBandwidth downlink, + @NonNull android.hardware.radio.V1_6.QosBandwidth uplink) { + this.type = type; + this.downlink = new QosBandwidth(downlink.maxBitrateKbps, downlink.guaranteedBitrateKbps); + this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps); + } + + static class QosBandwidth implements Parcelable { + int maxBitrateKbps; + int guaranteedBitrateKbps; + + QosBandwidth() { + } + + QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) { + this.maxBitrateKbps = maxBitrateKbps; + this.guaranteedBitrateKbps = guaranteedBitrateKbps; + } + + private QosBandwidth(Parcel source) { + maxBitrateKbps = source.readInt(); + guaranteedBitrateKbps = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(maxBitrateKbps); + dest.writeInt(guaranteedBitrateKbps); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(maxBitrateKbps, guaranteedBitrateKbps); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBandwidth)) { + return false; + } + + QosBandwidth other = (QosBandwidth) o; + return maxBitrateKbps == other.maxBitrateKbps + && guaranteedBitrateKbps == other.guaranteedBitrateKbps; + } + + @Override + public String toString() { + return "Bandwidth {" + + " maxBitrateKbps=" + maxBitrateKbps + + " guaranteedBitrateKbps=" + guaranteedBitrateKbps + "}"; + } + + public static final @NonNull Parcelable.Creator<QosBandwidth> CREATOR = + new Parcelable.Creator<QosBandwidth>() { + @Override + public QosBandwidth createFromParcel(Parcel source) { + return new QosBandwidth(source); + } + + @Override + public QosBandwidth[] newArray(int size) { + return new QosBandwidth[size]; + } + }; + }; + + protected Qos(@NonNull Parcel source) { + type = source.readInt(); + downlink = source.readParcelable(QosBandwidth.class.getClassLoader()); + uplink = source.readParcelable(QosBandwidth.class.getClassLoader()); + } + + /** + * Used by child classes for parceling. + * + * @hide + */ + @CallSuper + public void writeToParcel(@QosType int type, Parcel dest, int flags) { + dest.writeInt(type); + dest.writeParcelable(downlink, flags); + dest.writeParcelable(uplink, flags); + } + + /** @hide */ + public static @NonNull Qos create(@NonNull android.hardware.radio.V1_6.Qos qos) { + switch (qos.getDiscriminator()) { + case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps: + return new EpsQos(qos.eps()); + case android.hardware.radio.V1_6.Qos.hidl_discriminator.nr: + return new NrQos(qos.nr()); + default: + return null; + } + } + + /** @hide */ + public @QosType int getType() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(downlink, uplink); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + Qos other = (Qos) o; + return type == other.type + && downlink.equals(other.downlink) + && uplink.equals(other.uplink); + } +} diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosFilter.java new file mode 100644 index 000000000000..69277445634d --- /dev/null +++ b/telephony/java/android/telephony/data/QosFilter.java @@ -0,0 +1,373 @@ +/** + * 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.telephony.data; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.net.InetAddresses; +import android.net.LinkAddress; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores QOS filter parameters as defined in + * 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13. + * + * @hide + */ +public final class QosFilter implements Parcelable { + + private List<LinkAddress> localAddresses; + private List<LinkAddress> remoteAddresses; + private PortRange localPort; + private PortRange remotePort; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_PROTOCOL_", + value = {QOS_PROTOCOL_UNSPECIFIED, QOS_PROTOCOL_TCP, QOS_PROTOCOL_UDP, + QOS_PROTOCOL_ESP, QOS_PROTOCOL_AH}) + public @interface QosProtocol {} + + public static final int QOS_PROTOCOL_UNSPECIFIED = + android.hardware.radio.V1_6.QosProtocol.UNSPECIFIED; + public static final int QOS_PROTOCOL_TCP = android.hardware.radio.V1_6.QosProtocol.TCP; + public static final int QOS_PROTOCOL_UDP = android.hardware.radio.V1_6.QosProtocol.UDP; + public static final int QOS_PROTOCOL_ESP = android.hardware.radio.V1_6.QosProtocol.ESP; + public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH; + + @QosProtocol + private int protocol; + + private int typeOfServiceMask; + + private long flowLabel; + + /** IPSec security parameter index */ + private long securityParameterIndex; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_FILTER_DIRECTION_", + value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK, + QOS_FILTER_DIRECTION_BIDIRECTIONAL}) + public @interface QosFilterDirection {} + + public static final int QOS_FILTER_DIRECTION_DOWNLINK = + android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK; + public static final int QOS_FILTER_DIRECTION_UPLINK = + android.hardware.radio.V1_6.QosFilterDirection.UPLINK; + public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL = + android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL; + + @QosFilterDirection + private int filterDirection; + + /** + * Specified the order in which the filter needs to be matched. + * A Lower numerical value has a higher precedence. + */ + private int precedence; + + QosFilter() { + localAddresses = new ArrayList<>(); + remoteAddresses = new ArrayList<>(); + localPort = new PortRange(); + remotePort = new PortRange(); + protocol = QOS_PROTOCOL_UNSPECIFIED; + filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL; + } + + public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, + PortRange localPort, PortRange remotePort, int protocol, int tos, + long flowLabel, long spi, int direction, int precedence) { + this.localAddresses = localAddresses; + this.remoteAddresses = remoteAddresses; + this.localPort = localPort; + this.remotePort = remotePort; + this.protocol = protocol; + this.typeOfServiceMask = tos; + this.flowLabel = flowLabel; + this.securityParameterIndex = spi; + this.filterDirection = direction; + this.precedence = precedence; + } + + /** @hide */ + public static @NonNull QosFilter create( + @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) { + QosFilter ret = new QosFilter(); + + String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new); + if (localAddresses != null) { + for (String address : localAddresses) { + ret.localAddresses.add(createLinkAddressFromString(address)); + } + } + + String[] remoteAddresses = qosFilter.remoteAddresses.stream().toArray(String[]::new); + if (remoteAddresses != null) { + for (String address : remoteAddresses) { + ret.remoteAddresses.add(createLinkAddressFromString(address)); + } + } + + if (qosFilter.localPort != null) { + if (qosFilter.localPort.getDiscriminator() + == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) { + final android.hardware.radio.V1_6.PortRange portRange = qosFilter.localPort.range(); + ret.localPort.start = portRange.start; + ret.localPort.end = portRange.end; + } + } + + if (qosFilter.remotePort != null) { + if (qosFilter.remotePort.getDiscriminator() + == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) { + final android.hardware.radio.V1_6.PortRange portRange + = qosFilter.remotePort.range(); + ret.remotePort.start = portRange.start; + ret.remotePort.end = portRange.end; + } + } + + ret.protocol = qosFilter.protocol; + + if (qosFilter.tos != null) { + if (qosFilter.tos.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.TypeOfService.hidl_discriminator.value) { + ret.typeOfServiceMask = qosFilter.tos.value(); + } + } + + if (qosFilter.flowLabel != null) { + if (qosFilter.flowLabel.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.Ipv6FlowLabel.hidl_discriminator.value) { + ret.flowLabel = qosFilter.flowLabel.value(); + } + } + + if (qosFilter.spi != null) { + if (qosFilter.spi.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.IpsecSpi.hidl_discriminator.value) { + ret.securityParameterIndex = qosFilter.spi.value(); + } + } + + ret.filterDirection = qosFilter.direction; + ret.precedence = qosFilter.precedence; + + return ret; + } + + public static class PortRange implements Parcelable { + int start; + int end; + + PortRange() { + start = -1; + end = -1; + } + + private PortRange(Parcel source) { + start = source.readInt(); + end = source.readInt(); + } + + public PortRange(int start, int end) { + this.start = start; + this.end = end; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(start); + dest.writeInt(end); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<PortRange> CREATOR = + new Parcelable.Creator<PortRange>() { + @Override + public PortRange createFromParcel(Parcel source) { + return new PortRange(source); + } + + @Override + public PortRange[] newArray(int size) { + return new PortRange[size]; + } + }; + + @Override + public String toString() { + return "PortRange {" + + " start=" + start + + " end=" + end + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof PortRange)) { + return false; + } + + PortRange other = (PortRange) o; + return start == other.start + && end == other.end; + } + + @Override + public int hashCode() { + return Objects.hash(start, end); + } + }; + + @Override + public String toString() { + return "QosFilter {" + + " localAddresses=" + localAddresses + + " remoteAddresses=" + remoteAddresses + + " localPort=" + localPort + + " remotePort=" + remotePort + + " protocol=" + protocol + + " typeOfServiceMask=" + typeOfServiceMask + + " flowLabel=" + flowLabel + + " securityParameterIndex=" + securityParameterIndex + + " filterDirection=" + filterDirection + + " precedence=" + precedence + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(localAddresses, remoteAddresses, localPort, + remotePort, protocol, typeOfServiceMask, flowLabel, + securityParameterIndex, filterDirection, precedence); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosFilter)) { + return false; + } + + QosFilter other = (QosFilter) o; + + return localAddresses.size() == other.localAddresses.size() + && localAddresses.containsAll(other.localAddresses) + && remoteAddresses.size() == other.remoteAddresses.size() + && remoteAddresses.containsAll(other.remoteAddresses) + && localPort.equals(other.localPort) + && remotePort.equals(other.remotePort) + && protocol == other.protocol + && typeOfServiceMask == other.typeOfServiceMask + && flowLabel == other.flowLabel + && securityParameterIndex == other.securityParameterIndex + && filterDirection == other.filterDirection + && precedence == other.precedence; + } + + private static LinkAddress createLinkAddressFromString(String addressString) { + addressString = addressString.trim(); + InetAddress address = null; + int prefixLength = -1; + try { + String[] pieces = addressString.split("/", 2); + address = InetAddresses.parseNumericAddress(pieces[0]); + if (pieces.length == 1) { + prefixLength = (address instanceof Inet4Address) ? 32 : 128; + } else if (pieces.length == 2) { + prefixLength = Integer.parseInt(pieces[1]); + } + } catch (NullPointerException e) { // Null string. + } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. + } catch (NumberFormatException e) { // Non-numeric prefix. + } catch (IllegalArgumentException e) { // Invalid IP address. + } + + if (address == null || prefixLength == -1) { + throw new IllegalArgumentException("Invalid link address " + addressString); + } + + return new LinkAddress(address, prefixLength, 0, 0, + LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN); + } + + private QosFilter(Parcel source) { + localAddresses = new ArrayList<>(); + source.readList(localAddresses, LinkAddress.class.getClassLoader()); + remoteAddresses = new ArrayList<>(); + source.readList(remoteAddresses, LinkAddress.class.getClassLoader()); + localPort = source.readParcelable(PortRange.class.getClassLoader()); + remotePort = source.readParcelable(PortRange.class.getClassLoader()); + protocol = source.readInt(); + typeOfServiceMask = source.readInt(); + flowLabel = source.readLong(); + securityParameterIndex = source.readLong(); + filterDirection = source.readInt(); + precedence = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(localAddresses); + dest.writeList(remoteAddresses); + dest.writeParcelable(localPort, flags); + dest.writeParcelable(remotePort, flags); + dest.writeInt(protocol); + dest.writeInt(typeOfServiceMask); + dest.writeLong(flowLabel); + dest.writeLong(securityParameterIndex); + dest.writeInt(filterDirection); + dest.writeInt(precedence); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<QosFilter> CREATOR = + new Parcelable.Creator<QosFilter>() { + @Override + public QosFilter createFromParcel(Parcel source) { + return new QosFilter(source); + } + + @Override + public QosFilter[] newArray(int size) { + return new QosFilter[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java new file mode 100644 index 000000000000..f07b6a9f6725 --- /dev/null +++ b/telephony/java/android/telephony/data/QosSession.java @@ -0,0 +1,125 @@ +/** + * 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.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores information specific to QOS session. + * + * @hide + */ +public final class QosSession implements Parcelable{ + + final int qosSessionId; + final Qos qos; + final List<QosFilter> qosFilterList; + + public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) { + this.qosSessionId = qosSessionId; + this.qos = qos; + this.qosFilterList = qosFilterList; + } + + private QosSession(Parcel source) { + qosSessionId = source.readInt(); + qos = source.readParcelable(Qos.class.getClassLoader()); + qosFilterList = new ArrayList<>(); + source.readList(qosFilterList, QosFilter.class.getClassLoader()); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(qosSessionId); + if (qos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos)qos, flags); + } else { + dest.writeParcelable((NrQos)qos, flags); + } + dest.writeList(qosFilterList); + } + + public static @NonNull QosSession create( + @NonNull android.hardware.radio.V1_6.QosSession qosSession) { + List<QosFilter> qosFilters = new ArrayList<>(); + + if (qosSession.qosFilters != null) { + for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { + qosFilters.add(QosFilter.create(filter)); + } + } + + return new QosSession( + qosSession.qosSessionId, + Qos.create(qosSession.qos), + qosFilters); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "QosSession {" + + " qosSessionId=" + qosSessionId + + " qos=" + qos + + " qosFilterList=" + qosFilterList + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(qosSessionId, qos, qosFilterList); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosSession)) { + return false; + } + + QosSession other = (QosSession) o; + return this.qosSessionId == other.qosSessionId + && this.qos.equals(other.qos) + && this.qosFilterList.size() == other.qosFilterList.size() + && this.qosFilterList.containsAll(other.qosFilterList); + } + + + public static final @NonNull Parcelable.Creator<QosSession> CREATOR = + new Parcelable.Creator<QosSession>() { + @Override + public QosSession createFromParcel(Parcel source) { + return new QosSession(source); + } + + @Override + public QosSession[] newArray(int size) { + return new QosSession[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java index 05971c4d2e70..4af63b4cf981 100644 --- a/telephony/java/android/telephony/data/QualifiedNetworksService.java +++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java @@ -28,6 +28,7 @@ import android.os.Message; import android.os.RemoteException; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.Annotation.ApnType; +import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -65,6 +66,7 @@ public abstract class QualifiedNetworksService extends Service { private static final int QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER = 2; private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS = 3; private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4; + private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5; private final HandlerThread mHandlerThread; @@ -160,6 +162,17 @@ public abstract class QualifiedNetworksService extends Service { } /** + * The framework calls this method when the throttle status of an APN changes. + * + * This method is meant to be overridden. + * + * @param statuses the statuses that have changed + */ + public void reportApnThrottleStatusChanged(@NonNull List<ApnThrottleStatus> statuses) { + Log.d(TAG, "reportApnThrottleStatusChanged: statuses size=" + statuses.size()); + } + + /** * Called when the qualified networks provider is removed. The extended class should * implement this method to perform cleanup works. */ @@ -197,6 +210,12 @@ public abstract class QualifiedNetworksService extends Service { + slotIndex); } break; + case QNS_APN_THROTTLE_STATUS_CHANGED: + if (provider != null) { + List<ApnThrottleStatus> statuses = (List<ApnThrottleStatus>) message.obj; + provider.reportApnThrottleStatusChanged(statuses); + } + break; case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER: if (provider != null) { @@ -286,6 +305,13 @@ public abstract class QualifiedNetworksService extends Service { mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0) .sendToTarget(); } + + @Override + public void reportApnThrottleStatusChanged(int slotIndex, + List<ApnThrottleStatus> statuses) { + mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses) + .sendToTarget(); + } } private void log(String s) { diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 76fc4f7d0519..6fbde503c3a0 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -113,6 +113,7 @@ public class DctConstants { public static final int EVENT_NR_TIMER_WATCHDOG = BASE + 53; public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 54; public static final int EVENT_SIM_STATE_UPDATED = BASE + 55; + public static final int EVENT_APN_UNTHROTTLED = BASE + 56; /***** Constants *****/ diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index dba1856ea6d0..70f6386aa891 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -200,7 +200,8 @@ class ConnectivityServiceIntegrationTest { nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204)) nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204)) - val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context) + val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */, + context) networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) na.addCapability(NET_CAPABILITY_INTERNET) @@ -238,7 +239,7 @@ class ConnectivityServiceIntegrationTest { val lp = LinkProperties() lp.captivePortalApiUrl = Uri.parse(apiUrl) - val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context) + val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context) networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) na.addCapability(NET_CAPABILITY_INTERNET) diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 85704d033634..2a24d1ac22d2 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -72,12 +72,12 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { private long mKeepaliveResponseDelay = 0L; private Integer mExpectedKeepaliveSlot = null; - public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context) - throws Exception { + public NetworkAgentWrapper(int transport, LinkProperties linkProperties, + NetworkCapabilities ncTemplate, Context context) throws Exception { final int type = transportToLegacyType(transport); final String typeName = ConnectivityManager.getNetworkTypeName(type); mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); - mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mNetworkCapabilities.addTransportType(transport); switch (transport) { diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt index eb290dc7d24a..938a694e8ba9 100644 --- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt +++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt @@ -35,4 +35,5 @@ class TestNetIdManager : NetIdManager() { private val nextId = AtomicInteger(MAX_NET_ID) override fun reserveNetId() = nextId.decrementAndGet() override fun releaseNetId(id: Int) = Unit + fun peekNextNetId() = nextId.get() - 1 } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b78f0e237bc5..4bb13172335b 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -322,6 +322,7 @@ public class ConnectivityServiceTest { private static final String MOBILE_IFNAME = "test_rmnet_data0"; private static final String WIFI_IFNAME = "test_wlan0"; private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; + private static final String VPN_IFNAME = "tun10042"; private static final String TEST_PACKAGE_NAME = "com.android.test.package"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -339,6 +340,7 @@ public class ConnectivityServiceTest { private INetworkPolicyListener mPolicyListener; private WrappedMultinetworkPolicyTracker mPolicyTracker; private HandlerThread mAlarmManagerThread; + private TestNetIdManager mNetIdManager; @Mock IIpConnectivityMetrics mIpConnectivityMetrics; @Mock IpConnectivityMetrics.Logger mMetricsService; @@ -617,12 +619,17 @@ public class ConnectivityServiceTest { private String mRedirectUrl; TestNetworkAgentWrapper(int transport) throws Exception { - this(transport, new LinkProperties()); + this(transport, new LinkProperties(), null); } TestNetworkAgentWrapper(int transport, LinkProperties linkProperties) throws Exception { - super(transport, linkProperties, mServiceContext); + this(transport, linkProperties, null); + } + + private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties, + NetworkCapabilities ncTemplate) throws Exception { + super(transport, linkProperties, ncTemplate, mServiceContext); // Waits for the NetworkAgent to be registered, which includes the creation of the // NetworkMonitor. @@ -1017,46 +1024,36 @@ public class ConnectivityServiceTest { } } + private Set<UidRange> uidRangesForUid(int uid) { + final ArraySet<UidRange> ranges = new ArraySet<>(); + ranges.add(new UidRange(uid, uid)); + return ranges; + } + private static Looper startHandlerThreadAndReturnLooper() { final HandlerThread handlerThread = new HandlerThread("MockVpnThread"); handlerThread.start(); return handlerThread.getLooper(); } - private class MockVpn extends Vpn { - // TODO : the interactions between this mock and the mock network agent are too - // hard to get right at this moment, because it's unclear in which case which - // target needs to get a method call or both, and in what order. It's because - // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn - // parent class of MockVpn agent wants that responsibility. - // That being said inside the test it should be possible to make the interactions - // harder to get wrong with precise speccing, judicious comments, helper methods - // and a few sprinkled assertions. - - private boolean mConnected = false; + private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork { // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does // not inherit from NetworkAgent. private TestNetworkAgentWrapper mMockNetworkAgent; - private int mVpnType = VpnManager.TYPE_VPN_SERVICE; + private boolean mAgentRegistered = false; + private int mVpnType = VpnManager.TYPE_VPN_SERVICE; private VpnInfo mVpnInfo; - private Network[] mUnderlyingNetworks; public MockVpn(int userId) { super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService, userId, mock(KeyStore.class)); - } - - public void setNetworkAgent(TestNetworkAgentWrapper agent) { - agent.waitForIdle(TIMEOUT_MS); - mMockNetworkAgent = agent; - mNetworkAgent = agent.getNetworkAgent(); - mNetworkCapabilities.set(agent.getNetworkCapabilities()); + mConfig = new VpnConfig(); } public void setUids(Set<UidRange> uids) { mNetworkCapabilities.setUids(uids); - updateCapabilities(null /* defaultNetwork */); + updateCapabilitiesInternal(null /* defaultNetwork */, true); } public void setVpnType(int vpnType) { @@ -1064,21 +1061,13 @@ public class ConnectivityServiceTest { } @Override - public int getNetId() { - if (mMockNetworkAgent == null) { - return NETID_UNSET; - } - return mMockNetworkAgent.getNetwork().netId; - } - - @Override - public boolean appliesToUid(int uid) { - return mConnected; // Trickery to simplify testing. + public Network getNetwork() { + return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork(); } @Override - protected boolean isCallerEstablishedOwnerLocked() { - return mConnected; // Similar trickery + public int getNetId() { + return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId; } @Override @@ -1086,41 +1075,94 @@ public class ConnectivityServiceTest { return mVpnType; } - private void connect(boolean isAlwaysMetered) { - mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); - mConnected = true; - mConfig = new VpnConfig(); + private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp) + throws Exception { + if (mAgentRegistered) throw new IllegalStateException("already registered"); + setUids(uids); mConfig.isMetered = isAlwaysMetered; + mInterface = VPN_IFNAME; + mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, + mNetworkCapabilities); + mMockNetworkAgent.waitForIdle(TIMEOUT_MS); + mAgentRegistered = true; + mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); + mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); } - public void connectAsAlwaysMetered() { - connect(true /* isAlwaysMetered */); + private void registerAgent(Set<UidRange> uids) throws Exception { + registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties()); } - public void connect() { - connect(false /* isAlwaysMetered */); + private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { + mMockNetworkAgent.connect(validated, hasInternet, isStrictMode); } - @Override - public NetworkCapabilities updateCapabilities(Network defaultNetwork) { - if (!mConnected) return null; + private void connect(boolean validated) { + mMockNetworkAgent.connect(validated); + } + + private TestNetworkAgentWrapper getAgent() { + return mMockNetworkAgent; + } + + public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated, + boolean hasInternet, boolean isStrictMode) throws Exception { + mNetworkCapabilities.setOwnerUid(uid); + mNetworkCapabilities.setAdministratorUids(new int[]{uid}); + registerAgent(false, ranges, lp); + connect(validated, hasInternet, isStrictMode); + waitForIdle(); + } + + public void establish(LinkProperties lp, int uid, Set<UidRange> ranges) throws Exception { + establish(lp, uid, ranges, true, true, false); + } + + public void establishForMyUid(LinkProperties lp) throws Exception { + final int uid = Process.myUid(); + establish(lp, uid, uidRangesForUid(uid), true, true, false); + } + + public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode) + throws Exception { + final int uid = Process.myUid(); + establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet, + isStrictMode); + } + + public void establishForMyUid() throws Exception { + establishForMyUid(new LinkProperties()); + } + + public void sendLinkProperties(LinkProperties lp) { + mMockNetworkAgent.sendLinkProperties(lp); + } + + private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork, + boolean sendToConnectivityService) { + if (!mAgentRegistered) return null; super.updateCapabilities(defaultNetwork); // Because super.updateCapabilities will update the capabilities of the agent but // not the mock agent, the mock agent needs to know about them. - copyCapabilitiesToNetworkAgent(); + copyCapabilitiesToNetworkAgent(sendToConnectivityService); return new NetworkCapabilities(mNetworkCapabilities); } - private void copyCapabilitiesToNetworkAgent() { + private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) { if (null != mMockNetworkAgent) { mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, - false /* sendToConnectivityService */); + sendToConnectivityService); } } + @Override + public NetworkCapabilities updateCapabilities(Network defaultNetwork) { + return updateCapabilitiesInternal(defaultNetwork, false); + } + public void disconnect() { - mConnected = false; - mConfig = null; + if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect(); + mAgentRegistered = false; } @Override @@ -1133,18 +1175,6 @@ public class ConnectivityServiceTest { private synchronized void setVpnInfo(VpnInfo vpnInfo) { mVpnInfo = vpnInfo; } - - @Override - public synchronized Network[] getUnderlyingNetworks() { - if (mUnderlyingNetworks != null) return mUnderlyingNetworks; - - return super.getUnderlyingNetworks(); - } - - /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */ - private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) { - mUnderlyingNetworks = underlyingNetworks; - } } private void mockVpn(int uid) { @@ -1207,6 +1237,8 @@ public class ConnectivityServiceTest { @Before public void setUp() throws Exception { + mNetIdManager = new TestNetIdManager(); + mContext = InstrumentationRegistry.getContext(); MockitoAnnotations.initMocks(this); @@ -1277,7 +1309,7 @@ public class ConnectivityServiceTest { doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt()); final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class); doReturn(mCsHandlerThread).when(deps).makeHandlerThread(); - doReturn(new TestNetIdManager()).when(deps).makeNetIdManager(); + doReturn(mNetIdManager).when(deps).makeNetIdManager(); doReturn(mNetworkStack).when(deps).getNetworkStack(); doReturn(mSystemProperties).when(deps).getSystemProperties(); doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); @@ -1335,6 +1367,9 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.disconnect(); mEthernetNetworkAgent = null; } + mMockVpn.disconnect(); + waitForIdle(); + FakeSettingsProvider.clearSettingsProvider(); mCsHandlerThread.quitSafely(); @@ -3218,20 +3253,12 @@ public class ConnectivityServiceTest { waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); - final int uid = Process.myUid(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); + mMockVpn.establishForMyUid(); + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - vpnNetworkAgent.disconnect(); - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + mMockVpn.disconnect(); + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); } @@ -4808,13 +4835,52 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } + private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) { + // Easier to implement than a proper "assertSameElements" method that also correctly deals + // with duplicates. + final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual); + assertEquals(msg, expected.length, actual.length); + Set expectedSet = new ArraySet<>(Arrays.asList(expected)); + assertEquals("expected contains duplicates", expectedSet.size(), expected.length); + // actual cannot have duplicates because it's the same length and has the same elements. + Set actualSet = new ArraySet<>(Arrays.asList(actual)); + assertEquals(expectedSet, actualSet); + } + + private void expectForceUpdateIfaces(Network[] networks, String defaultIface, + Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { + ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class); + ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class); + + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(), + any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture()); + + assertSameElementsNoDuplicates(networksCaptor.getValue(), networks); + + VpnInfo[] infos = vpnInfosCaptor.getValue(); + if (vpnUid != null) { + assertEquals("Should have exactly one VPN:", 1, infos.length); + VpnInfo info = infos[0]; + assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid); + assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface); + assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces); + } else { + assertEquals(0, infos.length); + return; + } + } + + private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception { + expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]); + } + @Test public void testStatsIfacesChanged() throws Exception { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; - Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; + final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; + final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; LinkProperties cellLp = new LinkProperties(); cellLp.setInterfaceName(MOBILE_IFNAME); @@ -4825,9 +4891,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); // Default network switch should update ifaces. @@ -4835,32 +4899,24 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.sendLinkProperties(wifiLp); waitForIdle(); assertEquals(wifiLp, mService.getActiveLinkProperties()); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME); reset(mStatsService); // Disconnect should update ifaces. mWiFiNetworkAgent.disconnect(); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), - eq(MOBILE_IFNAME), eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); // Metered change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); // Captive portal change shouldn't update ifaces @@ -4874,9 +4930,102 @@ public class ConnectivityServiceTest { // Roaming change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); + reset(mStatsService); + + // Test VPNs. + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(VPN_IFNAME); + + mMockVpn.establishForMyUid(lp); + + final Network[] cellAndVpn = new Network[] { + mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + Network[] cellAndWifi = new Network[] { + mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()}; + + // A VPN with default (null) underlying networks sets the underlying network's interfaces... + expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME}); + + // ...and updates them as the default network switches. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + final Network[] wifiAndVpn = new Network[] { + mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + cellAndWifi = new Network[] { + mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()}; + + waitForIdle(); + assertEquals(wifiLp, mService.getActiveLinkProperties()); + expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{WIFI_IFNAME}); + reset(mStatsService); + + // A VPN that sets its underlying networks passes the underlying interfaces, and influences + // the default interface sent to NetworkStatsService by virtue of applying to the system + // server UID (or, in this test, to the test's UID). This is the reason for sending + // MOBILE_IFNAME even though the default network is wifi. + // TODO: fix this to pass in the actual default network interface. Whether or not the VPN + // applies to the system server UID should not have any bearing on network stats. + mService.setUnderlyingNetworksForVpn(onlyCell); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME}); + reset(mStatsService); + + mService.setUnderlyingNetworksForVpn(cellAndWifi); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + reset(mStatsService); + + // If an underlying network disconnects, that interface should no longer be underlying. + // This doesn't actually work because disconnectAndDestroyNetwork only notifies + // NetworkStatsService before the underlying network is actually removed. So the underlying + // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This + // could result in incorrect data usage measurements if the interface used by the + // disconnected network is reused by a system component that does not register an agent for + // it (e.g., tethering). + mCellNetworkAgent.disconnect(); + waitForIdle(); + assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork())); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + + // Confirm that we never tell NetworkStatsService that cell is no longer the underlying + // network for the VPN... + verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class), + any(NetworkState[].class), any() /* anyString() doesn't match null */, + argThat(infos -> infos[0].underlyingIfaces.length == 1 + && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0]))); + verifyNoMoreInteractions(mStatsService); + reset(mStatsService); + + // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be + // called again, it does. For example, connect Ethernet, but with a low score, such that it + // does not become the default network. + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.adjustScore(-40); + mEthernetNetworkAgent.connect(false); + waitForIdle(); + verify(mStatsService).forceUpdateIfaces(any(Network[].class), + any(NetworkState[].class), any() /* anyString() doesn't match null */, + argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1 + && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0]))); + mEthernetNetworkAgent.disconnect(); + reset(mStatsService); + + // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo + // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes + // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which + // is probably a performance improvement (though it's very unlikely that a VPN would declare + // no underlying networks). + // Also, for the same reason as above, the active interface passed in is null. + mService.setUnderlyingNetworksForVpn(new Network[0]); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); } @@ -5232,6 +5381,58 @@ public class ConnectivityServiceTest { } @Test + public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN).build(); + + mCm.registerNetworkCallback(request, callback); + + // Bring up a VPN that specifies an underlying network that does not exist yet. + // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet, + // (and doing so is difficult without using reflection) but it's good to test that the code + // behaves approximately correctly. + mMockVpn.establishForMyUid(false, true, false); + final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); + mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork}); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_VPN)); + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_WIFI)); + + // Make that underlying network connect, and expect to see its capabilities immediately + // reflected in the VPN's capabilities. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork()); + mWiFiNetworkAgent.connect(false); + // TODO: the callback for the VPN happens before any callbacks are called for the wifi + // network that has just connected. There appear to be two issues here: + // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for + // it returns non-null (which happens very early, during handleRegisterNetworkAgent). + // This is not correct because that that point the network is not connected and cannot + // pass any traffic. + // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities + // before rematching networks. + // Given that this scenario can't really happen, this is probably fine for now. + callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_VPN)); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_WIFI)); + + // Disconnect the network, and expect to see the VPN capabilities change accordingly. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (nc) -> + nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN)); + + mMockVpn.disconnect(); + mCm.unregisterNetworkCallback(callback); + } + + @Test public void testVpnNetworkActive() throws Exception { final int uid = Process.myUid(); @@ -5265,42 +5466,38 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); + final Set<UidRange> ranges = uidRangesForUid(uid); + mMockVpn.registerAgent(ranges); + // VPN networks do not satisfy the default request and are automatically validated // by NetworkMonitor assertFalse(NetworkMonitorUtils.isValidationRequired( - vpnNetworkAgent.getNetworkCapabilities())); - vpnNetworkAgent.setNetworkValid(false /* isStrictMode */); + mMockVpn.getAgent().getNetworkCapabilities())); + mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); - vpnNetworkAgent.connect(false); - mMockVpn.connect(); - mMockVpn.setUnderlyingNetworks(new Network[0]); + mMockVpn.connect(false); + mService.setUnderlyingNetworksForVpn(new Network[0]); - genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + genericNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + vpnNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn); + defaultCallback.expectAvailableCallbacksUnvalidated(mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids()); - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, nc -> null == nc.getUids()); + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); ranges.clear(); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setUids(ranges); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); // TODO : The default network callback should actually get a LOST call here (also see the // comment below for AVAILABLE). This is because ConnectivityService does not look at UID @@ -5308,19 +5505,18 @@ public class ConnectivityServiceTest { // can't currently update their UIDs without disconnecting, so this does not matter too // much, but that is the reason the test here has to check for an update to the // capabilities instead of the expected LOST then AVAILABLE. - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); ranges.add(new UidRange(uid, uid)); mMockVpn.setUids(ranges); - vpnNetworkAgent.setUids(ranges); - genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); + genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); + vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); // TODO : Here like above, AVAILABLE would be correct, but because this can't actually // happen outside of the test, ConnectivityService does not rematch callbacks. - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); mWiFiNetworkAgent.disconnect(); @@ -5330,13 +5526,13 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); defaultCallback.assertNoCallback(); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); assertEquals(null, mCm.getActiveNetwork()); mCm.unregisterNetworkCallback(genericNetworkCallback); @@ -5358,20 +5554,13 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - mMockVpn.connect(); defaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); defaultCallback.assertNoCallback(); mCm.unregisterNetworkCallback(defaultCallback); @@ -5390,21 +5579,14 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */, false /* isStrictMode */); - mMockVpn.connect(); - defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - vpnNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + mMockVpn.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); mCm.unregisterNetworkCallback(defaultCallback); @@ -5422,44 +5604,36 @@ public class ConnectivityServiceTest { callback.assertNoCallback(); // Bring up a VPN that has the INTERNET capability, initially unvalidated. - final int uid = Process.myUid(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */, + mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, false /* isStrictMode */); - mMockVpn.connect(); // Even though the VPN is unvalidated, it becomes the default network for our app. - callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); callback.assertNoCallback(); - assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore()); - assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore()); - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore()); + assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)); assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET)); assertFalse(NetworkMonitorUtils.isValidationRequired( - vpnNetworkAgent.getNetworkCapabilities())); + mMockVpn.getAgent().getNetworkCapabilities())); assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired( - vpnNetworkAgent.getNetworkCapabilities())); + mMockVpn.getAgent().getNetworkCapabilities())); // Pretend that the VPN network validates. - vpnNetworkAgent.setNetworkValid(false /* isStrictMode */); - vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); + mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid()); // Expect to see the validated capability, but no other changes, because the VPN is already // the default network for the app. - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn); callback.assertNoCallback(); - vpnNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + mMockVpn.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent); } @@ -5481,21 +5655,15 @@ public class ConnectivityServiceTest { mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mCellNetworkAgent.connect(true); - final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(), + vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(), false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS, nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)); - final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertTrue(nc.hasTransport(TRANSPORT_VPN)); assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); @@ -5517,18 +5685,11 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); vpnNetworkCallback.assertNoCallback(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); - nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertTrue(nc.hasTransport(TRANSPORT_VPN)); assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); @@ -5545,7 +5706,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5559,7 +5720,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5569,7 +5730,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5577,27 +5738,27 @@ public class ConnectivityServiceTest { // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended. mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); // Add NOT_SUSPENDED again and observe VPN is no longer suspended. mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); // Use Wifi but not cell. Note the VPN is now unmetered and not suspended. mService.setUnderlyingNetworksForVpn( new Network[] { mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5607,7 +5768,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5620,7 +5781,7 @@ public class ConnectivityServiceTest { // Stop using WiFi. The VPN is suspended again. mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5634,7 +5795,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5645,14 +5806,14 @@ public class ConnectivityServiceTest { // Disconnect cell. Receive update without even removing the dead network from the // underlying networks – it's dead anyway. Not metered any more. mCellNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); // Disconnect wifi too. No underlying networks means this is now metered. mWiFiNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5673,18 +5834,11 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); vpnNetworkCallback.assertNoCallback(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); - nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertTrue(nc.hasTransport(TRANSPORT_VPN)); assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); @@ -5696,7 +5850,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5706,7 +5860,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5718,7 +5872,7 @@ public class ConnectivityServiceTest { // Disconnect wifi too. Now we have no default network. mWiFiNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5761,18 +5915,10 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // Connect VPN network. By default it is using current default network (Cell). - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - final int uid = Process.myUid(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - waitForIdle(); + mMockVpn.establishForMyUid(); + // Ensure VPN is now the active network. - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // Expect VPN to be metered. assertTrue(mCm.isActiveNetworkMetered()); @@ -5783,7 +5929,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.connect(true); waitForIdle(); // VPN should still be the active network. - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // Expect VPN to be unmetered as it should now be using WiFi (new default). assertFalse(mCm.isActiveNetworkMetered()); @@ -5801,7 +5947,6 @@ public class ConnectivityServiceTest { // VPN without any underlying networks is treated as metered. assertTrue(mCm.isActiveNetworkMetered()); - vpnNetworkAgent.disconnect(); mMockVpn.disconnect(); } @@ -5822,18 +5967,10 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - final int uid = Process.myUid(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - waitForIdle(); + mMockVpn.establishForMyUid(); + // Ensure VPN is now the active network. - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is using Cell mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); @@ -5873,7 +6010,6 @@ public class ConnectivityServiceTest { // VPN without underlying networks is treated as metered. assertTrue(mCm.isActiveNetworkMetered()); - vpnNetworkAgent.disconnect(); mMockVpn.disconnect(); } @@ -5888,17 +6024,11 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - final int uid = Process.myUid(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connectAsAlwaysMetered(); + mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()), + new LinkProperties()); + mMockVpn.connect(true); waitForIdle(); - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is tracking current platform default (WiFi). mService.setUnderlyingNetworksForVpn(null); @@ -5922,7 +6052,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); } @Test @@ -6654,34 +6784,21 @@ public class ConnectivityServiceTest { waitForIdle(); assertNull(mService.getProxyForNetwork(null)); - // Set up a VPN network with a proxy - final int uid = Process.myUid(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setUids(ranges); + // Connect a VPN network with a proxy. LinkProperties testLinkProperties = new LinkProperties(); testLinkProperties.setHttpProxy(testProxyInfo); - vpnNetworkAgent.sendLinkProperties(testLinkProperties); - waitForIdle(); - - // Connect to VPN with proxy - mMockVpn.setNetworkAgent(vpnNetworkAgent); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - waitForIdle(); + mMockVpn.establishForMyUid(testLinkProperties); // Test that the VPN network returns a proxy, and the WiFi does not. - assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork())); + assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork())); assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); // Test that the VPN network returns no proxy when it is set to null. testLinkProperties.setHttpProxy(null); - vpnNetworkAgent.sendLinkProperties(testLinkProperties); + mMockVpn.sendLinkProperties(testLinkProperties); waitForIdle(); - assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork())); + assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork())); assertNull(mService.getProxyForNetwork(null)); // Set WiFi proxy and check that the vpn proxy is still null. @@ -6692,7 +6809,7 @@ public class ConnectivityServiceTest { // Disconnect from VPN and check that the active network, which is now the WiFi, has the // correct proxy setting. - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); waitForIdle(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); @@ -6707,7 +6824,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRange); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -6717,7 +6834,7 @@ public class ConnectivityServiceTest { assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange)); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); waitForIdle(); // Disconnected VPN should have interface rules removed @@ -6734,8 +6851,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn( - lp, Process.SYSTEM_UID, vpnRange); + mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); // Legacy VPN should not have interface rules set up verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -6750,8 +6866,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn( - lp, Process.SYSTEM_UID, vpnRange); + mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); // IPv6 unreachable route should not be misinterpreted as a default route verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -6765,7 +6880,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRange); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -6777,7 +6892,7 @@ public class ConnectivityServiceTest { reset(mMockNetd); InOrder inOrder = inOrder(mMockNetd); lp.setInterfaceName("tun1"); - vpnNetworkAgent.sendLinkProperties(lp); + mMockVpn.sendLinkProperties(lp); waitForIdle(); // VPN handover (switch to a new interface) should result in rules being updated (old rules // removed first, then new rules added) @@ -6790,7 +6905,7 @@ public class ConnectivityServiceTest { lp = new LinkProperties(); lp.setInterfaceName("tun1"); lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1")); - vpnNetworkAgent.sendLinkProperties(lp); + mMockVpn.sendLinkProperties(lp); waitForIdle(); // VPN not routing everything should no longer have interface filtering rules verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); @@ -6801,7 +6916,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName("tun1"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - vpnNetworkAgent.sendLinkProperties(lp); + mMockVpn.sendLinkProperties(lp); waitForIdle(); // Back to routing all IPv6 traffic should have filtering rules verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); @@ -6816,8 +6931,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final UidRange vpnRange = UidRange.createForUser(VPN_USER); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, - Collections.singleton(vpnRange)); + mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange)); reset(mMockNetd); InOrder inOrder = inOrder(mMockNetd); @@ -6826,7 +6940,7 @@ public class ConnectivityServiceTest { final Set<UidRange> newRanges = new HashSet<>(Arrays.asList( new UidRange(vpnRange.start, APP1_UID - 1), new UidRange(APP1_UID + 1, vpnRange.stop))); - vpnNetworkAgent.setUids(newRanges); + mMockVpn.setUids(newRanges); waitForIdle(); ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); @@ -6967,7 +7081,7 @@ public class ConnectivityServiceTest { private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange); + mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); mMockVpn.setVpnType(vpnType); final VpnInfo vpnInfo = new VpnInfo(); @@ -7048,19 +7162,6 @@ public class ConnectivityServiceTest { mService.getConnectionOwnerUid(getTestConnectionInfo()); } - private TestNetworkAgentWrapper establishVpn( - LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception { - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp); - vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(vpnRange); - vpnNetworkAgent.connect(true); - waitForIdle(); - return vpnNetworkAgent; - } - private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) { final PackageInfo packageInfo = new PackageInfo(); if (hasSystemPermission) { @@ -7240,22 +7341,28 @@ public class ConnectivityServiceTest { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be // active final VpnInfo info = new VpnInfo(); info.ownerUid = Process.myUid(); - info.vpnIface = "interface"; + info.vpnIface = VPN_IFNAME; mMockVpn.setVpnInfo(info); - mMockVpn.overrideUnderlyingNetworks(new Network[] {network}); + + mMockVpn.establishForMyUid(); + waitForIdle(); + + mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + + + assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network})); assertTrue( "Active VPN permission not applied", mService.checkConnectivityDiagnosticsPermissions( Process.myPid(), Process.myUid(), naiWithoutUid, mContext.getOpPackageName())); - mMockVpn.overrideUnderlyingNetworks(null); + assertTrue(mService.setUnderlyingNetworksForVpn(null)); assertFalse( "VPN shouldn't receive callback on non-underlying network", mService.checkConnectivityDiagnosticsPermissions( @@ -7276,8 +7383,6 @@ public class ConnectivityServiceTest { Manifest.permission.ACCESS_FINE_LOCATION); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested - mMockVpn.disconnect(); assertTrue( "NetworkCapabilities administrator uid permission not applied", mService.checkConnectivityDiagnosticsPermissions( diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index d235c80220ca..dc96df67e866 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -89,8 +89,6 @@ rule android.util.BackupUtils* com.android.wifi.x.@0 rule android.util.LocalLog* com.android.wifi.x.@0 rule android.util.Rational* com.android.wifi.x.@0 -rule android.os.BasicShellCommandHandler* com.android.wifi.x.@0 - # Use our statically linked bouncy castle library rule org.bouncycastle.** com.android.wifi.x.@0 # Use our statically linked protobuf library |