diff options
146 files changed, 4105 insertions, 999 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 088cadba89ab..8a9c774117d3 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -896,7 +896,7 @@ public class JobInfo implements Parcelable { * @param flags Flags for the observer. */ public TriggerContentUri(@NonNull Uri uri, @Flags int flags) { - mUri = uri; + mUri = Objects.requireNonNull(uri); mFlags = flags; } diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java index 1072406d26cc..7833a037463c 100644 --- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java @@ -16,6 +16,7 @@ package com.android.server.job; +import android.annotation.NonNull; import android.app.job.JobInfo; import android.util.proto.ProtoOutputStream; @@ -44,6 +45,10 @@ public interface JobSchedulerInternal { void removeBackingUpUid(int uid); void clearAllBackingUpUids(); + /** Returns the package responsible for backing up media on the device. */ + @NonNull + String getMediaBackupPackage(); + /** * The user has started interacting with the app. Take any appropriate action. */ 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 e4c6b52f94bb..ff7944d07310 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -77,6 +77,7 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.ArrayUtils; @@ -248,6 +249,9 @@ public class JobSchedulerService extends com.android.server.SystemService */ private final List<JobRestriction> mJobRestrictions; + @NonNull + private final String mSystemGalleryPackage; + private final CountQuotaTracker mQuotaTracker; private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; @@ -1394,6 +1398,9 @@ public class JobSchedulerService extends com.android.server.SystemService mJobRestrictions = new ArrayList<>(); mJobRestrictions.add(new ThermalStatusRestriction(this)); + mSystemGalleryPackage = Objects.requireNonNull( + context.getString(R.string.config_systemGallery)); + // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. if (!mJobs.jobTimesInflatedValid()) { @@ -2359,6 +2366,11 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override + public String getMediaBackupPackage() { + return mSystemGalleryPackage; + } + + @Override public void reportAppUsage(String packageName, int userId) { JobSchedulerService.this.reportAppUsage(packageName, userId); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java index a775cf5a671c..5fcd774189ac 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java @@ -344,7 +344,7 @@ public final class ContentObserverController extends StateController { mContext.getContentResolver().unregisterContentObserver(obs); ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser = mObservers.get(obs.mUserId); - if (observerOfUser != null) { + if (observerOfUser != null) { observerOfUser.remove(obs.mUri); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 1e89158ca4bb..cf7f3804b34d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; +import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.AppGlobals; @@ -30,6 +31,7 @@ import android.net.Network; import android.net.Uri; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.MediaStore; import android.text.format.DateFormat; import android.util.ArraySet; import android.util.Pair; @@ -207,6 +209,18 @@ public final class JobStatus { */ private int mDynamicConstraints = 0; + /** + * Indicates whether the job is responsible for backing up media, so we can be lenient in + * applying standby throttling. + * + * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or + * network changes, in which case this exemption does not make sense. + * + * TODO(b/149519887): Use a more explicit signal, maybe an API flag, that the scheduling package + * needs to provide at the time of scheduling a job. + */ + private final boolean mHasMediaBackupExemption; + // Set to true if doze constraint was satisfied due to app being whitelisted. public boolean dozeWhitelisted; @@ -415,9 +429,11 @@ public final class JobStatus { this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; + boolean requiresNetwork = false; int requiredConstraints = job.getConstraintFlags(); if (job.getRequiredNetwork() != null) { requiredConstraints |= CONSTRAINT_CONNECTIVITY; + requiresNetwork = true; } if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { requiredConstraints |= CONSTRAINT_TIMING_DELAY; @@ -425,8 +441,16 @@ public final class JobStatus { if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { requiredConstraints |= CONSTRAINT_DEADLINE; } + boolean mediaOnly = false; if (job.getTriggerContentUris() != null) { requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER; + mediaOnly = true; + for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) { + if (!MediaStore.AUTHORITY.equals(uri.getUri().getAuthority())) { + mediaOnly = false; + break; + } + } } this.requiredConstraints = requiredConstraints; mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; @@ -450,6 +474,9 @@ public final class JobStatus { // our source UID into place. job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid); } + final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); + mHasMediaBackupExemption = !job.hasLateConstraint() && mediaOnly && requiresNetwork + && this.sourcePackageName.equals(jsi.getMediaBackupPackage()); } /** Copy constructor: used specifically when cloning JobStatus objects for persistence, @@ -545,7 +572,6 @@ public final class JobStatus { int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, sourceUserId, elapsedNow); - JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); return new JobStatus(job, callingUid, sourcePkg, sourceUserId, standbyBucket, tag, 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, @@ -734,7 +760,14 @@ public final class JobStatus { // like other ACTIVE apps. return ACTIVE_INDEX; } - return getStandbyBucket(); + final int actualBucket = getStandbyBucket(); + if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX + && mHasMediaBackupExemption) { + // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the + // source package may not have been used directly in a while. + return Math.min(WORKING_INDEX, actualBucket); + } + return actualBucket; } /** Returns the real standby bucket of the job. */ diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp index c0f84a0ba070..baa1c251324f 100644 --- a/apex/statsd/Android.bp +++ b/apex/statsd/Android.bp @@ -20,6 +20,7 @@ apex { apex_defaults { native_shared_libs: [ + "libstatssocket", "libstatspull", "libstats_jni", ], @@ -76,4 +77,4 @@ cc_library_shared { //TODO (b/148620413): remove platform. "//apex_available:platform", ], -}
\ No newline at end of file +} diff --git a/api/current.txt b/api/current.txt index 4887c66660bb..8394416d04d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -289,6 +289,7 @@ package android { field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 + field public static final int allowNativeHeapPointerTagging = 16844311; // 0x1010617 field public static final int allowParallelSyncs = 16843570; // 0x1010332 field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 @@ -2876,6 +2877,7 @@ package android.accessibilityservice { method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>); field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14 + field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28 field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13 field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b @@ -2883,6 +2885,7 @@ package android.accessibilityservice { field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19 field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15 field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17 + field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29 field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16 field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f @@ -2890,6 +2893,7 @@ package android.accessibilityservice { field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18 field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26 + field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25 field public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; // 0x22 field public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; // 0x23 @@ -2954,7 +2958,7 @@ package android.accessibilityservice { } public static final class AccessibilityService.ScreenshotResult { - method @Nullable public android.graphics.ColorSpace getColorSpace(); + method @NonNull public android.graphics.ColorSpace getColorSpace(); method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer(); method public long getTimestamp(); } @@ -10141,7 +10145,6 @@ package android.content { field public static final String ALARM_SERVICE = "alarm"; field public static final String APPWIDGET_SERVICE = "appwidget"; field public static final String APP_OPS_SERVICE = "appops"; - field public static final String APP_SEARCH_SERVICE = "app_search"; field public static final String AUDIO_SERVICE = "audio"; field public static final String BATTERY_SERVICE = "batterymanager"; field public static final int BIND_ABOVE_CLIENT = 8; // 0x8 @@ -12068,7 +12071,7 @@ package android.content.pm { field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup"; field public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice"; field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir"; - field public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub"; + field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin"; field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded"; field public static final String FEATURE_ETHERNET = "android.hardware.ethernet"; diff --git a/api/system-current.txt b/api/system-current.txt index 0fd8a20600b0..8725538f1fd8 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1809,7 +1809,7 @@ package android.content { field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry"; field public static final String TETHERING_SERVICE = "tethering"; field public static final String VR_SERVICE = "vrmanager"; - field public static final String WIFI_COND_SERVICE = "wificond"; + field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; } @@ -2217,6 +2217,7 @@ package android.content.pm { field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000 field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 + field public static final int FLAG_PERMISSION_AUTO_REVOKED = 1048576; // 0x100000 field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000 field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000 field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 @@ -2302,7 +2303,7 @@ package android.content.pm { method public void onPermissionsChanged(int); } - @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { + @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { } public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { @@ -8329,21 +8330,21 @@ package android.net.wifi.wificond { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.RadioChainInfo> CREATOR; } - public class WifiCondManager { + public class WifiNl80211Manager { method public void abortScan(@NonNull String); method public void enableVerboseLogging(boolean); method @NonNull public int[] getChannelsMhzForBand(int); method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String); method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int); - method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String); - method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]); - method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback); - method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback); + method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String); + method @Nullable public static android.net.wifi.wificond.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]); + method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback); + method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SendMgmtFrameCallback); method public void setOnServiceDeadCallback(@NonNull Runnable); - method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback); + method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback); method public boolean setupInterfaceForSoftApMode(@NonNull String); - method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String); - method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.PnoScanRequestCallback); + method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String); + method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.PnoScanRequestCallback); method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>); method public boolean stopPnoScan(@NonNull String); method public boolean tearDownClientInterface(@NonNull String); @@ -8358,43 +8359,43 @@ package android.net.wifi.wificond { field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1 } - public static class WifiCondManager.OemSecurityType { - ctor public WifiCondManager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int); + public static class WifiNl80211Manager.OemSecurityType { + ctor public WifiNl80211Manager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int); field public final int groupCipher; field @NonNull public final java.util.List<java.lang.Integer> keyManagement; field @NonNull public final java.util.List<java.lang.Integer> pairwiseCipher; field public final int protocol; } - public static interface WifiCondManager.PnoScanRequestCallback { + public static interface WifiNl80211Manager.PnoScanRequestCallback { method public void onPnoRequestFailed(); method public void onPnoRequestSucceeded(); } - public static interface WifiCondManager.ScanEventCallback { + public static interface WifiNl80211Manager.ScanEventCallback { method public void onScanFailed(); method public void onScanResultReady(); } - public static interface WifiCondManager.SendMgmtFrameCallback { + public static interface WifiNl80211Manager.SendMgmtFrameCallback { method public void onAck(int); method public void onFailure(int); } - public static class WifiCondManager.SignalPollResult { + public static class WifiNl80211Manager.SignalPollResult { field public final int associationFrequencyMHz; field public final int currentRssiDbm; field public final int rxBitrateMbps; field public final int txBitrateMbps; } - public static interface WifiCondManager.SoftApCallback { + public static interface WifiNl80211Manager.SoftApCallback { method public void onConnectedClientsChanged(@NonNull android.net.wifi.wificond.NativeWifiClient, boolean); method public void onFailure(); method public void onSoftApChannelSwitched(int, int); } - public static class WifiCondManager.TxPacketCounters { + public static class WifiNl80211Manager.TxPacketCounters { field public final int txPacketFailed; field public final int txPacketSucceeded; } diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index 0caee6bebbda..dfb0d7460ca4 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -234,7 +234,7 @@ OnNameExpected: android.content.ContentProvider#checkUriPermission(android.net.U If implemented by developer, should follow the on<Something> style; otherwise consider marking final -PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback): +PairedRegistration: android.net.wifi.wificond.WifiNl80211Manager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback): diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 956fd29205cb..1bcf44e03c09 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -120,13 +120,14 @@ cc_defaults { "libstatslog", "libstatsmetadata", "libsysutils", + // TODO(b/145923087): move to shared when statsd is moved to the apex + "libstatssocket", "libutils", ], shared_libs: [ "libbinder", "libincident", "liblog", - "libstatssocket", "statsd-aidl-cpp", ], } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 03f97d80824d..23a4437910f7 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -381,7 +381,7 @@ message Atom { UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"]; NotificationReported notification_reported = 244 [(module) = "framework"]; NotificationPanelReported notification_panel_reported = 245; - NotificationChannelModified notification_panel_modified = 246; + NotificationChannelModified notification_channel_modified = 246; IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"]; IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"]; CellBroadcastMessageReported cb_message_reported = diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 1bac19ed2c5d..4899b4a5247c 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -690,14 +690,16 @@ void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) { if (name[0] == '.') continue; FileName output; + string file_name; if (parseTimestampOnly) { + file_name = StringPrintf("%s/%s", path, name); output.mTimestampSec = StrToInt64(strtok(name, "_")); output.mIsHistory = false; } else { parseFileName(name, &output); + file_name = output.getFullFileName(path); } if (output.mTimestampSec == -1) continue; - string file_name = output.getFullFileName(path); // Check for timestamp and delete if it's too old. long fileAge = nowSec - output.mTimestampSec; diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index ace13513e39d..25729abf9e05 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -18,6 +18,7 @@ package android.accessibilityservice; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT; @@ -25,6 +26,7 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT; @@ -32,6 +34,7 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT; @@ -83,9 +86,11 @@ public final class AccessibilityGestureEvent implements Parcelable { @IntDef(prefix = { "GESTURE_" }, value = { GESTURE_2_FINGER_SINGLE_TAP, GESTURE_2_FINGER_DOUBLE_TAP, + GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, GESTURE_2_FINGER_TRIPLE_TAP, GESTURE_3_FINGER_SINGLE_TAP, GESTURE_3_FINGER_DOUBLE_TAP, + GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, GESTURE_3_FINGER_TRIPLE_TAP, GESTURE_DOUBLE_TAP, GESTURE_DOUBLE_TAP_AND_HOLD, @@ -114,6 +119,7 @@ public final class AccessibilityGestureEvent implements Parcelable { GESTURE_3_FINGER_SWIPE_RIGHT, GESTURE_3_FINGER_SWIPE_UP, GESTURE_4_FINGER_DOUBLE_TAP, + GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, GESTURE_4_FINGER_SINGLE_TAP, GESTURE_4_FINGER_SWIPE_DOWN, GESTURE_4_FINGER_SWIPE_LEFT, @@ -175,12 +181,18 @@ public final class AccessibilityGestureEvent implements Parcelable { switch (eventType) { case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP"; case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP"; + case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD: + return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD"; case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP"; case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP"; case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP"; + case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD: + return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD"; case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP"; case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP"; case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP"; + case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD: + return "GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD"; case GESTURE_4_FINGER_TRIPLE_TAP: return "GESTURE_4_FINGER_TRIPLE_TAP"; case GESTURE_DOUBLE_TAP: return "GESTURE_DOUBLE_TAP"; case GESTURE_DOUBLE_TAP_AND_HOLD: return "GESTURE_DOUBLE_TAP_AND_HOLD"; diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index b65f68e177ca..b7a35f7548c7 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; import android.graphics.ColorSpace; +import android.graphics.ParcelableColorSpace; import android.graphics.Region; import android.hardware.HardwareBuffer; import android.os.Binder; @@ -39,6 +40,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.SystemClock; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -411,6 +413,15 @@ public abstract class AccessibilityService extends Service { /** The user has performed a four-finger triple tap gesture on the touch screen. */ public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39; + /** The user has performed a two-finger double tap and hold gesture on the touch screen. */ + public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; + + /** The user has performed a three-finger double tap and hold gesture on the touch screen. */ + public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; + + /** The user has performed a two-finger double tap and hold gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; + /** * The {@link Intent} that must be declared as handled by the service. */ @@ -591,8 +602,12 @@ public abstract class AccessibilityService extends Service { "screenshot_hardwareBuffer"; /** @hide */ - public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID = - "screenshot_colorSpaceId"; + public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE = + "screenshot_colorSpace"; + + /** @hide */ + public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP = + "screenshot_timestamp"; /** * Callback for {@link android.view.accessibility.AccessibilityEvent}s. @@ -1911,6 +1926,8 @@ public abstract class AccessibilityService extends Service { * default display. * @param executor Executor on which to run the callback. * @param callback The callback invoked when the taking screenshot is done. + * The {@link AccessibilityService.ScreenshotResult} will be null for an + * invalid display. * * @return {@code true} if the taking screenshot accepted, {@code false} if not. */ @@ -1932,14 +1949,11 @@ public abstract class AccessibilityService extends Service { } final HardwareBuffer hardwareBuffer = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER); - final int colorSpaceId = - result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID); - ColorSpace colorSpace = null; - if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { - colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); - } + final ParcelableColorSpace colorSpace = + result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE); ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer, - colorSpace, System.currentTimeMillis()); + colorSpace.getColorSpace(), + result.getLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP)); sendScreenshotResult(executor, callback, screenshot); })); } catch (RemoteException re) { @@ -2352,41 +2366,38 @@ public abstract class AccessibilityService extends Service { } /** - * Class including hardwareBuffer, colorSpace, and timestamp to be the result for + * Can be used to construct a bitmap of the screenshot or any other operations for * {@link AccessibilityService#takeScreenshot} API. - * <p> - * <strong>Note:</strong> colorSpace would be null if the name of this colorSpace isn't at - * {@link ColorSpace.Named}. - * </p> */ public static final class ScreenshotResult { private final @NonNull HardwareBuffer mHardwareBuffer; - private final @Nullable ColorSpace mColorSpace; + private final @NonNull ColorSpace mColorSpace; private final long mTimestamp; private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer, - @Nullable ColorSpace colorSpace, long timestamp) { + @NonNull ColorSpace colorSpace, long timestamp) { Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null"); + Preconditions.checkNotNull(colorSpace, "colorSpace cannot be null"); mHardwareBuffer = hardwareBuffer; mColorSpace = colorSpace; mTimestamp = timestamp; } /** - * Gets the colorSpace identifying a specific organization of colors of the screenshot. + * Gets the {@link ColorSpace} identifying a specific organization of colors of the + * screenshot. * - * @return the colorSpace or {@code null} if the name of colorSpace isn't at - * {@link ColorSpace.Named} + * @return the color space */ - @Nullable + @NonNull public ColorSpace getColorSpace() { return mColorSpace; } /** - * Gets the hardwareBuffer representing a memory buffer of the screenshot. + * Gets the {@link HardwareBuffer} representing a memory buffer of the screenshot. * - * @return the hardwareBuffer + * @return the hardware buffer */ @NonNull public HardwareBuffer getHardwareBuffer() { @@ -2396,7 +2407,8 @@ public abstract class AccessibilityService extends Service { /** * Gets the timestamp of taking the screenshot. * - * @return the timestamp from {@link System#currentTimeMillis()} + * @return milliseconds of non-sleep uptime before screenshot since boot and it's from + * {@link SystemClock#uptimeMillis()} */ public long getTimestamp() { return mTimestamp; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 367c2f235f5f..1de68ba56c6a 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -7824,9 +7824,7 @@ public class AppOpsManager { * @hide */ public static boolean isCollectingNotedAppOps() { - synchronized (sLock) { - return sNotedAppOpsCollector != null; - } + return sNotedAppOpsCollector != null; } /** diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 9aa6b870792d..526c0b3f87ee 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -162,6 +162,7 @@ interface INotificationManager void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments); void unsnoozeNotificationFromAssistant(in INotificationListener token, String key); + void unsnoozeNotificationFromSystemListener(in INotificationListener token, String key); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 8b07418668ba..9b7306089dca 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -132,7 +132,7 @@ import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; import android.net.wifi.WifiFrameworkInitializer; -import android.net.wifi.wificond.WifiCondManager; +import android.net.wifi.wificond.WifiNl80211Manager; import android.nfc.NfcManager; import android.os.BatteryManager; import android.os.BatteryStats; @@ -761,11 +761,11 @@ public final class SystemServiceRegistry { return new EthernetManager(ctx.getOuterContext(), service); }}); - registerService(Context.WIFI_COND_SERVICE, WifiCondManager.class, - new CachedServiceFetcher<WifiCondManager>() { + registerService(Context.WIFI_NL80211_SERVICE, WifiNl80211Manager.class, + new CachedServiceFetcher<WifiNl80211Manager>() { @Override - public WifiCondManager createService(ContextImpl ctx) { - return new WifiCondManager(ctx.getOuterContext()); + public WifiNl80211Manager createService(ContextImpl ctx) { + return new WifiNl80211Manager(ctx.getOuterContext()); } }); diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index ab71e73fd58c..0f999ad68a62 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -41,6 +41,43 @@ public final class UsageEvents implements Parcelable { /** @hide */ public static final String INSTANT_APP_CLASS_NAME = "android.instant_class"; + /** @hide */ + public static final String OBFUSCATED_NOTIFICATION_CHANNEL_ID = "unknown_channel_id"; + + /** + * Flag: indicates to not obfuscate or hide any usage event data when being queried. + * @hide + */ + public static final int SHOW_ALL_EVENT_DATA = 0x00000000; + + /** + * Flag: indicates to obfuscate package and class names for instant apps when querying usage + * events. + * @hide + */ + public static final int OBFUSCATE_INSTANT_APPS = 0x00000001; + + /** + * Flag: indicates to hide all {@link Event#SHORTCUT_INVOCATION} events when querying usage + * events. + * @hide + */ + public static final int HIDE_SHORTCUT_EVENTS = 0x00000002; + + /** + * Flag: indicates to obfuscate the notification channel id for all notification events, + * such as {@link Event#NOTIFICATION_SEEN} and {@link Event#NOTIFICATION_INTERRUPTION} events, + * when querying usage events. + * @hide + */ + public static final int OBFUSCATE_NOTIFICATION_EVENTS = 0x00000004; + + /** + * Flag: indicates to hide all {@link Event#LOCUS_ID_SET} events when querying usage events. + * @hide + */ + public static final int HIDE_LOCUS_EVENTS = 0x00000008; + /** * An event representing a state change for a component. */ @@ -627,6 +664,13 @@ public final class UsageEvents implements Parcelable { return ret; } + /** @hide */ + public Event getObfuscatedNotificationEvent() { + final Event ret = new Event(this); + ret.mNotificationChannelId = OBFUSCATED_NOTIFICATION_CHANNEL_ID; + return ret; + } + /** * Returns the locusId for this event if the event is of type {@link #LOCUS_ID_SET}, * otherwise it returns null. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 49f62f407806..ae12de027e6e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4064,16 +4064,16 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.net.wifi.WifiCondManager} for handling management of the Wi-Fi control - * daemon. + * {@link android.net.wifi.wificond.WifiNl80211Manager} for handling management of the + * Wi-Fi nl802.11 daemon (wificond). * * @see #getSystemService(String) - * @see android.net.wifi.WifiCondManager + * @see android.net.wifi.wificond.WifiNl80211Manager * @hide */ @SystemApi @SuppressLint("ServiceName") - public static final String WIFI_COND_SERVICE = "wificond"; + public static final String WIFI_NL80211_SERVICE = "wifinl80211"; /** * Use with {@link #getSystemService(String)} to retrieve a {@link @@ -5098,10 +5098,11 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an - * AppSearchManager for indexing and querying app data managed - * by the system. + * {@link android.app.appsearch.AppSearchManager} for + * indexing and querying app data managed by the system. * * @see #getSystemService(String) + * @hide */ public static final String APP_SEARCH_SERVICE = "app_search"; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index d251ba9519db..9d1c677f35c6 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -702,6 +702,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_ODM = 1 << 30; + /** + * Value for {@link #privateFlags}: If {@code true} this app allows heap tagging. + * {@link com.android.server.am.ProcessList#NATIVE_HEAP_POINTER_TAGGING} + * @hide + */ + public static final int PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1 << 31; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = { PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE, @@ -733,6 +740,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE, PRIVATE_FLAG_ODM, + PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlags {} @@ -1878,6 +1886,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0; } + /** + * If {@code true} this app allows heap pointer tagging. + * + * @hide + */ + public boolean allowsNativeHeapPointerTagging() { + return (privateFlags & PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) != 0; + } + private boolean isAllowedToUseHiddenApis() { if (isSignedWithPlatformKey()) { return true; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c78d30dd9133..7b484b7c2c3d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -38,7 +38,7 @@ import android.app.PackageInstallObserver; import android.app.admin.DevicePolicyManager; import android.app.usage.StorageStatsManager; import android.compat.annotation.ChangeId; -import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -1988,10 +1988,11 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and - * {@link #hasSystemFeature}: The device supports a Context Hub. + * {@link #hasSystemFeature}: The device supports a Context Hub, used to expose the + * functionalities in {@link android.hardware.location.ContextHubManager}. */ @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub"; + public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; /** {@hide} */ @SdkConstant(SdkConstantType.FEATURE) @@ -3385,6 +3386,14 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18; /** + * Permission flag: Whether permission was revoked by auto-revoke. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 20; + + /** * Permission flags: Reserved for use by the permission controller. * * @hide @@ -3437,7 +3446,8 @@ public abstract class PackageManager { | FLAG_PERMISSION_REVOKED_COMPAT | FLAG_PERMISSION_ONE_TIME | FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED - | FLAG_PERMISSION_AUTO_REVOKE_USER_SET; + | FLAG_PERMISSION_AUTO_REVOKE_USER_SET + | FLAG_PERMISSION_AUTO_REVOKED; /** * Injected activity in app that forwards user to setting activity of that app. @@ -3581,7 +3591,7 @@ public abstract class PackageManager { * @hide */ @ChangeId - @Disabled + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) public static final long FILTER_APPLICATION_QUERY = 135549675L; /** {@hide} */ @@ -4262,7 +4272,8 @@ public abstract class PackageManager { FLAG_PERMISSION_REVOKED_COMPAT, FLAG_PERMISSION_ONE_TIME, FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, - FLAG_PERMISSION_AUTO_REVOKE_USER_SET + FLAG_PERMISSION_AUTO_REVOKE_USER_SET, + FLAG_PERMISSION_AUTO_REVOKED }) @Retention(RetentionPolicy.SOURCE) public @interface PermissionFlags {} @@ -7401,6 +7412,7 @@ public abstract class PackageManager { case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME"; case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED"; case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET"; + case FLAG_PERMISSION_AUTO_REVOKED: return "AUTO_REVOKED"; default: return Integer.toString(flag); } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 637e64d3d2c2..da44f70178de 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3710,6 +3710,11 @@ public class PackageParser { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE; } + if (sa.getBoolean( + R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true)) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING; + } + ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0); diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java index 548d82a6ab76..a2671136ff7b 100644 --- a/core/java/android/content/pm/parsing/ApkParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkParseUtils.java @@ -2098,6 +2098,9 @@ public class ApkParseUtils { R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q)); + parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean( + R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true)); + parsingPackage .setMaxAspectRatio( sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0)) diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java index 0df950006f43..778d7b8b26b6 100644 --- a/core/java/android/content/pm/parsing/PackageImpl.java +++ b/core/java/android/content/pm/parsing/PackageImpl.java @@ -1509,6 +1509,16 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android } @Override + public PackageImpl setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging) { + this.privateFlags = allowNativeHeapPointerTagging + ? this.privateFlags | ApplicationInfo + .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING + : this.privateFlags & ~ApplicationInfo + .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING; + return this; + } + + @Override public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) { this.privateFlags = usesNonSdkApi ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index a2fe064b66c3..954d65c75730 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -191,6 +191,8 @@ public interface ParsingPackage extends AndroidPackage { ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage); + ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging); + ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion); ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode); diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index ef28e6c6db2a..ac36188ee61a 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -75,6 +75,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * @hide */ + public static final String KEY_DEVICE_CREDENTIAL_TITLE = "device_credential_title"; + /** + * @hide + */ + public static final String KEY_DEVICE_CREDENTIAL_SUBTITLE = "device_credential_subtitle"; + /** + * @hide + */ + public static final String KEY_DEVICE_CREDENTIAL_DESCRIPTION = "device_credential_description"; + /** + * @hide + */ public static final String KEY_NEGATIVE_TEXT = "negative_text"; /** * @hide @@ -221,6 +233,30 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * Sets an optional title, subtitle, and/or description that will override other text when + * the user is authenticating with PIN/pattern/password. Currently for internal use only. + * @return This builder. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull + public Builder setTextForDeviceCredential( + @Nullable CharSequence title, + @Nullable CharSequence subtitle, + @Nullable CharSequence description) { + if (title != null) { + mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_TITLE, title); + } + if (subtitle != null) { + mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_SUBTITLE, subtitle); + } + if (description != null) { + mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_DESCRIPTION, description); + } + return this; + } + + /** * Required: Sets the text, executor, and click listener for the negative button on the * prompt. This is typically a cancel button, but may be also used to show an alternative * method for authentication, such as a screen that asks for a backup password. diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index db16d24e0af1..1ed791d66f74 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -54,7 +54,7 @@ import java.util.concurrent.Executor; */ @SystemApi @SystemService(Context.CONTEXTHUB_SERVICE) -@RequiresFeature(PackageManager.FEATURE_CONTEXTHUB) +@RequiresFeature(PackageManager.FEATURE_CONTEXT_HUB) public final class ContextHubManager { private static final String TAG = "ContextHubManager"; diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS new file mode 100644 index 000000000000..444719701df2 --- /dev/null +++ b/core/java/android/inputmethodservice/OWNERS @@ -0,0 +1,3 @@ +set noparent + +include ../../../../services/core/java/com/android/server/inputmethod/OWNERS diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java index 6450a67572bf..6516917afd9d 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -16,6 +16,7 @@ package android.os.incremental; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -47,6 +48,15 @@ public class V4Signature { } /** + * Construct a V4Signature from .idsig file. + */ + public static V4Signature readFrom(byte[] bytes) throws IOException { + try (DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes))) { + return readFrom(stream); + } + } + + /** * Store the V4Signature to a byte-array. */ public byte[] toByteArray() { diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index afeb6c391009..44e7ae6c13c5 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -79,6 +79,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false"); + DEFAULT_FLAGS.put("settings_contextual_home2", "false"); } /** diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 06cb51927ba8..0c1edac6608c 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.compat.annotation.UnsupportedAppUsage; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.graphics.RecordingCanvas; @@ -109,12 +108,10 @@ public class RenderNodeAnimator extends Animator { private long mStartDelay = 0; private long mStartTime; - @UnsupportedAppUsage public static int mapViewPropertyToRenderProperty(int viewProperty) { return sViewPropertyAnimatorMap.get(viewProperty); } - @UnsupportedAppUsage public RenderNodeAnimator(int property, float finalValue) { mRenderProperty = property; mFinalValue = finalValue; @@ -122,7 +119,6 @@ public class RenderNodeAnimator extends Animator { init(nCreateAnimator(property, finalValue)); } - @UnsupportedAppUsage public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) { init(nCreateCanvasPropertyFloatAnimator( property.getNativeContainer(), finalValue)); @@ -137,7 +133,6 @@ public class RenderNodeAnimator extends Animator { * {@link #PAINT_STROKE_WIDTH} * @param finalValue The target value for the property */ - @UnsupportedAppUsage public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) { init(nCreateCanvasPropertyPaintAnimator( property.getNativeContainer(), paintField, finalValue)); @@ -289,7 +284,6 @@ public class RenderNodeAnimator extends Animator { } /** @hide */ - @UnsupportedAppUsage public void setTarget(View view) { mViewTarget = view; setTarget(mViewTarget.mRenderNode); @@ -301,7 +295,6 @@ public class RenderNodeAnimator extends Animator { } /** @hide */ - @UnsupportedAppUsage public void setTarget(DisplayListCanvas canvas) { setTarget((RecordingCanvas) canvas); } @@ -316,7 +309,6 @@ public class RenderNodeAnimator extends Animator { mTarget.addAnimator(this); } - @UnsupportedAppUsage public void setStartValue(float startValue) { checkMutable(); nSetStartValue(mNativePtr.get(), startValue); @@ -337,7 +329,6 @@ public class RenderNodeAnimator extends Animator { return mUnscaledStartDelay; } - @UnsupportedAppUsage @Override public RenderNodeAnimator setDuration(long duration) { checkMutable(); @@ -502,7 +493,6 @@ public class RenderNodeAnimator extends Animator { } // Called by native - @UnsupportedAppUsage private static void callOnFinished(RenderNodeAnimator animator) { if (animator.mHandler != null) { animator.mHandler.post(animator::onFinished); diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index bf848196454d..680a8789a6b8 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -24,6 +24,7 @@ import android.graphics.PixelFormat; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.view.accessibility.IAccessibilityEmbeddedConnection; /** * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy @@ -40,6 +41,7 @@ public class SurfaceControlViewHost { private WindowlessWindowManager mWm; private SurfaceControl mSurfaceControl; + private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; /** * Package encapsulating a Surface hierarchy which contains interactive view @@ -49,15 +51,18 @@ public class SurfaceControlViewHost { */ public static final class SurfacePackage implements Parcelable { private final SurfaceControl mSurfaceControl; - // TODO: Accessibility ID goes here + private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; - SurfacePackage(SurfaceControl sc) { + SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) { mSurfaceControl = sc; + mAccessibilityEmbeddedConnection = connection; } private SurfacePackage(Parcel in) { mSurfaceControl = new SurfaceControl(); mSurfaceControl.readFromParcel(in); + mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface( + in.readStrongBinder()); } /** @@ -69,6 +74,16 @@ public class SurfaceControlViewHost { return mSurfaceControl; } + /** + * Gets an accessibility embedded connection interface for this SurfaceControlViewHost. + * + * @return {@link IAccessibilityEmbeddedConnection} interface. + * @hide + */ + public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { + return mAccessibilityEmbeddedConnection; + } + @Override public int describeContents() { return 0; @@ -77,6 +92,7 @@ public class SurfaceControlViewHost { @Override public void writeToParcel(@NonNull Parcel out, int flags) { mSurfaceControl.writeToParcel(out, flags); + out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder()); } public static final @NonNull Creator<SurfacePackage> CREATOR @@ -95,6 +111,7 @@ public class SurfaceControlViewHost { @NonNull WindowlessWindowManager wwm) { mWm = wwm; mViewRoot = new ViewRootImpl(c, d, mWm); + mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); } /** @@ -118,6 +135,7 @@ public class SurfaceControlViewHost { mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), mSurfaceControl, hostToken); mViewRoot = new ViewRootImpl(context, display, mWm); + mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); } /** @@ -128,8 +146,8 @@ public class SurfaceControlViewHost { * are linked. */ public @Nullable SurfacePackage getSurfacePackage() { - if (mSurfaceControl != null) { - return new SurfacePackage(mSurfaceControl); + if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { + return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection); } else { return null; } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d5ed36b57c02..47ffd3e2714c 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -28,6 +28,7 @@ import android.content.res.CompatibilityInfo.Translator; import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; @@ -38,12 +39,14 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceControl.Transaction; import android.view.SurfaceControlViewHost; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityEmbeddedConnection; import com.android.internal.view.SurfaceCallbackHelper; @@ -203,8 +206,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); private int mParentSurfaceGenerationId; - // The token of embedded windowless view hierarchy. - private IBinder mEmbeddedViewHierarchy; + private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection; + + private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix(); + private final Matrix mTmpMatrix = new Matrix(); + private final float[] mMatrixValues = new float[9]; + SurfaceControlViewHost.SurfacePackage mSurfacePackage; public SurfaceView(Context context) { @@ -923,6 +930,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } mTmpTransaction.apply(); + updateScreenMatrixForEmbeddedHierarchy(); if (sizeChanged || creating) { redrawNeeded = true; @@ -1510,6 +1518,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void surfaceDestroyed() { setWindowStopped(true); + setRemoteAccessibilityEmbeddedConnection(null, null); } /** @@ -1568,31 +1577,133 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void reparentSurfacePackage(SurfaceControl.Transaction t, SurfaceControlViewHost.SurfacePackage p) { - // TODO: Link accessibility IDs here. + initEmbeddedHierarchyForAccessibility(p); final SurfaceControl sc = p.getSurfaceControl(); t.reparent(sc, mSurfaceControl).show(sc); } - /** - * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view - * hierarchy. - * - * @param token IBinder token. - * @hide - */ - public void setEmbeddedViewHierarchy(IBinder token) { - mEmbeddedViewHierarchy = token; - } - /** @hide */ @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); - if (mEmbeddedViewHierarchy == null) { + final RemoteAccessibilityEmbeddedConnection wrapper = + getRemoteAccessibilityEmbeddedConnection(); + if (wrapper == null) { return; } // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this // leashed child would return the root node in the embedded hierarchy - info.addChild(mEmbeddedViewHierarchy); + info.addChild(wrapper.getLeashToken()); + } + + private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) { + final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection(); + final RemoteAccessibilityEmbeddedConnection wrapper = + getRemoteAccessibilityEmbeddedConnection(); + + // Do nothing if package is embedding the same view hierarchy. + if (wrapper != null && wrapper.getConnection().equals(connection)) { + return; + } + + // If this SurfaceView embeds a different view hierarchy, unlink the previous one first. + setRemoteAccessibilityEmbeddedConnection(null, null); + + try { + final IBinder leashToken = connection.associateEmbeddedHierarchy( + getViewRootImpl().mLeashToken, getAccessibilityViewId()); + setRemoteAccessibilityEmbeddedConnection(connection, leashToken); + } catch (RemoteException e) { + Log.d(TAG, "Error while associateEmbeddedHierarchy " + e); + } + updateScreenMatrixForEmbeddedHierarchy(); + } + + private void setRemoteAccessibilityEmbeddedConnection( + IAccessibilityEmbeddedConnection connection, IBinder leashToken) { + try { + if (mRemoteAccessibilityEmbeddedConnection != null) { + mRemoteAccessibilityEmbeddedConnection.getConnection() + .disassociateEmbeddedHierarchy(); + mRemoteAccessibilityEmbeddedConnection.unlinkToDeath(); + mRemoteAccessibilityEmbeddedConnection = null; + } + if (connection != null && leashToken != null) { + mRemoteAccessibilityEmbeddedConnection = + new RemoteAccessibilityEmbeddedConnection(connection, leashToken); + mRemoteAccessibilityEmbeddedConnection.linkToDeath(); + } + } catch (RemoteException e) { + Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e); + } + } + + private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() { + return mRemoteAccessibilityEmbeddedConnection; + } + + private void updateScreenMatrixForEmbeddedHierarchy() { + mTmpMatrix.reset(); + mTmpMatrix.setTranslate(mScreenRect.left, mScreenRect.top); + mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth, + mScreenRect.height() / (float) mSurfaceHeight); + + // If the screen matrix is identity or doesn't change, do nothing. + if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) { + return; + } + + try { + final RemoteAccessibilityEmbeddedConnection wrapper = + getRemoteAccessibilityEmbeddedConnection(); + if (wrapper == null) { + return; + } + mTmpMatrix.getValues(mMatrixValues); + wrapper.getConnection().setScreenMatrix(mMatrixValues); + mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix); + } catch (RemoteException e) { + Log.d(TAG, "Error while setScreenMatrix " + e); + } + } + + /** + * Wrapper of accessibility embedded connection for embedded view hierarchy. + */ + private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient { + private final IAccessibilityEmbeddedConnection mConnection; + private final IBinder mLeashToken; + + RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection, + IBinder leashToken) { + mConnection = connection; + mLeashToken = leashToken; + } + + IAccessibilityEmbeddedConnection getConnection() { + return mConnection; + } + + IBinder getLeashToken() { + return mLeashToken; + } + + void linkToDeath() throws RemoteException { + mConnection.asBinder().linkToDeath(this, 0); + } + + void unlinkToDeath() { + mConnection.asBinder().unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + unlinkToDeath(); + runOnUiThread(() -> { + if (mRemoteAccessibilityEmbeddedConnection == this) { + mRemoteAccessibilityEmbeddedConnection = null; + } + }); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 857bc5058d60..b971a20d11c9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -655,7 +655,7 @@ public final class ViewRootImpl implements ViewParent, private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); - private IAccessibilityEmbeddedConnection mEmbeddedConnection; + private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; static final class SystemUiVisibilityInfo { int seq; @@ -9370,11 +9370,12 @@ public final class ViewRootImpl implements ViewParent, * Gets an accessibility embedded connection interface for this ViewRootImpl. * @hide */ - public IAccessibilityEmbeddedConnection getEmbeddedConnection() { - if (mEmbeddedConnection == null) { - mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this); + public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { + if (mAccessibilityEmbeddedConnection == null) { + mAccessibilityEmbeddedConnection = new AccessibilityEmbeddedConnection( + ViewRootImpl.this); } - return mEmbeddedConnection; + return mAccessibilityEmbeddedConnection; } private class SendWindowContentChangedAccessibilityEvent implements Runnable { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 56683dd9a5d1..3274449b95c8 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -16,6 +16,9 @@ package android.view; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + import android.annotation.NonNull; import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; @@ -67,10 +70,6 @@ public final class WindowManagerImpl implements WindowManager { private IBinder mDefaultToken; - private boolean mIsViewAdded; - private View mLastView; - private WindowManager.LayoutParams mLastParams; - public WindowManagerImpl(Context context) { this(context, null); } @@ -102,9 +101,6 @@ public final class WindowManagerImpl implements WindowManager { public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); - mIsViewAdded = true; - mLastView = view; - mLastParams = (WindowManager.LayoutParams) params; } @Override @@ -250,18 +246,15 @@ public final class WindowManagerImpl implements WindowManager { // TODO(window-context): This can only be properly implemented // once we flip the new insets mode flag. if (mParentWindow != null) { - if (mParentWindow.getDecorView().isAttachedToWindow()) { - return mParentWindow.getDecorView().getViewRootImpl() - .getWindowInsets(true /* forceConstruct */); - } return getWindowInsetsFromServer(mParentWindow.getAttributes()); } - if (mIsViewAdded) { - return mLastView.getViewRootImpl().getWindowInsets(true /* forceConstruct */); - } else { - return getWindowInsetsFromServer(new WindowManager.LayoutParams()); - } + return getWindowInsetsFromServer(getDefaultParams()); + } + private static WindowManager.LayoutParams getDefaultParams() { + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + return params; } private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) { diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS new file mode 100644 index 000000000000..244cc30e089e --- /dev/null +++ b/core/java/android/view/inputmethod/OWNERS @@ -0,0 +1,3 @@ +set noparent + +include ../../../../../services/core/java/com/android/server/inputmethod/OWNERS diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java index 8a1a0b5ef9b0..556b24c94b36 100644 --- a/core/java/android/webkit/UserPackage.java +++ b/core/java/android/webkit/UserPackage.java @@ -34,7 +34,7 @@ public class UserPackage { private final UserInfo mUserInfo; private final PackageInfo mPackageInfo; - public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q; + public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R; public UserPackage(UserInfo user, PackageInfo packageInfo) { this.mUserInfo = user; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 941af6ef1d7a..8790bbdcd8f7 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -47,7 +47,7 @@ public final class WebViewFactory { // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote. /** @hide */ private static final String CHROMIUM_WEBVIEW_FACTORY = - "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ"; + "com.android.webview.chromium.WebViewChromiumFactoryProviderForR"; private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 46b8b771de87..12f245e87da2 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -5271,6 +5271,8 @@ public class Editor { if (opacity < 10 || opacity > 100) { opacity = 50; } + // Converts the opacity value from range {0..100} to {0..255}. + opacity = opacity * 255 / 100; } mDeltaHeight = deltaHeight; mDrawableOpacity = opacity; diff --git a/core/java/com/android/internal/inputmethod/OWNERS b/core/java/com/android/internal/inputmethod/OWNERS new file mode 100644 index 000000000000..fc0e5d4dea2b --- /dev/null +++ b/core/java/com/android/internal/inputmethod/OWNERS @@ -0,0 +1,3 @@ +set noparent + +include ../../../../../../services/core/java/com/android/server/inputmethod/OWNERS diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 518911e652f6..0e9c2c4dd659 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -19,8 +19,6 @@ package com.android.internal.os; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ApplicationErrorReport; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.type.DefaultMimeMapFactory; import android.os.Build; @@ -36,7 +34,6 @@ import android.util.Slog; import com.android.internal.logging.AndroidConfig; import com.android.server.NetworkManagementSocketTagger; -import dalvik.annotation.compat.VersionCodes; import dalvik.system.RuntimeHooks; import dalvik.system.ThreadPrioritySetter; import dalvik.system.VMRuntime; @@ -67,18 +64,8 @@ public class RuntimeInit { private static volatile boolean mCrashing = false; - /** - * Native heap allocations will now have a non-zero tag in the most significant byte. - * See - * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>. - */ - @ChangeId - @EnabledAfter(targetSdkVersion = VersionCodes.Q) - private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. - private static final native void nativeFinishInit(); private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup); - private static native void nativeDisableHeapPointerTagging(); private static int Clog_e(String tag, String msg, Throwable tr) { return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr); @@ -411,20 +398,6 @@ public class RuntimeInit { if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } - private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) { - // Heap tagging needs to be disabled before any additional threads are created, but the - // AppCompat framework is not initialized enough at this point. - // Check if the change is enabled manually. - if (disabledCompatChanges != null) { - for (int i = 0; i < disabledCompatChanges.length; i++) { - if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) { - nativeDisableHeapPointerTagging(); - break; - } - } - } - } - protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) { // If the application calls System.exit(), terminate the process @@ -437,8 +410,6 @@ public class RuntimeInit { VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges); - maybeDisableHeapPointerTagging(disabledCompatChanges); - final Arguments args = new Arguments(argv); // The end of of the RuntimeInit event (see #zygoteInit). diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index dfd700f51103..556586477218 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -122,6 +122,25 @@ public final class Zygote { */ public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18; + public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20); + /** + * Enable pointer tagging in this process. + * Tags are checked during memory deallocation, but not on access. + * TBI stands for Top-Byte-Ignore, an ARM CPU feature. + * {@link https://developer.arm.com/docs/den0024/latest/the-memory-management-unit/translation-table-configuration/virtual-address-tagging} + */ + public static final int MEMORY_TAG_LEVEL_TBI = 1 << 19; + + /** + * Enable asynchronous memory tag checks in this process. + */ + public static final int MEMORY_TAG_LEVEL_ASYNC = 2 << 19; + + /** + * Enable synchronous memory tag checks in this process. + */ + public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ae54eb210de7..e34aa9722ec2 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -788,6 +788,10 @@ public class ZygoteInit { Zygote.applyDebuggerSystemProperty(parsedArgs); Zygote.applyInvokeWithSystemProperty(parsedArgs); + /* Enable pointer tagging in the system server unconditionally. Hardware support for + * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */ + parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI; + if (shouldProfileSystemServer()) { parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER; } diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 0b1c8b7f5335..19f6e2750219 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -243,14 +243,6 @@ static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIE gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup); } -static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging( - JNIEnv* env, jobject clazz) { - HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE; - if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) { - ALOGE("ERROR: could not disable heap pointer tagging\n"); - } -} - /* * JNI registration. */ @@ -262,8 +254,6 @@ int register_com_android_internal_os_RuntimeInit(JNIEnv* env) (void*)com_android_internal_os_RuntimeInit_nativeFinishInit}, {"nativeSetExitWithoutCleanup", "(Z)V", (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup}, - {"nativeDisableHeapPointerTagging", "()V", - (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging}, }; return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", methods, NELEM(methods)); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 7a9a3f8643c0..9fbb8df0d079 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -349,6 +349,8 @@ static const std::array<const std::string, MOUNT_EXTERNAL_COUNT> ExternalStorage enum RuntimeFlags : uint32_t { DEBUG_ENABLE_JDWP = 1, PROFILE_FROM_SHELL = 1 << 15, + MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20), + MEMORY_TAG_LEVEL_TBI = 1 << 19, }; enum UnsolicitedZygoteMessageTypes : uint32_t { @@ -1627,6 +1629,16 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } } + HeapTaggingLevel heap_tagging_level; + switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) { + case RuntimeFlags::MEMORY_TAG_LEVEL_TBI: + heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI; + break; + default: + heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE; + } + android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level)); + if (NeedsNoRandomizeWorkaround()) { // Work around ARM kernel ASLR lossage (http://b/5817320). int old_personality = personality(0xffffffff); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c66261bb6630..e231c3a323fd 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1782,6 +1782,15 @@ The default value is {@code false}. --> <attr name="crossProfile" format="boolean" /> + + <!-- If {@code true} this app will receive tagged pointers to native heap allocations + from functions like malloc() on compatible devices. Note that this may not always + be respected due to policy or backwards compatibility reasons. See the + <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged Pointers</a> + document for more information on this feature. + + The default value is {@code true}. --> + <attr name="allowNativeHeapPointerTagging" format="boolean" /> </declare-styleable> <!-- The <code>feature</code> tag declares a feature. A feature is a logical part of an app. diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 4172044dfa13..0edad3b0d9cd 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3017,6 +3017,7 @@ <public name="autofillInlineSuggestionSubtitle" /> <!-- @hide @SystemApi --> <public name="isAutofillInlineSuggestionTheme" /> + <public name="allowNativeHeapPointerTagging" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 59335a595334..be94f02dc3e7 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -138,6 +138,9 @@ <!-- vr test permissions --> <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> + <!-- WindowMetricsTest permissions --> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java new file mode 100644 index 000000000000..d2eb8f5f5573 --- /dev/null +++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java @@ -0,0 +1,81 @@ +/* + * 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.view; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +/** + * Tests for {@link WindowManager#getCurrentWindowMetrics()} and + * {@link WindowManager#getMaximumWindowMetrics()}. + * + * <p>Build/Install/Run: + * atest FrameworksCoreTests:WindowMetricsTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@FlakyTest(bugId = 148789183, detail = "Remove after confirmed it's stable.") +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class WindowMetricsTest { + private Context mWindowContext; + private WindowManager mWm; + + @Before + public void setUp() { + final Context insetContext = InstrumentationRegistry.getInstrumentation() + .getTargetContext(); + final Display display = insetContext.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY); + mWindowContext = insetContext.createDisplayContext(display) + .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */); + mWm = mWindowContext.getSystemService(WindowManager.class); + } + + @Test + public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() { + final View view = new View(mWindowContext); + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + Handler.getMain().runWithScissors(() -> { + mWm.addView(view, params); + // Check get metrics do not crash. + mWm.getCurrentWindowMetrics(); + mWm.getMaximumWindowMetrics(); + + mWm.removeViewImmediate(view); + // Check get metrics do not crash. + mWm.getCurrentWindowMetrics(); + mWm.getMaximumWindowMetrics(); + }, 0); + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index af115b1e80c1..4b95e4d7fa6c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -381,6 +381,8 @@ applications that come with the platform <permission name="android.permission.REBOOT"/> <!-- Permission required for access VIBRATOR_STATE. --> <permission name="android.permission.ACCESS_VIBRATOR_STATE"/> + <!-- Permission required for UsageStatsTest CTS test. --> + <permission name="android.permission.MANAGE_NOTIFICATIONS"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 06d4fbdd85b1..ce8ff7dc38ba 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -867,7 +867,8 @@ public abstract class ColorSpace { } } - private ColorSpace( + /** @hide */ + ColorSpace( @NonNull String name, @NonNull Model model, @IntRange(from = MIN_ID, to = MAX_ID) int id) { diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java new file mode 100644 index 000000000000..f9033a53d7e6 --- /dev/null +++ b/graphics/java/android/graphics/ParcelableColorSpace.java @@ -0,0 +1,183 @@ +/* + * 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.graphics; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A {@link Parcelable} {@link ColorSpace}. In order to enable parceling, the ColorSpace + * must be either a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} instance + * that has an ICC parametric transfer function as returned by {@link Rgb#getTransferParameters()}. + * TODO: Make public + * @hide + */ +public final class ParcelableColorSpace extends ColorSpace implements Parcelable { + private final ColorSpace mColorSpace; + + /** + * Checks if the given ColorSpace is able to be parceled. A ColorSpace can only be + * parceled if it is a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} + * instance that has an ICC parametric transfer function as returned by + * {@link Rgb#getTransferParameters()} + */ + public static boolean isParcelable(@NonNull ColorSpace colorSpace) { + if (colorSpace.getId() == ColorSpace.MIN_ID) { + if (!(colorSpace instanceof ColorSpace.Rgb)) { + return false; + } + ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace; + if (rgb.getTransferParameters() == null) { + return false; + } + } + return true; + } + + /** + * Constructs a new ParcelableColorSpace that wraps the provided ColorSpace. + * + * @param colorSpace The ColorSpace to wrap. The ColorSpace must be either named or be an + * RGB ColorSpace with an ICC parametric transfer function. + * @throws IllegalArgumentException If the provided ColorSpace does not satisfy the requirements + * to be parceled. See {@link #isParcelable(ColorSpace)}. + */ + public ParcelableColorSpace(@NonNull ColorSpace colorSpace) { + super(colorSpace.getName(), colorSpace.getModel(), colorSpace.getId()); + mColorSpace = colorSpace; + + if (mColorSpace.getId() == ColorSpace.MIN_ID) { + if (!(mColorSpace instanceof ColorSpace.Rgb)) { + throw new IllegalArgumentException( + "Unable to parcel unknown ColorSpaces that are not ColorSpace.Rgb"); + } + ColorSpace.Rgb rgb = (ColorSpace.Rgb) mColorSpace; + if (rgb.getTransferParameters() == null) { + throw new IllegalArgumentException("ColorSpace must use an ICC " + + "parametric transfer function to be parcelable"); + } + } + } + + public @NonNull ColorSpace getColorSpace() { + return mColorSpace; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + final int id = mColorSpace.getId(); + dest.writeInt(id); + if (id == ColorSpace.MIN_ID) { + // Not a named color space. We have to actually write, like, stuff. And things. Ugh. + // Cast is safe because this was asserted in the constructor + ColorSpace.Rgb rgb = (ColorSpace.Rgb) mColorSpace; + dest.writeString(rgb.getName()); + dest.writeFloatArray(rgb.getPrimaries()); + dest.writeFloatArray(rgb.getWhitePoint()); + ColorSpace.Rgb.TransferParameters transferParameters = rgb.getTransferParameters(); + dest.writeDouble(transferParameters.a); + dest.writeDouble(transferParameters.b); + dest.writeDouble(transferParameters.c); + dest.writeDouble(transferParameters.d); + dest.writeDouble(transferParameters.e); + dest.writeDouble(transferParameters.f); + dest.writeDouble(transferParameters.g); + } + } + + @NonNull + public static final Parcelable.Creator<ParcelableColorSpace> CREATOR = + new Parcelable.Creator<ParcelableColorSpace>() { + + public @NonNull ParcelableColorSpace createFromParcel(@NonNull Parcel in) { + final int id = in.readInt(); + if (id == ColorSpace.MIN_ID) { + String name = in.readString(); + float[] primaries = in.createFloatArray(); + float[] whitePoint = in.createFloatArray(); + double a = in.readDouble(); + double b = in.readDouble(); + double c = in.readDouble(); + double d = in.readDouble(); + double e = in.readDouble(); + double f = in.readDouble(); + double g = in.readDouble(); + ColorSpace.Rgb.TransferParameters function = + new ColorSpace.Rgb.TransferParameters(a, b, c, d, e, f, g); + return new ParcelableColorSpace( + new ColorSpace.Rgb(name, primaries, whitePoint, function)); + } else { + return new ParcelableColorSpace(ColorSpace.get(id)); + } + } + + public ParcelableColorSpace[] newArray(int size) { + return new ParcelableColorSpace[size]; + } + }; + + @Override + public boolean isWideGamut() { + return mColorSpace.isWideGamut(); + } + + @Override + public float getMinValue(int component) { + return mColorSpace.getMinValue(component); + } + + @Override + public float getMaxValue(int component) { + return mColorSpace.getMaxValue(component); + } + + @Override + public @NonNull float[] toXyz(@NonNull float[] v) { + return mColorSpace.toXyz(v); + } + + @Override + public @NonNull float[] fromXyz(@NonNull float[] v) { + return mColorSpace.fromXyz(v); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ParcelableColorSpace other = (ParcelableColorSpace) o; + return mColorSpace.equals(other.mColorSpace); + } + + @Override + public int hashCode() { + return mColorSpace.hashCode(); + } + + /** @hide */ + @Override + long getNativeInstance() { + return mColorSpace.getNativeInstance(); + } +} diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 89a9b997af97..84c07d7d9dff 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -16,16 +16,16 @@ #include "Readback.h" -#include "pipeline/skia/LayerDrawable.h" -#include "renderthread/EglManager.h" -#include "renderthread/VulkanManager.h" - -#include <gui/Surface.h> -#include <ui/Fence.h> +#include <sync/sync.h> +#include <system/window.h> #include <ui/GraphicBuffer.h> + #include "DeferredLayerUpdater.h" #include "Properties.h" #include "hwui/Bitmap.h" +#include "pipeline/skia/LayerDrawable.h" +#include "renderthread/EglManager.h" +#include "renderthread/VulkanManager.h" #include "utils/Color.h" #include "utils/MathUtils.h" #include "utils/TraceUtils.h" @@ -35,40 +35,43 @@ using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { -CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { +CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap) { ATRACE_CALL(); // Setup the source - sp<GraphicBuffer> sourceBuffer; - sp<Fence> sourceFence; + AHardwareBuffer* rawSourceBuffer; + int rawSourceFence; Matrix4 texTransform; - status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); + status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence, + texTransform.data); + base::unique_fd sourceFence(rawSourceFence); texTransform.invalidateType(); if (err != NO_ERROR) { ALOGW("Failed to get last queued buffer, error = %d", err); return CopyResult::UnknownError; } - if (!sourceBuffer.get()) { + if (rawSourceBuffer == nullptr) { ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); return CopyResult::SourceEmpty; } - if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { + + std::unique_ptr<AHardwareBuffer, decltype(&AHardwareBuffer_release)> sourceBuffer( + rawSourceBuffer, AHardwareBuffer_release); + AHardwareBuffer_Desc description; + AHardwareBuffer_describe(sourceBuffer.get(), &description); + if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { ALOGW("Surface is protected, unable to copy from it"); return CopyResult::SourceInvalid; } - err = sourceFence->wait(500 /* ms */); - if (err != NO_ERROR) { + + if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) { ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); return CopyResult::Timeout; } - if (!sourceBuffer.get()) { - return CopyResult::UnknownError; - } - sk_sp<SkColorSpace> colorSpace = - DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace())); - sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), - kPremul_SkAlphaType, colorSpace); + sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace( + static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window))); + sk_sp<SkImage> image = + SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace); return copyImageInto(image, texTransform, srcRect, bitmap); } diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index e86a8136cfa3..e36f1ff6a072 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -47,7 +47,7 @@ public: /** * Copies the surface's most recently queued buffer into the provided bitmap. */ - CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap); + CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap); CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index f9e401a2e93b..1e7fc71a7f04 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -317,8 +317,9 @@ void RenderProxy::setRenderAheadDepth(int renderAhead) { int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); + ANativeWindow* window = surface.get(); return static_cast<int>(thread.queue().runSync([&]() -> auto { - return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap); + return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap); })); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 4683e1d69019..ab0dd2bcc8f5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -140,6 +140,10 @@ public: */ ANDROID_API void setRenderAheadDepth(int renderAhead); + // TODO: This api will need to take in an ANativeWindow instead, but the + // caller, ThreadedRenderer, doesn't have access to libandroid due to a + // circular dependency, so it can't use the JNI ANativeWindow methods. Once + // that is resolved then replace the surface type here. ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java index 05fa511fc81a..2bffe8ac0649 100644 --- a/media/java/android/media/MediaScannerConnection.java +++ b/media/java/android/media/MediaScannerConnection.java @@ -156,9 +156,7 @@ public class MediaScannerConnection implements ServiceConnection { } BackgroundThread.getExecutor().execute(() -> { final Uri uri = scanFileQuietly(mProvider, new File(path)); - if (mClient != null) { - mClient.onScanCompleted(path, uri); - } + runCallBack(mContext, mClient, path, uri); }); } } @@ -187,9 +185,7 @@ public class MediaScannerConnection implements ServiceConnection { .acquireContentProviderClient(MediaStore.AUTHORITY)) { for (String path : paths) { final Uri uri = scanFileQuietly(client, new File(path)); - if (callback != null) { - callback.onScanCompleted(path, uri); - } + runCallBack(context, callback, path, uri); } } }); @@ -206,6 +202,23 @@ public class MediaScannerConnection implements ServiceConnection { return uri; } + private static void runCallBack(Context context, OnScanCompletedListener callback, + String path, Uri uri) { + if (callback != null) { + // Ignore exceptions from callback to avoid calling app from crashing. + // Don't ignore exceptions for apps targeting 'R' or higher. + try { + callback.onScanCompleted(path, uri); + } catch (Throwable e) { + if (context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) { + throw e; + } else { + Log.w(TAG, "Ignoring exception from callback for backward compatibility", e); + } + } + } + } + @Deprecated static class ClientProxy implements MediaScannerConnectionClient { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 9b183a3e0e92..44142e324962 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -532,6 +532,8 @@ public class Tuner implements AutoCloseable { Filter filter = nativeOpenFilter( mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize); if (filter != null) { + filter.setMainType(mainType); + filter.setSubtype(subType); filter.setCallback(cb); if (mHandler == null) { mHandler = createEventHandler(); diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 06de6e8a83c7..a98183bbf1a3 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerConstants.Result; +import android.media.tv.tuner.TunerUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -177,6 +178,8 @@ public class Filter implements AutoCloseable { private long mNativeContext; private FilterCallback mCallback; private final int mId; + private int mMainType; + private int mSubtype; private native int nativeConfigureFilter( int type, int subType, FilterConfiguration settings); @@ -197,6 +200,15 @@ public class Filter implements AutoCloseable { } /** @hide */ + public void setMainType(@Type int mainType) { + mMainType = mainType; + } + /** @hide */ + public void setSubtype(@Subtype int subtype) { + mSubtype = subtype; + } + + /** @hide */ public void setCallback(FilterCallback cb) { mCallback = cb; } @@ -213,10 +225,13 @@ public class Filter implements AutoCloseable { */ @Result public int configure(@NonNull FilterConfiguration config) { - int subType = -1; + // TODO: validate main type, subtype, config, settings + int subType; Settings s = config.getSettings(); if (s != null) { subType = s.getType(); + } else { + subType = TunerUtils.getFilterSubtype(mMainType, mSubtype); } return nativeConfigureFilter(config.getType(), subType, config); } diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java index bf5aaeda4742..a8dbfa5b11ec 100644 --- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java @@ -160,6 +160,12 @@ public class IpFilterConfiguration extends FilterConfiguration { */ @NonNull public IpFilterConfiguration build() { + int ipAddrLength = mSrcIpAddress.length; + if (ipAddrLength != mDstIpAddress.length || (ipAddrLength != 4 && ipAddrLength != 16)) { + throw new IllegalArgumentException( + "The lengths of src and dst IP address must be 4 or 16 and must be the same." + + "srcLength=" + ipAddrLength + ", dstLength=" + mDstIpAddress.length); + } return new IpFilterConfiguration( mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough); } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 08c3f988e85e..ac59003de5e6 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -27,16 +27,36 @@ #pragma GCC diagnostic ignored "-Wunused-function" using ::android::hardware::Void; +using ::android::hardware::hidl_bitfield; using ::android::hardware::hidl_vec; using ::android::hardware::tv::tuner::V1_0::DataFormat; +using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress; +using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid; using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; +using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType; +using ::android::hardware::tv::tuner::V1_0::DemuxScHevcIndex; +using ::android::hardware::tv::tuner::V1_0::DemuxScIndex; +using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxTpid; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxTsIndex; using ::android::hardware::tv::tuner::V1_0::DvrSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard; @@ -111,6 +131,9 @@ struct fields_t { static fields_t gFields; +static int IP_V4_LENGTH = 4; +static int IP_V6_LENGTH = 16; + namespace android { /////////////// LnbCallback /////////////////////// LnbCallback::LnbCallback(jweak tunerObj, LnbId id) : mObject(tunerObj), mId(id) {} @@ -1367,38 +1390,352 @@ static jobject android_media_tv_Tuner_open_time_filter(JNIEnv, jobject) { return NULL; } -static DemuxFilterSettings getFilterSettings( - JNIEnv *env, int type, int subtype, jobject filterSettingsObj) { +static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"); + jbyteArray jfilterBytes = static_cast<jbyteArray>( + env->GetObjectField(settings, env->GetFieldID(clazz, "mFilter", "[B"))); + jsize size = env->GetArrayLength(jfilterBytes); + std::vector<uint8_t> filterBytes(size); + env->GetByteArrayRegion( + jfilterBytes, 0, size, reinterpret_cast<jbyte*>(&filterBytes[0])); + + jbyteArray jmask = static_cast<jbyteArray>( + env->GetObjectField(settings, env->GetFieldID(clazz, "mMask", "[B"))); + size = env->GetArrayLength(jmask); + std::vector<uint8_t> mask(size); + env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast<jbyte*>(&mask[0])); + + jbyteArray jmode = static_cast<jbyteArray>( + env->GetObjectField(settings, env->GetFieldID(clazz, "mMode", "[B"))); + size = env->GetArrayLength(jmode); + std::vector<uint8_t> mode(size); + env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast<jbyte*>(&mode[0])); + + DemuxFilterSectionBits filterSectionBits { + .filter = filterBytes, + .mask = mask, + .mode = mode, + }; + return filterSectionBits; +} + +static DemuxFilterSectionSettings::Condition::TableInfo getFilterTableInfo( + JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"); + uint16_t tableId = static_cast<uint16_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I"))); + uint16_t version = static_cast<uint16_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I"))); + DemuxFilterSectionSettings::Condition::TableInfo tableInfo { + .tableId = tableId, + .version = version, + }; + return tableInfo; +} + +static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettings"); + bool isCheckCrc = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z"))); + bool isRepeat = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z"))); + bool isRaw = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"))); + + DemuxFilterSectionSettings filterSectionSettings { + .isCheckCrc = isCheckCrc, + .isRepeat = isRepeat, + .isRaw = isRaw, + }; + if (env->IsInstanceOf( + settings, + env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) { + filterSectionSettings.condition.sectionBits(getFilterSectionBits(env, settings)); + } else if (env->IsInstanceOf( + settings, + env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) { + filterSectionSettings.condition.tableInfo(getFilterTableInfo(env, settings)); + } + return filterSectionSettings; +} + +static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings"); + bool isPassthrough = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z"))); + DemuxFilterAvSettings filterAvSettings { + .isPassthrough = isPassthrough, + }; + return filterAvSettings; +} + +static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings"); + uint16_t streamId = static_cast<uint16_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"))); + bool isRaw = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"))); + DemuxFilterPesDataSettings filterPesDataSettings { + .streamId = streamId, + .isRaw = isRaw, + }; + return filterPesDataSettings; +} + +static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/RecordSettings"); + hidl_bitfield<DemuxTsIndex> tsIndexMask = static_cast<hidl_bitfield<DemuxTsIndex>>( + env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I"))); + DemuxRecordScIndexType scIndexType = static_cast<DemuxRecordScIndexType>( + env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I"))); + jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I")); + + DemuxFilterRecordSettings filterRecordSettings { + .tsIndexMask = tsIndexMask, + .scIndexType = scIndexType, + }; + if (scIndexType == DemuxRecordScIndexType::SC) { + filterRecordSettings.scIndexMask.sc(static_cast<hidl_bitfield<DemuxScIndex>>(scIndexMask)); + } else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) { + filterRecordSettings.scIndexMask.scHevc( + static_cast<hidl_bitfield<DemuxScHevcIndex>>(scIndexMask)); + } + return filterRecordSettings; +} + +static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings"); + uint32_t downloadId = static_cast<uint32_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"))); + + DemuxFilterDownloadSettings filterDownloadSettings { + .downloadId = downloadId, + }; + return filterDownloadSettings; +} + +static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/IpFilterConfiguration"); + + jbyteArray jsrcIpAddress = static_cast<jbyteArray>( + env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B"))); + jsize srcSize = env->GetArrayLength(jsrcIpAddress); + jbyteArray jdstIpAddress = static_cast<jbyteArray>( + env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B"))); + jsize dstSize = env->GetArrayLength(jdstIpAddress); + + DemuxIpAddress res; + + if (srcSize != dstSize) { + // should never happen. Validated on Java size. + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize); + return res; + } + + if (srcSize == IP_V4_LENGTH) { + uint8_t srcAddr[IP_V4_LENGTH]; + uint8_t dstAddr[IP_V4_LENGTH]; + env->GetByteArrayRegion( + jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr)); + env->GetByteArrayRegion( + jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr)); + res.srcIpAddress.v4(srcAddr); + res.dstIpAddress.v4(dstAddr); + } else if (srcSize == IP_V6_LENGTH) { + uint8_t srcAddr[IP_V6_LENGTH]; + uint8_t dstAddr[IP_V6_LENGTH]; + env->GetByteArrayRegion( + jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr)); + env->GetByteArrayRegion( + jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr)); + res.srcIpAddress.v6(srcAddr); + res.dstIpAddress.v6(dstAddr); + } else { + // should never happen. Validated on Java size. + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Invalid IP address length %d", srcSize); + return res; + } + + uint16_t srcPort = static_cast<uint16_t>( + env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"))); + uint16_t dstPort = static_cast<uint16_t>( + env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"))); + + res.srcPort = srcPort; + res.dstPort = dstPort; + + return res; +} + +static DemuxFilterSettings getFilterConfiguration( + JNIEnv *env, int type, int subtype, jobject filterConfigObj) { DemuxFilterSettings filterSettings; - // TODO: more setting types jobject settingsObj = env->GetObjectField( - filterSettingsObj, + filterConfigObj, env->GetFieldID( env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"), "mSettings", "Landroid/media/tv/tuner/filter/Settings;")); - if (type == (int)DemuxFilterMainType::TS) { - // DemuxTsFilterSettings - jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration"); - int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I")); - if (subtype == (int)DemuxTsFilterType::PES) { - // DemuxFilterPesDataSettings - jclass settingClazz = - env->FindClass("android/media/tv/tuner/filter/PesSettings"); - int streamId = env->GetIntField( - settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I")); - bool isRaw = (bool)env->GetBooleanField( - settingsObj, env->GetFieldID(settingClazz, "mIsRaw", "Z")); - DemuxFilterPesDataSettings filterPesDataSettings { - .streamId = static_cast<uint16_t>(streamId), - .isRaw = isRaw, - }; + DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type); + switch (mainType) { + case DemuxFilterMainType::TS: { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration"); + uint16_t tpid = static_cast<uint16_t>( + env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I"))); DemuxTsFilterSettings tsFilterSettings { - .tpid = static_cast<uint16_t>(tpid), + .tpid = tpid, }; - tsFilterSettings.filterSettings.pesData(filterPesDataSettings); + + DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype); + switch (tsType) { + case DemuxTsFilterType::SECTION: + tsFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxTsFilterType::AUDIO: + case DemuxTsFilterType::VIDEO: + tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxTsFilterType::PES: + tsFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxTsFilterType::RECORD: + tsFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + default: + break; + } filterSettings.ts(tsFilterSettings); + break; + } + case DemuxFilterMainType::MMTP: { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/MmtpFilterConfiguration"); + uint16_t mmtpPid = static_cast<uint16_t>( + env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I"))); + DemuxMmtpFilterSettings mmtpFilterSettings { + .mmtpPid = mmtpPid, + }; + DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype); + switch (mmtpType) { + case DemuxMmtpFilterType::SECTION: + mmtpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::AUDIO: + case DemuxMmtpFilterType::VIDEO: + mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::PES: + mmtpFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::RECORD: + mmtpFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::DOWNLOAD: + mmtpFilterSettings.filterSettings.download( + getFilterDownloadSettings(env, settingsObj)); + break; + default: + break; + } + filterSettings.mmtp(mmtpFilterSettings); + break; + } + case DemuxFilterMainType::IP: { + DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj); + + DemuxIpFilterSettings ipFilterSettings { + .ipAddr = ipAddr, + }; + DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype); + switch (ipType) { + case DemuxIpFilterType::SECTION: { + ipFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + } + case DemuxIpFilterType::IP: { + jclass clazz = env->FindClass( + "android/media/tv/tuner/filter/IpFilterConfiguration"); + bool bPassthrough = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID( + clazz, "mPassthrough", "Z"))); + ipFilterSettings.filterSettings.bPassthrough(bPassthrough); + break; + } + default: { + break; + } + } + filterSettings.ip(ipFilterSettings); + break; + } + case DemuxFilterMainType::TLV: { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/TlvFilterConfiguration"); + uint8_t packetType = static_cast<uint8_t>( + env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I"))); + bool isCompressedIpPacket = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z"))); + + DemuxTlvFilterSettings tlvFilterSettings { + .packetType = packetType, + .isCompressedIpPacket = isCompressedIpPacket, + }; + DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype); + switch (tlvType) { + case DemuxTlvFilterType::SECTION: { + tlvFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + } + case DemuxTlvFilterType::TLV: { + bool bPassthrough = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID( + clazz, "mPassthrough", "Z"))); + tlvFilterSettings.filterSettings.bPassthrough(bPassthrough); + break; + } + default: { + break; + } + } + filterSettings.tlv(tlvFilterSettings); + break; + } + case DemuxFilterMainType::ALP: { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/AlpFilterConfiguration"); + uint8_t packetType = static_cast<uint8_t>( + env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I"))); + DemuxAlpLengthType lengthType = static_cast<DemuxAlpLengthType>( + env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mLengthType", "I"))); + DemuxAlpFilterSettings alpFilterSettings { + .packetType = packetType, + .lengthType = lengthType, + }; + DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype); + switch (alpType) { + case DemuxAlpFilterType::SECTION: + alpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + default: + break; + } + filterSettings.alp(alpFilterSettings); + break; + } + default: { + break; } } return filterSettings; @@ -1439,7 +1776,7 @@ static int android_media_tv_Tuner_configure_filter( ALOGD("Failed to configure filter: filter not found"); return (int)Result::INVALID_STATE; } - DemuxFilterSettings filterSettings = getFilterSettings(env, type, subtype, settings); + DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings); Result res = iFilterSp->configure(filterSettings); MQDescriptorSync<uint8_t> filterMQDesc; if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) { diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index 3ffb9514a98b..615dc48b969c 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -70,8 +70,8 @@ import java.util.function.Predicate; @RunWith(AndroidJUnit4.class) @SmallTest -public class MediaRouterManagerTest { - private static final String TAG = "MediaRouterManagerTest"; +public class MediaRouter2ManagerTest { + private static final String TAG = "MediaRouter2ManagerTest"; private static final int TIMEOUT_MS = 5000; private Context mContext; diff --git a/media/tests/TunerTest/Android.bp b/media/tests/TunerTest/Android.bp new file mode 100644 index 000000000000..cef879112225 --- /dev/null +++ b/media/tests/TunerTest/Android.bp @@ -0,0 +1,18 @@ +android_test { + name: "mediatunertest", + + srcs: ["**/*.java"], + + libs: [ + "android.test.runner", + "android.test.base", + ], + + static_libs: [ + "android-support-test", + "testng" + ], + + platform_apis: true, + certificate: "platform", +} diff --git a/media/tests/TunerTest/AndroidManifest.xml b/media/tests/TunerTest/AndroidManifest.xml new file mode 100644 index 000000000000..17e9f197b68c --- /dev/null +++ b/media/tests/TunerTest/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.mediatunertest"> + + <uses-permission android:name="android.permission.ACCESS_TV_TUNER" /> + + <application android:label="@string/app_name"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.mediatunertest" + android:label="Media Tuner Tests"/> +</manifest> diff --git a/media/tests/TunerTest/AndroidTest.xml b/media/tests/TunerTest/AndroidTest.xml new file mode 100644 index 000000000000..d9c31f45532f --- /dev/null +++ b/media/tests/TunerTest/AndroidTest.xml @@ -0,0 +1,17 @@ +<configuration description="Runs Media Tuner tests."> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="MediaTunerTest"/> + + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="mediatunertest.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.mediatunertest"/> + <option name="hidden-api-checks" value="false"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/media/tests/TunerTest/res/values/strings.xml b/media/tests/TunerTest/res/values/strings.xml new file mode 100644 index 000000000000..b313944ed256 --- /dev/null +++ b/media/tests/TunerTest/res/values/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- name of the app [CHAR LIMIT=25]--> + <string name="app_name">MediaTunerTest</string> +</resources>
\ No newline at end of file diff --git a/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java new file mode 100644 index 000000000000..cbfbf77d11f5 --- /dev/null +++ b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 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 com.android.mediatunertest; + +import static org.junit.Assert.assertNotNull; + +import android.content.Context; +import android.media.tv.tuner.Descrambler; +import android.media.tv.tuner.Tuner; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TunerTest { + private static final String TAG = "MediaTunerTest"; + + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + } + + @After + public void tearDown() { + } + + @Test + public void testTunerConstructor() throws Exception { + Tuner tuner = new Tuner(mContext, "123", 1, null); + assertNotNull(tuner); + } + + @Test + public void testOpenDescrambler() throws Exception { + Tuner tuner = new Tuner(mContext, "123", 1, null); + Descrambler descrambler = tuner.openDescrambler(); + assertNotNull(descrambler); + } +} diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 62124934f416..3f42ad40eb8e 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -27,6 +27,7 @@ android_library { "SettingsLibRadioButtonPreference", "WifiTrackerLib", "SettingsLibDisplayDensityUtils", + "SettingsLibSchedulesProvider", ], // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp new file mode 100644 index 000000000000..ef592527ba92 --- /dev/null +++ b/packages/SettingsLib/SchedulesProvider/Android.bp @@ -0,0 +1,12 @@ +android_library { + name: "SettingsLibSchedulesProvider", + + srcs: ["src/**/*.java"], + + static_libs: [ + "androidx.annotation_annotation", + ], + + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml b/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml new file mode 100644 index 000000000000..1b0e4bfc1e1d --- /dev/null +++ b/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.schedulesprovider"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java new file mode 100644 index 000000000000..7d2b8e2878d6 --- /dev/null +++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java @@ -0,0 +1,167 @@ +/* + * 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.settingslib.schedulesprovider; + +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +/** + * This is a schedule data item. It contains the schedule title text, the summary text which + * displays on the summary of the Settings preference and an {@link Intent}. Intent is able to + * launch the editing page of the schedule data when user clicks this item (preference). + */ +public class ScheduleInfo implements Parcelable { + private static final String TAG = "ScheduleInfo"; + private final String mTitle; + private final String mSummary; + private final Intent mIntent; + + public ScheduleInfo(Builder builder) { + mTitle = builder.mTitle; + mSummary = builder.mSummary; + mIntent = builder.mIntent; + } + + protected ScheduleInfo(Parcel in) { + mTitle = in.readString(); + mSummary = in.readString(); + mIntent = in.readParcelable(Intent.class.getClassLoader()); + } + + /** + * Returns the title text. + * + * @return The title. + */ + public String getTitle() { + return mTitle; + } + + /** + * Returns the summary text. + * + * @return The summary. + */ + public String getSummary() { + return mSummary; + } + + /** + * Returns an {@link Intent}. + */ + public Intent getIntent() { + return mIntent; + } + + /** + * Verify the member variables are valid. + * + * @return {@code true} if all member variables are valid. + */ + public boolean isValid() { + return !TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mSummary) && (mIntent != null); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mTitle); + dest.writeString(mSummary); + dest.writeParcelable(mIntent, flags); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<ScheduleInfo> CREATOR = new Creator<ScheduleInfo>() { + @Override + public ScheduleInfo createFromParcel(Parcel in) { + return new ScheduleInfo(in); + } + + @Override + public ScheduleInfo[] newArray(int size) { + return new ScheduleInfo[size]; + } + }; + + @NonNull + @Override + public String toString() { + return "title : " + mTitle + " summary : " + mSummary + (mIntent == null + ? " and intent is null." : "."); + } + + /** + * A simple builder for {@link ScheduleInfo}. + */ + public static class Builder { + @NonNull + private String mTitle; + @NonNull + private String mSummary; + @NonNull + private Intent mIntent; + + /** + * Sets the title. + * + * @param title The title of the preference item. + * @return This instance. + */ + public Builder setTitle(@NonNull String title) { + mTitle = title; + return this; + } + + /** + * Sets the summary. + * + * @param summary The summary of the preference summary. + * @return This instance. + */ + public Builder setSummary(@NonNull String summary) { + mSummary = summary; + return this; + } + + /** + * Sets the {@link Intent}. + * + * @param intent The action when user clicks the preference. + * @return This instance. + */ + public Builder setIntent(@NonNull Intent intent) { + mIntent = intent; + return this; + } + + /** + * Creates an instance of {@link ScheduleInfo}. + * + * @return The instance of {@link ScheduleInfo}. + */ + public ScheduleInfo build() { + return new ScheduleInfo(this); + } + } +} diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java new file mode 100644 index 000000000000..a423e475d357 --- /dev/null +++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java @@ -0,0 +1,133 @@ +/* + * 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.settingslib.schedulesprovider; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.SystemProperties; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This provider is a bridge for client apps to provide the schedule data. + * Client provider needs to implement their {@link #getScheduleInfoList()} and returns a list of + * {@link ScheduleInfo}. + */ +public abstract class SchedulesProvider extends ContentProvider { + public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList"; + public static final String BUNDLE_SCHEDULE_INFO_LIST = "scheduleInfoList"; + private static final String TAG = "SchedulesProvider"; + + @Override + public boolean onCreate() { + return true; + } + + @Override + public final Cursor query( + Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + throw new UnsupportedOperationException("Query operation is not supported currently."); + } + + @Override + public final String getType(Uri uri) { + throw new UnsupportedOperationException("GetType operation is not supported currently."); + } + + @Override + public final Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("Insert operation is not supported currently."); + } + + @Override + public final int delete(Uri uri, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("Delete operation not supported currently."); + } + + @Override + public final int update(Uri uri, ContentValues values, String selection, + String[] selectionArgs) { + throw new UnsupportedOperationException("Update operation is not supported currently."); + } + + /** + * Return the list of the schedule information. + * + * @return a list of the {@link ScheduleInfo}. + */ + public abstract ArrayList<ScheduleInfo> getScheduleInfoList(); + + /** + * Returns a bundle which contains a list of {@link ScheduleInfo} and data types: + * scheduleInfoList : ArrayList<ScheduleInfo> + */ + @Override + public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { + final Bundle bundle = new Bundle(); + if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) { + final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData( + getScheduleInfoList()); + if (scheduleInfoList != null) { + bundle.putParcelableArrayList(BUNDLE_SCHEDULE_INFO_LIST, scheduleInfoList); + } + } + return bundle; + } + + /** + * To filter the invalid schedule info. + * + * @param scheduleInfoList The list of the {@link ScheduleInfo}. + * @return The valid list of the {@link ScheduleInfo}. + */ + private ArrayList<ScheduleInfo> filterInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) { + if (scheduleInfoList == null) { + Log.d(TAG, "package : " + getContext().getPackageName() + " has no scheduling data."); + return null; + } + // Dump invalid data in debug mode. + if (SystemProperties.getInt("ro.debuggable", 0) == 1) { + new Thread(() -> { + dumpInvalidData(scheduleInfoList); + }).start(); + } + final List<ScheduleInfo> filteredList = scheduleInfoList + .stream() + .filter(scheduleInfo -> scheduleInfo.isValid()) + .collect(Collectors.toList()); + + return new ArrayList<>(filteredList); + } + + private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) { + Log.d(TAG, "package : " + getContext().getPackageName() + + " provided some scheduling data are invalid."); + scheduleInfoList + .stream() + .filter(scheduleInfo -> !scheduleInfo.isValid()) + .forEach(scheduleInfo -> Log.d(TAG, scheduleInfo.toString())); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index e91096760180..8e4a982fde8c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -74,13 +74,6 @@ public class InfoMediaManager extends MediaManager { refreshDevices(); } - @VisibleForTesting - String getControlCategoryByPackageName(String packageName) { - //TODO(b/117129183): Use package name to get ControlCategory. - //Since api not ready, return fixed ControlCategory for prototype. - return "com.google.android.gms.cast.CATEGORY_CAST"; - } - @Override public void stopScan() { mRouterManager.unregisterCallback(mMediaRouterCallback); @@ -192,5 +185,10 @@ public class InfoMediaManager extends MediaManager { public void onRoutesChanged(List<MediaRoute2Info> routes) { refreshDevices(); } + + @Override + public void onRoutesRemoved(List<MediaRoute2Info> routes) { + refreshDevices(); + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 984ab11a1a71..f8db70ae0963 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -16,6 +16,8 @@ package com.android.settingslib.media; import android.app.Notification; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.text.TextUtils; @@ -26,13 +28,13 @@ import androidx.annotation.IntDef; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -66,9 +68,16 @@ public class LocalMediaManager implements BluetoothCallback { @VisibleForTesting List<MediaDevice> mMediaDevices = new ArrayList<>(); @VisibleForTesting + List<MediaDevice> mDisconnectedMediaDevices = new ArrayList<>(); + @VisibleForTesting MediaDevice mPhoneDevice; @VisibleForTesting MediaDevice mCurrentConnectedDevice; + @VisibleForTesting + DeviceAttributeChangeCallback mDeviceAttributeChangeCallback = + new DeviceAttributeChangeCallback(); + @VisibleForTesting + BluetoothAdapter mBluetoothAdapter; /** * Register to start receiving callbacks for MediaDevice events. @@ -89,6 +98,7 @@ public class LocalMediaManager implements BluetoothCallback { mPackageName = packageName; mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null); + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mLocalBluetoothManager == null) { Log.e(TAG, "Bluetooth is not supported on this device"); return; @@ -164,7 +174,7 @@ public class LocalMediaManager implements BluetoothCallback { } void dispatchDeviceListUpdate() { - Collections.sort(mMediaDevices, COMPARATOR); + //TODO(b/149260820): Use new rule to rank device once device type api is ready. for (DeviceCallback callback : getCallbacks()) { callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices)); } @@ -278,12 +288,44 @@ public class LocalMediaManager implements BluetoothCallback { public void onDeviceListAdded(List<MediaDevice> devices) { mMediaDevices.clear(); mMediaDevices.addAll(devices); + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); mCurrentConnectedDevice = infoMediaDevice != null ? infoMediaDevice : updateCurrentConnectedDevice(); dispatchDeviceListUpdate(); } + private List<MediaDevice> buildDisconnectedBluetoothDevice() { + for (MediaDevice device : mDisconnectedMediaDevices) { + ((BluetoothMediaDevice) device).getCachedDevice() + .unregisterCallback(mDeviceAttributeChangeCallback); + } + mDisconnectedMediaDevices.clear(); + final List<BluetoothDevice> bluetoothDevices = + mBluetoothAdapter.getMostRecentlyConnectedDevices(); + final CachedBluetoothDeviceManager cachedDeviceManager = + mLocalBluetoothManager.getCachedDeviceManager(); + + for (BluetoothDevice device : bluetoothDevices) { + final CachedBluetoothDevice cachedDevice = + cachedDeviceManager.findDevice(device); + if (cachedDevice != null) { + if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED + && !cachedDevice.isConnected()) { + final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext, + cachedDevice, + null, null, mPackageName); + if (!mMediaDevices.contains(mediaDevice)) { + cachedDevice.registerCallback(mDeviceAttributeChangeCallback); + mDisconnectedMediaDevices.add(mediaDevice); + } + } + } + } + return new ArrayList<>(mDisconnectedMediaDevices); + } + @Override public void onDeviceRemoved(MediaDevice device) { if (mMediaDevices.contains(device)) { @@ -345,4 +387,17 @@ public class LocalMediaManager implements BluetoothCallback { */ default void onDeviceAttributesChanged() {}; } + + /** + * This callback is for update {@link BluetoothMediaDevice} summary when + * {@link CachedBluetoothDevice} connection state is changed. + */ + @VisibleForTesting + class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback { + + @Override + public void onDeviceAttributesChanged() { + dispatchDeviceAttributesChanged(); + } + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index 05f5fa0afc9e..8b815bfba19d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -203,4 +203,46 @@ public class InfoMediaManagerTest { assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse(); } + + @Test + public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() { + final MediaRoute2Info info = mock(MediaRoute2Info.class); + when(info.getId()).thenReturn(TEST_ID); + when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + + final List<MediaRoute2Info> routes = new ArrayList<>(); + routes.add(info); + when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes); + + final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); + assertThat(mediaDevice).isNull(); + + mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes); + + final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); + assertThat(infoDevice.getId()).isEqualTo(TEST_ID); + assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice); + assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); + } + + @Test + public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() { + final MediaRoute2Info info = mock(MediaRoute2Info.class); + when(info.getId()).thenReturn(TEST_ID); + when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + + final List<MediaRoute2Info> routes = new ArrayList<>(); + routes.add(info); + when(mRouterManager.getAllRoutes()).thenReturn(routes); + + final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); + assertThat(mediaDevice).isNull(); + + mInfoMediaManager.mPackageName = ""; + mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes); + + final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); + assertThat(infoDevice.getId()).isEqualTo(TEST_ID); + assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 3d67ba053a45..3611dfefbb7f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -25,13 +25,17 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.content.Context; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; import org.junit.Test; @@ -40,11 +44,14 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class LocalMediaManagerTest { private static final String TEST_DEVICE_ID_1 = "device_id_1"; @@ -69,11 +76,15 @@ public class LocalMediaManagerTest { private Context mContext; private LocalMediaManager mLocalMediaManager; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; + final List<BluetoothDevice> bluetoothDevices = new ArrayList<>(); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager); when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); @@ -81,6 +92,7 @@ public class LocalMediaManagerTest { mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, mInfoMediaManager, "com.test.packagename"); + mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } @Test @@ -419,4 +431,50 @@ public class LocalMediaManagerTest { MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); assertThat(activeDevices).containsExactly(device3); } + + @Test + public void onDeviceAttributesChanged_shouldBeCalled() { + mLocalMediaManager.registerCallback(mCallback); + + mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged(); + + verify(mCallback).onDeviceAttributesChanged(); + } + + @Test + public void onDeviceListAdded_haveDisconnectedDevice_addDisconnectedDevice() { + final List<MediaDevice> devices = new ArrayList<>(); + final MediaDevice device1 = mock(MediaDevice.class); + final MediaDevice device2 = mock(MediaDevice.class); + final MediaDevice device3 = mock(MediaDevice.class); + mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class); + devices.add(device1); + devices.add(device2); + mLocalMediaManager.mMediaDevices.add(device3); + mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice); + + final List<BluetoothDevice> bluetoothDevices = new ArrayList<>(); + final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class); + final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + final CachedBluetoothDeviceManager cachedManager = mock(CachedBluetoothDeviceManager.class); + bluetoothDevices.add(bluetoothDevice); + mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices); + + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager); + when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); + when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(cachedDevice.isConnected()).thenReturn(false); + + when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); + when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); + when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); + + assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); + mLocalMediaManager.registerCallback(mCallback); + mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); + + assertThat(mLocalMediaManager.mMediaDevices).hasSize(3); + verify(mCallback).onDeviceListUpdate(any()); + } } diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java index 906dba487734..015ce149a9c4 100644 --- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java +++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java @@ -17,6 +17,7 @@ package com.android.settingslib.testutils.shadow; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; @@ -29,6 +30,7 @@ import java.util.List; public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter { private List<Integer> mSupportedProfiles; + private List<BluetoothDevice> mMostRecentlyConnectedDevices; private BluetoothProfile.ServiceListener mServiceListener; @Implementation @@ -50,4 +52,13 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto public void setSupportedProfiles(List<Integer> supportedProfiles) { mSupportedProfiles = supportedProfiles; } + + @Implementation + protected List<BluetoothDevice> getMostRecentlyConnectedDevices() { + return mMostRecentlyConnectedDevices; + } + + public void setMostRecentlyConnectedDevices(List<BluetoothDevice> list) { + mMostRecentlyConnectedDevices = list; + } } diff --git a/packages/SettingsProvider/res/values/strings.xml b/packages/SettingsProvider/res/values/strings.xml index 378772750194..76bea3160afe 100644 --- a/packages/SettingsProvider/res/values/strings.xml +++ b/packages/SettingsProvider/res/values/strings.xml @@ -23,15 +23,10 @@ <!-- A notification is shown when the user's softap config has been changed due to underlying hardware restrictions. This is the notifications's title. [CHAR_LIMIT=NONE] --> - <string name="wifi_softap_config_change">Changes to your hotspot settings</string> + <string name="wifi_softap_config_change">Hotspot settings have changed</string> <!-- A notification is shown when the user's softap config has been changed due to underlying hardware restrictions. This is the notification's summary message. [CHAR_LIMIT=NONE] --> - <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string> - - <!-- A notification is shown when the user's softap config has been changed due to underlying - hardware restrictions. This is the notification's full message. - [CHAR_LIMIT=NONE] --> - <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string> + <string name="wifi_softap_config_change_summary">Tap to see details</string> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java index 1ee5f9093421..ca841a5cdcd6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java @@ -57,7 +57,6 @@ public class WifiSoftApConfigChangedNotifier { Resources resources = context.getResources(); CharSequence title = resources.getText(R.string.wifi_softap_config_change); CharSequence contentSummary = resources.getText(R.string.wifi_softap_config_change_summary); - CharSequence content = resources.getText(R.string.wifi_softap_config_change_detailed); int color = resources.getColor( android.R.color.system_notification_accent_color, context.getTheme()); @@ -73,7 +72,6 @@ public class WifiSoftApConfigChangedNotifier { .setLocalOnly(true) .setColor(color) .setStyle(new Notification.BigTextStyle() - .bigText(content) .setBigContentTitle(title) .setSummaryText(contentSummary)) .setAutoCancel(true) diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index cc2c92b4c7eb..d821050ac5b4 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -253,7 +253,8 @@ <!-- Permission required for CTS test - ShortcutManagerUsageTest --> <uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/> - <!-- Permission required for CTS test - UsageStatsTest --> + <!-- Permissions required for CTS test - UsageStatsTest --> + <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS"/> <uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"/> <!-- Permissions required to test ambient display. --> diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java index 0f7f1bebae57..023b74b9c07e 100644 --- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java @@ -19,7 +19,6 @@ import static android.widget.Toast.Duration; import android.annotation.StringRes; import android.content.Context; -import android.view.WindowManager; import android.widget.Toast; public class SysUIToast { @@ -29,10 +28,7 @@ public class SysUIToast { } public static Toast makeText(Context context, CharSequence text, @Duration int duration) { - Toast toast = Toast.makeText(context, text, duration); - toast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - return toast; + return Toast.makeText(context, text, duration); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index 68b05e358786..9de10406a822 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -16,6 +16,7 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; import android.content.Context; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricPrompt; @@ -33,6 +34,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -126,18 +129,18 @@ public abstract class AuthCredentialView extends LinearLayout { mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS); } - private void setTextOrHide(TextView view, String string) { - if (TextUtils.isEmpty(string)) { + private void setTextOrHide(TextView view, CharSequence text) { + if (TextUtils.isEmpty(text)) { view.setVisibility(View.GONE); } else { - view.setText(string); + view.setText(text); } Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); } - private void setText(TextView view, String string) { - view.setText(string); + private void setText(TextView view, CharSequence text) { + view.setText(text); } void setEffectiveUserId(int effectiveUserId) { @@ -173,11 +176,9 @@ public abstract class AuthCredentialView extends LinearLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); - setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE)); - setTextOrHide(mSubtitleView, - mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE)); - setTextOrHide(mDescriptionView, - mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION)); + setText(mTitleView, getTitle(mBiometricPromptBundle)); + setTextOrHide(mSubtitleView, getSubtitle(mBiometricPromptBundle)); + setTextOrHide(mDescriptionView, getDescription(mBiometricPromptBundle)); final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId); final Drawable image; @@ -279,4 +280,28 @@ public abstract class AuthCredentialView extends LinearLayout { } } } + + @Nullable + private static CharSequence getTitle(@NonNull Bundle bundle) { + final CharSequence credentialTitle = + bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE); + return credentialTitle != null ? credentialTitle + : bundle.getCharSequence(BiometricPrompt.KEY_TITLE); + } + + @Nullable + private static CharSequence getSubtitle(@NonNull Bundle bundle) { + final CharSequence credentialSubtitle = + bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE); + return credentialSubtitle != null ? credentialSubtitle + : bundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); + } + + @Nullable + private static CharSequence getDescription(@NonNull Bundle bundle) { + final CharSequence credentialDescription = + bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION); + return credentialDescription != null ? credentialDescription + : bundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt index ab8de263765e..5c1d332df7a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt @@ -105,8 +105,8 @@ class QSLogger @Inject constructor( fun logTileUpdated(tileSpec: String, state: QSTile.State) { log(VERBOSE, { str1 = tileSpec - str2 = state.label.toString() - str3 = state.icon.toString() + str2 = state.label?.toString() + str3 = state.icon?.toString() int1 = state.state if (state is QSTile.SignalState) { bool1 = true diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 573ea4dd85de..9f64b397e9d9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -352,6 +352,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis try { Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, + AccessibilityManager.ACCESSIBILITY_BUTTON); mContext.startActivityAsUser(intent, UserHandle.CURRENT); } finally { Binder.restoreCallingIdentity(token); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 4f27c0f04c3f..5b4a927bb8f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt @@ -26,26 +26,23 @@ import android.content.Context import android.content.DialogInterface import android.graphics.Color import android.graphics.PixelFormat -import android.graphics.drawable.Drawable import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable import android.util.Log import android.view.Gravity import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.Window -import android.view.WindowInsets.Type import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView import com.android.internal.annotations.VisibleForTesting - import com.android.systemui.R - import javax.inject.Inject import javax.inject.Singleton -const val TAG = "ChannelDialogController" +private const val TAG = "ChannelDialogController" /** * ChannelEditorDialogController is the controller for the dialog half-shelf @@ -149,9 +146,9 @@ class ChannelEditorDialogController @Inject constructor( val channels = groupList .flatMap { group -> group.channels.asSequence().filterNot { channel -> - channel.isImportanceLockedByOEM - || channel.importance == IMPORTANCE_NONE - || channel.isImportanceLockedByCriticalDeviceFunction + channel.isImportanceLockedByOEM || + channel.importance == IMPORTANCE_NONE || + channel.isImportanceLockedByCriticalDeviceFunction } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java index e2513dac44d8..d744fc398d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java @@ -74,11 +74,15 @@ import javax.inject.Singleton; @Singleton public final class NotifBindPipeline { private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>(); + private final NotifBindPipelineLogger mLogger; private BindStage mStage; @Inject - NotifBindPipeline(CommonNotifCollection collection) { + NotifBindPipeline( + CommonNotifCollection collection, + NotifBindPipelineLogger logger) { collection.addCollectionListener(mCollectionListener); + mLogger = logger; } /** @@ -86,6 +90,8 @@ public final class NotifBindPipeline { */ public void setStage( BindStage stage) { + mLogger.logStageSet(stage.getClass().getName()); + mStage = stage; mStage.setBindRequestListener(this::onBindRequested); } @@ -96,6 +102,8 @@ public final class NotifBindPipeline { public void manageRow( @NonNull NotificationEntry entry, @NonNull ExpandableNotificationRow row) { + mLogger.logManagedRow(entry.getKey()); + final BindEntry bindEntry = getBindEntry(entry); bindEntry.row = row; if (bindEntry.invalidated) { @@ -130,6 +138,8 @@ public final class NotifBindPipeline { * callbacks when the run finishes. If a run is already in progress, it is restarted. */ private void startPipeline(NotificationEntry entry) { + mLogger.logStartPipeline(entry.getKey()); + if (mStage == null) { throw new IllegalStateException("No stage was ever set on the pipeline"); } @@ -147,10 +157,11 @@ public final class NotifBindPipeline { private void onPipelineComplete(NotificationEntry entry) { final BindEntry bindEntry = getBindEntry(entry); + final Set<BindCallback> callbacks = bindEntry.callbacks; - bindEntry.invalidated = false; + mLogger.logFinishedPipeline(entry.getKey(), callbacks.size()); - final Set<BindCallback> callbacks = bindEntry.callbacks; + bindEntry.invalidated = false; for (BindCallback cb : callbacks) { cb.onBindFinished(entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt new file mode 100644 index 000000000000..2717d7ad143b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt @@ -0,0 +1,61 @@ +/* + * 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.statusbar.notification.row + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import javax.inject.Inject + +class NotifBindPipelineLogger @Inject constructor( + @NotificationLog private val buffer: LogBuffer +) { + fun logStageSet(stageName: String) { + buffer.log(TAG, INFO, { + str1 = stageName + }, { + "Stage set: $str1" + }) + } + + fun logManagedRow(notifKey: String) { + buffer.log(TAG, INFO, { + str1 = notifKey + }, { + "Row set for notif: $str1" + }) + } + + fun logStartPipeline(notifKey: String) { + buffer.log(TAG, INFO, { + str1 = notifKey + }, { + "Start pipeline for notif: $str1" + }) + } + + fun logFinishedPipeline(notifKey: String, numCallbacks: Int) { + buffer.log(TAG, INFO, { + str1 = notifKey + int1 = numCallbacks + }, { + "Finished pipeline for notif $str1 with $int1 callbacks" + }) + } +} + +private const val TAG = "NotifBindPipeline"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java index 5170d0b85b17..88ed0bb37d0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java @@ -157,6 +157,15 @@ public final class RowContentBindParams { return mViewsNeedReinflation; } + @Override + public String toString() { + return String.format("RowContentBindParams[mContentViews=%x mDirtyContentViews=%x " + + "mUseLowPriority=%b mUseChildInGroup=%b mUseIncreasedHeight=%b " + + "mUseIncreasedHeadsUpHeight=%b mViewsNeedReinflation=%b]", + mContentViews, mDirtyContentViews, mUseLowPriority, mUseChildInGroup, + mUseIncreasedHeight, mUseIncreasedHeadsUpHeight, mViewsNeedReinflation); + } + /** * Content views that should be inflated by default for all notifications. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java index f78324596fb4..c632f3eb22a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java @@ -38,13 +38,16 @@ import javax.inject.Singleton; public class RowContentBindStage extends BindStage<RowContentBindParams> { private final NotificationRowContentBinder mBinder; private final NotifInflationErrorManager mNotifInflationErrorManager; + private final RowContentBindStageLogger mLogger; @Inject RowContentBindStage( NotificationRowContentBinder binder, - NotifInflationErrorManager errorManager) { + NotifInflationErrorManager errorManager, + RowContentBindStageLogger logger) { mBinder = binder; mNotifInflationErrorManager = errorManager; + mLogger = logger; } @Override @@ -54,6 +57,8 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> { @NonNull StageCallback callback) { RowContentBindParams params = getStageParams(entry); + mLogger.logStageParams(entry.getKey(), params.toString()); + // Resolve content to bind/unbind. @InflationFlag int inflationFlags = params.getContentViews(); @InflationFlag int invalidatedFlags = params.getDirtyContentViews(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt new file mode 100644 index 000000000000..29cce3375c8a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt @@ -0,0 +1,37 @@ +/* + * 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.statusbar.notification.row + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import javax.inject.Inject + +class RowContentBindStageLogger @Inject constructor( + @NotificationLog private val buffer: LogBuffer +) { + fun logStageParams(notifKey: String, stageParams: String) { + buffer.log(TAG, INFO, { + str1 = notifKey + str2 = stageParams + }, { + "Invalidated notif $str1 with params: \n$str2" + }) + } +} + +private const val TAG = "RowContentBindStage"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 3f5215e1a639..fd8c71b48417 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -956,6 +956,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private boolean onAccessibilityLongClick(View v) { Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, + AccessibilityManager.ACCESSIBILITY_BUTTON); v.getContext().startActivityAsUser(intent, UserHandle.CURRENT); return true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java index 408bba48d422..6408f7a38133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java @@ -59,7 +59,7 @@ public class NotifBindPipelineTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); CommonNotifCollection collection = mock(CommonNotifCollection.class); - mBindPipeline = new NotifBindPipeline(collection); + mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class)); mBindPipeline.setStage(mStage); ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index fd5512d62968..7a1bd052a336 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -111,11 +111,13 @@ public class NotificationTestHelper { mock(NotifRemoteViewCache.class), mock(NotificationRemoteInputManager.class)); contentBinder.setInflateSynchronously(true); - mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class)); + mBindStage = new RowContentBindStage(contentBinder, + mock(NotifInflationErrorManager.class), + mock(RowContentBindStageLogger.class)); CommonNotifCollection collection = mock(CommonNotifCollection.class); - mBindPipeline = new NotifBindPipeline(collection); + mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class)); mBindPipeline.setStage(mBindStage); ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java index d9fe6551ba1c..0f2482ce9c4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java @@ -60,8 +60,10 @@ public class RowContentBindStageTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - mRowContentBindStage = new RowContentBindStage(mBinder, - mock(NotifInflationErrorManager.class)); + mRowContentBindStage = new RowContentBindStage( + mBinder, + mock(NotifInflationErrorManager.class), + mock(RowContentBindStageLogger.class)); mRowContentBindStage.createStageParams(mEntry); } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 75ec4b04eeed..6c8aaf465c44 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -16,8 +16,9 @@ package com.android.server.accessibility; -import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID; +import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE; import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER; +import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP; import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; @@ -39,6 +40,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.graphics.GraphicBuffer; +import android.graphics.ParcelableColorSpace; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -1021,13 +1023,15 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer(); final HardwareBuffer hardwareBuffer = HardwareBuffer.createFromGraphicBuffer(graphicBuffer); - final int colorSpaceId = screenshotBuffer.getColorSpace().getId(); + final ParcelableColorSpace colorSpace = + new ParcelableColorSpace(screenshotBuffer.getColorSpace()); // Send back the result. final Bundle payload = new Bundle(); payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, hardwareBuffer); - payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID, colorSpaceId); + payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace); + payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP, SystemClock.uptimeMillis()); callback.sendResult(payload); }, null).recycleOnUse()); } finally { diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index b74be7e3f345..a3b5a3e2dcf9 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -17,6 +17,7 @@ package com.android.server.accessibility.gestures; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT; @@ -24,6 +25,7 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT; @@ -31,6 +33,7 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT; @@ -132,6 +135,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( + mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this)); + mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this)); // Three-finger taps. mMultiFingerGestures.add( @@ -139,6 +145,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( + mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this)); + mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this)); // Four-finger taps. mMultiFingerGestures.add( @@ -146,6 +155,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( + mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, this)); + mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this)); // Two-finger swipes. mMultiFingerGestures.add( diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java index 20def6154977..e5340f10dc4c 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java @@ -42,10 +42,10 @@ class MultiFingerMultiTap extends GestureMatcher { // The acceptable distance the pointer can move and still count as a tap. private int mTouchSlop; // A tap counts when target number of fingers are down and up once. - private int mCompletedTapCount; + protected int mCompletedTapCount; // A flag set to true when target number of fingers have touched down at once before. // Used to indicate what next finger action should be. Down when false and lift when true. - private boolean mIsTargetFingerCountReached = false; + protected boolean mIsTargetFingerCountReached = false; // Store initial down points for slop checking and update when next down if is inside slop. private PointF[] mBases; // The points in bases that already have slop checked when onDown or onPointerDown. @@ -56,7 +56,11 @@ class MultiFingerMultiTap extends GestureMatcher { * @throws IllegalArgumentException if <code>fingers<code/> is less than 2 * or <code>taps<code/> is not positive. */ - MultiFingerMultiTap(Context context, int fingers, int taps, int gestureId, + MultiFingerMultiTap( + Context context, + int fingers, + int taps, + int gestureId, GestureMatcher.StateChangeListener listener) { super(gestureId, new Handler(context.getMainLooper()), listener); Preconditions.checkArgument(fingers >= 2); @@ -117,8 +121,7 @@ class MultiFingerMultiTap extends GestureMatcher { cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags); final PointF nearest = findNearestPoint(rawEvent, mTouchSlop, false); - if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) - && null != nearest) { + if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && null != nearest) { // Increase current tap count when the user have all fingers lifted // within the tap timeout since the target number of fingers are down. if (mIsTargetFingerCountReached) { @@ -169,8 +172,7 @@ class MultiFingerMultiTap extends GestureMatcher { } else { nearest = findNearestPoint(rawEvent, mDoubleTapSlop, true); } - if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) - && nearest != null) { + if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && nearest != null) { // The user have all fingers down within the tap timeout since first finger down, // setting the timeout for fingers to be lifted. if (currentFingerCount == mTargetFingerCount) { @@ -227,11 +229,11 @@ class MultiFingerMultiTap extends GestureMatcher { } /** - * Find the nearest location to the given event in the bases. - * If no one found, it could be not inside {@code slop}, filtered or empty bases. - * When {@code filterMatched} is true, if the location of given event matches one of the points - * in {@link #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location - * will be added to {@link #mExcludedPointsForDownSlopChecked}. + * Find the nearest location to the given event in the bases. If no one found, it could be not + * inside {@code slop}, filtered or empty bases. When {@code filterMatched} is true, if the + * location of given event matches one of the points in {@link + * #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location will be + * added to {@link #mExcludedPointsForDownSlopChecked}. * * @param event to find nearest point in bases. * @param slop to check to the given location of the event. diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java new file mode 100644 index 000000000000..7824fd902c9b --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java @@ -0,0 +1,71 @@ +/* + * 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.gestures; + +import android.content.Context; +import android.view.MotionEvent; + +/** + * This class matches gestures of the form multi-finger multi-tap and hold. The number of fingers + * and taps for each instance is specified in the constructor. + */ +class MultiFingerMultiTapAndHold extends MultiFingerMultiTap { + + MultiFingerMultiTapAndHold( + Context context, + int fingers, + int taps, + int gestureId, + GestureMatcher.StateChangeListener listener) { + super(context, fingers, taps, gestureId, listener); + } + + @Override + protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + super.onPointerDown(event, rawEvent, policyFlags); + if (mIsTargetFingerCountReached && mCompletedTapCount + 1 == mTargetTapCount) { + completeAfterLongPressTimeout(event, rawEvent, policyFlags); + } + } + + @Override + protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mCompletedTapCount + 1 == mTargetFingerCount) { + // Calling super.onUp would complete the multi-tap version of this. + cancelGesture(event, rawEvent, policyFlags); + } else { + super.onUp(event, rawEvent, policyFlags); + cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags); + } + } + + @Override + public String getGestureName() { + final StringBuilder builder = new StringBuilder(); + builder.append(mTargetFingerCount).append("-Finger "); + if (mTargetTapCount == 1) { + builder.append("Single"); + } else if (mTargetTapCount == 2) { + builder.append("Double"); + } else if (mTargetTapCount == 3) { + builder.append("Triple"); + } else if (mTargetTapCount > 3) { + builder.append(mTargetTapCount); + } + return builder.append(" Tap and hold").toString(); + } +} diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index 4f49fb7578a1..0b3899d15993 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -16,6 +16,8 @@ package com.android.server.appprediction; +import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; @@ -30,12 +32,17 @@ import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.provider.DeviceConfig; import android.service.appprediction.AppPredictionService; +import android.service.appprediction.IPredictionService; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.AbstractRemoteService; +import com.android.server.LocalServices; import com.android.server.infra.AbstractPerUserSystemService; +import com.android.server.people.PeopleServiceInternal; import java.util.function.Consumer; @@ -47,6 +54,8 @@ public class AppPredictionPerUserService extends implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks { private static final String TAG = AppPredictionPerUserService.class.getSimpleName(); + private static final String PREDICT_USING_PEOPLE_SERVICE_PREFIX = + "predict_using_people_service_"; @Nullable @GuardedBy("mLock") @@ -104,14 +113,16 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context, @NonNull AppPredictionSessionId sessionId) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.onCreatePredictionSession(context, sessionId); - - if (!mSessionInfos.containsKey(sessionId)) { - mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, - this::removeAppPredictionSessionInfo)); - } + if (!mSessionInfos.containsKey(sessionId)) { + mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, + DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, + PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false), + this::removeAppPredictionSessionInfo)); + } + final boolean serviceExists = resolveService(sessionId, s -> + s.onCreatePredictionSession(context, sessionId)); + if (!serviceExists) { + mSessionInfos.remove(sessionId); } } @@ -121,10 +132,7 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId, @NonNull AppTargetEvent event) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.notifyAppTargetEvent(sessionId, event); - } + resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event)); } /** @@ -133,10 +141,8 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId, @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds); - } + resolveService(sessionId, s -> + s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds)); } /** @@ -145,10 +151,7 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId, @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.sortAppTargets(sessionId, targets, callback); - } + resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback)); } /** @@ -157,14 +160,11 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.registerPredictionUpdates(sessionId, callback); - - AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo != null) { - sessionInfo.addCallbackLocked(callback); - } + final boolean serviceExists = resolveService(sessionId, s -> + s.registerPredictionUpdates(sessionId, callback)); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (serviceExists && sessionInfo != null) { + sessionInfo.addCallbackLocked(callback); } } @@ -174,14 +174,11 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.unregisterPredictionUpdates(sessionId, callback); - - AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo != null) { - sessionInfo.removeCallbackLocked(callback); - } + final boolean serviceExists = resolveService(sessionId, s -> + s.unregisterPredictionUpdates(sessionId, callback)); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (serviceExists && sessionInfo != null) { + sessionInfo.removeCallbackLocked(callback); } } @@ -190,10 +187,7 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.requestPredictionUpdate(sessionId); - } + resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId)); } /** @@ -201,14 +195,11 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.onDestroyPredictionSession(sessionId); - - AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo != null) { - sessionInfo.destroy(); - } + final boolean serviceExists = resolveService(sessionId, s -> + s.onDestroyPredictionSession(sessionId)); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (serviceExists && sessionInfo != null) { + sessionInfo.destroy(); } } @@ -312,6 +303,33 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") @Nullable + protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId, + @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) { + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return false; + if (sessionInfo.mUsesPeopleService) { + final IPredictionService service = + LocalServices.getService(PeopleServiceInternal.class); + if (service != null) { + try { + cb.run(service); + } catch (RemoteException e) { + // Shouldn't happen. + Slog.w(TAG, "Failed to invoke service:" + service, e); + } + } + return service != null; + } else { + final RemoteAppPredictionService service = getRemoteServiceLocked(); + if (service != null) { + service.scheduleOnResolvedService(cb); + } + return service != null; + } + } + + @GuardedBy("mLock") + @Nullable private RemoteAppPredictionService getRemoteServiceLocked() { if (mRemoteService == null) { final String serviceName = getComponentNameLocked(); @@ -334,8 +352,12 @@ public class AppPredictionPerUserService extends private static final class AppPredictionSessionInfo { private static final boolean DEBUG = false; // Do not submit with true + @NonNull private final AppPredictionSessionId mSessionId; + @NonNull private final AppPredictionContext mPredictionContext; + private final boolean mUsesPeopleService; + @NonNull private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction; private final RemoteCallbackList<IPredictionCallback> mCallbacks = @@ -352,13 +374,17 @@ public class AppPredictionPerUserService extends } }; - AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext, - Consumer<AppPredictionSessionId> removeSessionInfoAction) { + AppPredictionSessionInfo( + @NonNull final AppPredictionSessionId id, + @NonNull final AppPredictionContext predictionContext, + final boolean usesPeopleService, + @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) { if (DEBUG) { Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id); } mSessionId = id; mPredictionContext = predictionContext; + mUsesPeopleService = usesPeopleService; mRemoveSessionInfoAction = removeSessionInfoAction; } diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java index 04e0e7f7102f..ceb1cafcebeb 100644 --- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java +++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java @@ -16,13 +16,8 @@ package com.android.server.appprediction; import android.annotation.NonNull; -import android.app.prediction.AppPredictionContext; -import android.app.prediction.AppPredictionSessionId; -import android.app.prediction.AppTargetEvent; -import android.app.prediction.IPredictionCallback; import android.content.ComponentName; import android.content.Context; -import android.content.pm.ParceledListSlice; import android.os.IBinder; import android.service.appprediction.IPredictionService; import android.text.format.DateUtils; @@ -71,74 +66,17 @@ public class RemoteAppPredictionService extends } /** - * Notifies the service of a new prediction session. - */ - public void onCreatePredictionSession(@NonNull AppPredictionContext context, - @NonNull AppPredictionSessionId sessionId) { - scheduleAsyncRequest((s) -> s.onCreatePredictionSession(context, sessionId)); - } - - /** - * Records an app target event to the service. - */ - public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId, - @NonNull AppTargetEvent event) { - scheduleAsyncRequest((s) -> s.notifyAppTargetEvent(sessionId, event)); - } - - /** - * Records when a launch location is shown. - */ - public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId, - @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { - scheduleAsyncRequest((s) - -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds)); - } - - /** - * Requests the service to sort a list of apps or shortcuts. - */ - public void sortAppTargets(@NonNull AppPredictionSessionId sessionId, - @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) { - scheduleAsyncRequest((s) -> s.sortAppTargets(sessionId, targets, callback)); - } - - - /** - * Registers a callback for continuous updates of predicted apps or shortcuts. - */ - public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId, - @NonNull IPredictionCallback callback) { - scheduleAsyncRequest((s) -> s.registerPredictionUpdates(sessionId, callback)); - } - - /** - * Unregisters a callback for continuous updates of predicted apps or shortcuts. - */ - public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId, - @NonNull IPredictionCallback callback) { - scheduleAsyncRequest((s) -> s.unregisterPredictionUpdates(sessionId, callback)); - } - - /** - * Requests a new set of predicted apps or shortcuts. - */ - public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) { - scheduleAsyncRequest((s) -> s.requestPredictionUpdate(sessionId)); - } - - /** - * Notifies the service of the end of an existing prediction session. + * Schedules a request to bind to the remote service. */ - public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) { - scheduleAsyncRequest((s) -> s.onDestroyPredictionSession(sessionId)); + public void reconnect() { + super.scheduleBind(); } /** - * Schedules a request to bind to the remote service. + * Schedule async request on remote service. */ - public void reconnect() { - super.scheduleBind(); + public void scheduleOnResolvedService(@NonNull AsyncRequest<IPredictionService> request) { + scheduleAsyncRequest(request); } /** diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 31ea5faa05f1..9a33fc9548a0 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -998,6 +998,11 @@ public final class ContentCaptureManagerService extends sendErrorSignal(mClientAdapterReference, serviceAdapterReference, ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); + } finally { + synchronized (parentService.mLock) { + parentService.mPackagesWithShareRequests + .remove(mDataShareRequest.getPackageName()); + } } }); diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index 442c9e543b01..f688759e9f63 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -216,17 +216,11 @@ public abstract class UsageStatsManagerInternal { /** * Returns the events for the user in the given time period. * - * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the - * result. - * @param hideShortcutInvocationEvents whether the {@link UsageEvents.Event#SHORTCUT_INVOCATION} - * events need to be excluded from the result. - * @param hideLocusIdEvents whether the {@link UsageEvents.Event#LOCUS_ID_SET} - * events need to be excluded from the result. - * + * @param flags defines the visibility of certain usage events - see flags defined in + * {@link UsageEvents}. */ public abstract UsageEvents queryEventsForUser(@UserIdInt int userId, long beginTime, - long endTime, boolean obfuscateInstantApps, boolean hideShortcutInvocationEvents, - boolean hideLocusIdEvents); + long endTime, int flags); /** * Used to persist the last time a job was run for this app, in order to make decisions later diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index abe0dd543deb..ffa7d9202371 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -59,6 +59,8 @@ import android.app.ApplicationExitInfo.Reason; import android.app.ApplicationExitInfo.SubReason; import android.app.IApplicationThread; import android.app.IUidObserver; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -119,6 +121,7 @@ import com.android.server.pm.dex.DexManager; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.WindowManagerService; +import dalvik.annotation.compat.VersionCodes; import dalvik.system.VMRuntime; import java.io.File; @@ -327,6 +330,15 @@ public final class ProcessList { */ private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds; + /** + * Native heap allocations will now have a non-zero tag in the most significant byte. + * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged + * Pointers</a> + */ + @ChangeId + @EnabledAfter(targetSdkVersion = VersionCodes.Q) + private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. + ActivityManagerService mService = null; // To kill process groups asynchronously @@ -1768,6 +1780,13 @@ public final class ProcessList { runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE; } + // Enable heap pointer tagging, unless disabled by the app manifest, target sdk level, + // or the compat feature. + if (app.info.allowsNativeHeapPointerTagging() + && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) { + runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI; + } + String invokeWith = null; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { // Debuggable apps may include a wrapper script with their library directory. diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index c9c2c96a642a..204f072a72fc 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -153,7 +153,12 @@ public class AuthService extends SystemService { // Only allow internal clients to enable non-public options. if (bundle.getBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS) - || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) { + || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false) + || bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE) != null + || bundle.getCharSequence( + BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE) != null + || bundle.getCharSequence( + BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION) != null) { checkInternalPermission(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 7c2ec78c1cbc..d9e30250ba2d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -105,32 +105,57 @@ import java.util.stream.Collectors; */ public class HdmiControlService extends SystemService { private static final String TAG = "HdmiControlService"; - private final Locale HONG_KONG = new Locale("zh", "HK"); - private final Locale MACAU = new Locale("zh", "MO"); + private static final Locale HONG_KONG = new Locale("zh", "HK"); + private static final Locale MACAU = new Locale("zh", "MO"); - private static final Map<String, String> mTerminologyToBibliographicMap; - static { - mTerminologyToBibliographicMap = new HashMap<>(); + private static final Map<String, String> sTerminologyToBibliographicMap = + createsTerminologyToBibliographicMap(); + + private static Map<String, String> createsTerminologyToBibliographicMap() { + Map<String, String> temp = new HashMap<>(); // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE) - mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian - mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian - mTerminologyToBibliographicMap.put("eus", "baq"); // Basque - mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese - mTerminologyToBibliographicMap.put("ces", "cze"); // Czech - mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch - mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian - mTerminologyToBibliographicMap.put("deu", "ger"); // German - mTerminologyToBibliographicMap.put("ell", "gre"); // Greek - mTerminologyToBibliographicMap.put("fra", "fre"); // French - mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic - mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian - mTerminologyToBibliographicMap.put("mri", "mao"); // Maori - mTerminologyToBibliographicMap.put("msa", "may"); // Malay - mTerminologyToBibliographicMap.put("fas", "per"); // Persian - mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian - mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak - mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan - mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh + temp.put("sqi", "alb"); // Albanian + temp.put("hye", "arm"); // Armenian + temp.put("eus", "baq"); // Basque + temp.put("mya", "bur"); // Burmese + temp.put("ces", "cze"); // Czech + temp.put("nld", "dut"); // Dutch + temp.put("kat", "geo"); // Georgian + temp.put("deu", "ger"); // German + temp.put("ell", "gre"); // Greek + temp.put("fra", "fre"); // French + temp.put("isl", "ice"); // Icelandic + temp.put("mkd", "mac"); // Macedonian + temp.put("mri", "mao"); // Maori + temp.put("msa", "may"); // Malay + temp.put("fas", "per"); // Persian + temp.put("ron", "rum"); // Romanian + temp.put("slk", "slo"); // Slovak + temp.put("bod", "tib"); // Tibetan + temp.put("cym", "wel"); // Welsh + return Collections.unmodifiableMap(temp); + } + + @VisibleForTesting static String localeToMenuLanguage(Locale locale) { + if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) { + // Android always returns "zho" for all Chinese variants. + // Use "bibliographic" code defined in CEC639-2 for traditional + // Chinese used in Taiwan/Hong Kong/Macau. + return "chi"; + } else { + String language = locale.getISO3Language(); + + // locale.getISO3Language() returns terminology code and need to + // send it as bibliographic code instead since the Bibliographic + // codes of ISO/FDIS 639-2 shall be used. + // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi" + // But, as it depends on the locale, is not handled here. + if (sTerminologyToBibliographicMap.containsKey(language)) { + language = sTerminologyToBibliographicMap.get(language); + } + + return language; + } } static final String PERMISSION = "android.permission.HDMI_CEC"; @@ -208,8 +233,8 @@ public class HdmiControlService extends SystemService { } break; case Intent.ACTION_CONFIGURATION_CHANGED: - String language = getMenuLanguage(); - if (!mLanguage.equals(language)) { + String language = HdmiControlService.localeToMenuLanguage(Locale.getDefault()); + if (!mMenuLanguage.equals(language)) { onLanguageChanged(language); } break; @@ -221,28 +246,6 @@ public class HdmiControlService extends SystemService { } } - private String getMenuLanguage() { - Locale locale = Locale.getDefault(); - if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) { - // Android always returns "zho" for all Chinese variants. - // Use "bibliographic" code defined in CEC639-2 for traditional - // Chinese used in Taiwan/Hong Kong/Macau. - return "chi"; - } else { - String language = locale.getISO3Language(); - - // locale.getISO3Language() returns terminology code and need to - // send it as bibliographic code instead since the Bibliographic - // codes of ISO/FDIS 639-2 shall be used. - // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi" - // But, as it depends on the locale, is not handled here. - if (mTerminologyToBibliographicMap.containsKey(language)) { - language = mTerminologyToBibliographicMap.get(language); - } - - return language; - } - } } // A thread to handle synchronous IO of CEC and MHL control service. @@ -339,7 +342,7 @@ public class HdmiControlService extends SystemService { private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; @ServiceThreadOnly - private String mLanguage = Locale.getDefault().getISO3Language(); + private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault()); @ServiceThreadOnly private boolean mStandbyMessageReceived = false; @@ -759,7 +762,7 @@ public class HdmiControlService extends SystemService { private void initializeCec(int initiatedBy) { mAddressAllocated = false; mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); - mCecController.setLanguage(mLanguage); + mCecController.setLanguage(mMenuLanguage); initializeLocalDevices(initiatedBy); } @@ -2818,7 +2821,7 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly private void onLanguageChanged(String language) { assertRunOnServiceThread(); - mLanguage = language; + mMenuLanguage = language; if (isTvDeviceEnabled()) { tv().broadcastMenuLanguage(language); @@ -2826,10 +2829,19 @@ public class HdmiControlService extends SystemService { } } + /** + * Gets the CEC menu language. + * + * <p>This is the ISO/FDIS 639-2 3 letter language code sent in the CEC message @{code <Set Menu + * Language>}. + * See HDMI 1.4b section CEC 13.6.2 + * + * @see {@link Locale#getISO3Language()} + */ @ServiceThreadOnly String getLanguage() { assertRunOnServiceThread(); - return mLanguage; + return mMenuLanguage; } private void disableDevices(PendingActionClearedCallback callback) { @@ -2838,7 +2850,6 @@ public class HdmiControlService extends SystemService { device.disableDevice(mStandbyMessageReceived, callback); } } - mMhlController.clearAllLocalDevices(); } diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS new file mode 100644 index 000000000000..25ef9facb216 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/OWNERS @@ -0,0 +1,6 @@ +set noparent + +ogunwale@google.com +yukawa@google.com +tarandeep@google.com +lumark@google.com diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a570502dee10..d0d0f5a3a233 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1055,7 +1055,7 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key); reportSeen(r); } - r.setVisibility(true, nv.rank, nv.count); + r.setVisibility(true, nv.rank, nv.count, mNotificationRecordLogger); mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true); boolean isHun = (nv.location == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP); @@ -1074,7 +1074,7 @@ public class NotificationManagerService extends SystemService { for (NotificationVisibility nv : noLongerVisibleKeys) { NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; - r.setVisibility(false, nv.rank, nv.count); + r.setVisibility(false, nv.rank, nv.count, mNotificationRecordLogger); mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false); nv.recycle(); } @@ -3979,6 +3979,29 @@ public class NotificationManagerService extends SystemService { } /** + * Allows the notification assistant to un-snooze a single notification. + * + * @param token The binder for the listener, to check that the caller is allowed + */ + @Override + public void unsnoozeNotificationFromSystemListener(INotificationListener token, + String key) { + long identity = Binder.clearCallingIdentity(); + try { + synchronized (mNotificationLock) { + final ManagedServiceInfo info = + mListeners.checkServiceTokenLocked(token); + if (!info.isSystem) { + throw new SecurityException("Not allowed to unsnooze before deadline"); + } + unsnoozeNotificationInt(key, info); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** * Allow an INotificationListener to simulate clearing (dismissing) a single notification. * * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} @@ -6468,7 +6491,7 @@ public class NotificationManagerService extends SystemService { mUsageStats.registerPostedByApp(r); r.setInterruptive(isVisuallyInterruptive(null, r)); } else { - old = mNotificationList.get(index); + old = mNotificationList.get(index); // Potentially *changes* old mNotificationList.set(index, r); mUsageStats.registerUpdatedByApp(r, old); // Make sure we don't lose the foreground service state. @@ -6537,7 +6560,7 @@ public class NotificationManagerService extends SystemService { maybeRecordInterruptionLocked(r); // Log event to statsd - mNotificationRecordLogger.logNotificationReported(r, old, position, + mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position, buzzBeepBlinkLoggingCode); } finally { int N = mEnqueuedNotifications.size(); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 4785da9a5922..0ada58e1ce16 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -909,7 +909,8 @@ public final class NotificationRecord { /** * Set the visibility of the notification. */ - public void setVisibility(boolean visible, int rank, int count) { + public void setVisibility(boolean visible, int rank, int count, + NotificationRecordLogger notificationRecordLogger) { final long now = System.currentTimeMillis(); mVisibleSinceMs = visible ? now : mVisibleSinceMs; stats.onVisibilityChanged(visible); @@ -927,6 +928,7 @@ public final class NotificationRecord { getFreshnessMs(now), 0, // exposure time rank); + notificationRecordLogger.logNotificationVisibility(this, visible); } /** diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index 2f7854226c5c..eaca066f026f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -41,13 +41,14 @@ import java.util.Objects; public interface NotificationRecordLogger { /** - * Logs a NotificationReported atom reflecting the posting or update of a notification. + * May log a NotificationReported atom reflecting the posting or update of a notification. * @param r The new NotificationRecord. If null, no action is taken. * @param old The previous NotificationRecord. Null if there was no previous record. * @param position The position at which this notification is ranked. * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user. */ - void logNotificationReported(@Nullable NotificationRecord r, @Nullable NotificationRecord old, + void maybeLogNotificationPosted(@Nullable NotificationRecord r, + @Nullable NotificationRecord old, int position, int buzzBeepBlink); /** @@ -62,6 +63,14 @@ public interface NotificationRecordLogger { @NotificationStats.DismissalSurface int dismissalSurface); /** + * Logs a notification visibility change event using UiEventReported (event ids from the + * NotificationEvents enum). + * @param r The NotificationRecord. If null, no action is taken. + * @param visible True if the notification became visible. + */ + void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible); + + /** * The UiEvent enums that this class can log. */ enum NotificationReportedEvent implements UiEventLogger.UiEventEnum { @@ -145,6 +154,7 @@ public interface NotificationRecordLogger { @Override public int getId() { return mId; } + public static NotificationCancelledEvent fromCancelReason( @NotificationListenerService.NotificationCancelReason int reason, @NotificationStats.DismissalSurface int surface) { @@ -191,6 +201,24 @@ public interface NotificationRecordLogger { } } + enum NotificationEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Notification became visible.") + NOTIFICATION_OPEN(197), + @UiEvent(doc = "Notification stopped being visible.") + NOTIFICATION_CLOSE(198); + + private final int mId; + NotificationEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + + public static NotificationEvent fromVisibility(boolean visible) { + return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE; + } + } /** * A helper for extracting logging information from one or two NotificationRecords. */ @@ -209,7 +237,7 @@ public interface NotificationRecordLogger { /** * @return True if old is null, alerted, or important logged fields have changed. */ - boolean shouldLog(int buzzBeepBlink) { + boolean shouldLogReported(int buzzBeepBlink) { if (r == null) { return false; } diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java index bb23d1e876dc..9fcac257d328 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java @@ -26,13 +26,13 @@ import com.android.internal.util.FrameworkStatsLog; */ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { - UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @Override - public void logNotificationReported(NotificationRecord r, NotificationRecord old, + public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old, int position, int buzzBeepBlink) { NotificationRecordPair p = new NotificationRecordPair(r, old); - if (!p.shouldLog(buzzBeepBlink)) { + if (!p.shouldLogReported(buzzBeepBlink)) { return; } FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED, @@ -66,8 +66,19 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { @Override public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) { - mUiEventLogger.logWithInstanceId( - NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), - r.getUid(), r.getSbn().getPackageName(), r.getSbn().getInstanceId()); + log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r); + } + + @Override + public void logNotificationVisibility(NotificationRecord r, boolean visible) { + log(NotificationEvent.fromVisibility(visible), r); + } + + void log(UiEventLogger.UiEventEnum event, NotificationRecord r) { + if (r == null) { + return; + } + mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(), + r.getSbn().getInstanceId()); } } diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 661297a7346e..bae1dd3c6cb6 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -16,6 +16,7 @@ package com.android.server.notification; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -42,7 +43,9 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; +import java.sql.Array; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -77,26 +80,26 @@ public class SnoozeHelper { private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE"; private static final int REQUEST_CODE_REPOST = 1; private static final String REPOST_SCHEME = "repost"; - private static final String EXTRA_KEY = "key"; + static final String EXTRA_KEY = "key"; private static final String EXTRA_USER_ID = "userId"; private final Context mContext; private AlarmManager mAm; private final ManagedServices.UserProfiles mUserProfiles; - // User id : package name : notification key : record. - private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>> + // User id | package name : notification key : record. + private ArrayMap<String, ArrayMap<String, NotificationRecord>> mSnoozedNotifications = new ArrayMap<>(); - // User id : package name : notification key : time-milliseconds . + // User id | package name : notification key : time-milliseconds . // This member stores persisted snoozed notification trigger times. it persists through reboots // It should have the notifications that haven't expired or re-posted yet - private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>> + private final ArrayMap<String, ArrayMap<String, Long>> mPersistedSnoozedNotifications = new ArrayMap<>(); - // User id : package name : notification key : creation ID . + // User id | package name : notification key : creation ID . // This member stores persisted snoozed notification trigger context for the assistant // it persists through reboots. // It should have the notifications that haven't expired or re-posted yet - private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>> + private final ArrayMap<String, ArrayMap<String, String>> mPersistedSnoozedNotificationsWithContext = new ArrayMap<>(); // notification key : package. private ArrayMap<String, String> mPackages = new ArrayMap<>(); @@ -115,6 +118,10 @@ public class SnoozeHelper { mUserProfiles = userProfiles; } + private String getPkgKey(@UserIdInt int userId, String pkg) { + return userId + "|" + pkg; + } + void cleanupPersistedContext(String key){ int userId = mUsers.get(key); String pkg = mPackages.get(key); @@ -144,15 +151,13 @@ public class SnoozeHelper { } protected boolean isSnoozed(int userId, String pkg, String key) { - return mSnoozedNotifications.containsKey(userId) - && mSnoozedNotifications.get(userId).containsKey(pkg) - && mSnoozedNotifications.get(userId).get(pkg).containsKey(key); + return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg)) + && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key); } protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { - if (mSnoozedNotifications.containsKey(userId) - && mSnoozedNotifications.get(userId).containsKey(pkg)) { - return mSnoozedNotifications.get(userId).get(pkg).values(); + if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) { + return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values(); } return Collections.EMPTY_LIST; } @@ -161,14 +166,14 @@ public class SnoozeHelper { ArrayList<NotificationRecord> getNotifications(String pkg, String groupKey, Integer userId) { ArrayList<NotificationRecord> records = new ArrayList<>(); - if (mSnoozedNotifications.containsKey(userId) - && mSnoozedNotifications.get(userId).containsKey(pkg)) { - ArrayMap<String, NotificationRecord> packages = - mSnoozedNotifications.get(userId).get(pkg); - for (int i = 0; i < packages.size(); i++) { - String currentGroupKey = packages.valueAt(i).getSbn().getGroup(); + ArrayMap<String, NotificationRecord> allRecords = + mSnoozedNotifications.get(getPkgKey(userId, pkg)); + if (allRecords != null) { + for (int i = 0; i < allRecords.size(); i++) { + NotificationRecord r = allRecords.valueAt(i); + String currentGroupKey = r.getSbn().getGroup(); if (currentGroupKey.equals(groupKey)) { - records.add(packages.valueAt(i)); + records.add(r); } } } @@ -176,47 +181,30 @@ public class SnoozeHelper { } protected NotificationRecord getNotification(String key) { - List<NotificationRecord> snoozedForUser = new ArrayList<>(); - IntArray userIds = mUserProfiles.getCurrentProfileIds(); - if (userIds != null) { - final int userIdsSize = userIds.size(); - for (int i = 0; i < userIdsSize; i++) { - final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = - mSnoozedNotifications.get(userIds.get(i)); - if (snoozedPkgs != null) { - final int snoozedPkgsSize = snoozedPkgs.size(); - for (int j = 0; j < snoozedPkgsSize; j++) { - final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); - if (records != null) { - return records.get(key); - } - } - } - } + if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) { + Slog.w(TAG, "Snoozed data sets no longer agree for " + key); + return null; + } + int userId = mUsers.get(key); + String pkg = mPackages.get(key); + ArrayMap<String, NotificationRecord> snoozed = + mSnoozedNotifications.get(getPkgKey(userId, pkg)); + if (snoozed == null) { + return null; } - return null; + return snoozed.get(key); } protected @NonNull List<NotificationRecord> getSnoozed() { - List<NotificationRecord> snoozedForUser = new ArrayList<>(); - IntArray userIds = mUserProfiles.getCurrentProfileIds(); - if (userIds != null) { - final int N = userIds.size(); - for (int i = 0; i < N; i++) { - final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = - mSnoozedNotifications.get(userIds.get(i)); - if (snoozedPkgs != null) { - final int M = snoozedPkgs.size(); - for (int j = 0; j < M; j++) { - final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); - if (records != null) { - snoozedForUser.addAll(records.values()); - } - } - } - } - } - return snoozedForUser; + // caller filters records based on the current user profiles and listener access, so just + // return everything + List<NotificationRecord> snoozed= new ArrayList<>(); + for (String userPkgKey : mSnoozedNotifications.keySet()) { + ArrayMap<String, NotificationRecord> snoozedRecords = + mSnoozedNotifications.get(userPkgKey); + snoozed.addAll(snoozedRecords.values()); + } + return snoozed; } /** @@ -261,120 +249,88 @@ public class SnoozeHelper { } private <T> void storeRecord(String pkg, String key, Integer userId, - ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) { + ArrayMap<String, ArrayMap<String, T>> targets, T object) { - ArrayMap<String, ArrayMap<String, T>> records = - targets.get(userId); - if (records == null) { - records = new ArrayMap<>(); - } - ArrayMap<String, T> pkgRecords = records.get(pkg); - if (pkgRecords == null) { - pkgRecords = new ArrayMap<>(); + ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); + if (keyToValue == null) { + keyToValue = new ArrayMap<>(); } - pkgRecords.put(key, object); - records.put(pkg, pkgRecords); - targets.put(userId, records); + keyToValue.put(key, object); + targets.put(getPkgKey(userId, pkg), keyToValue); } private <T> T removeRecord(String pkg, String key, Integer userId, - ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) { + ArrayMap<String, ArrayMap<String, T>> targets) { T object = null; - - ArrayMap<String, ArrayMap<String, T>> records = - targets.get(userId); - if (records == null) { + ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); + if (keyToValue == null) { return null; } - ArrayMap<String, T> pkgRecords = records.get(pkg); - if (pkgRecords == null) { - return null; - } - object = pkgRecords.remove(key); - if (pkgRecords.size() == 0) { - records.remove(pkg); - } - if (records.size() == 0) { - targets.remove(userId); + object = keyToValue.remove(key); + if (keyToValue.size() == 0) { + targets.remove(getPkgKey(userId, pkg)); } return object; } protected boolean cancel(int userId, String pkg, String tag, int id) { - if (mSnoozedNotifications.containsKey(userId)) { - ArrayMap<String, NotificationRecord> recordsForPkg = - mSnoozedNotifications.get(userId).get(pkg); - if (recordsForPkg != null) { - final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet(); - for (Map.Entry<String, NotificationRecord> record : records) { - final StatusBarNotification sbn = record.getValue().getSbn(); - if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) { - record.getValue().isCanceled = true; - return true; - } + ArrayMap<String, NotificationRecord> recordsForPkg = + mSnoozedNotifications.get(getPkgKey(userId, pkg)); + if (recordsForPkg != null) { + final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet(); + for (Map.Entry<String, NotificationRecord> record : records) { + final StatusBarNotification sbn = record.getValue().getSbn(); + if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) { + record.getValue().isCanceled = true; + return true; } } } return false; } - protected boolean cancel(int userId, boolean includeCurrentProfiles) { - int[] userIds = {userId}; + protected void cancel(int userId, boolean includeCurrentProfiles) { + if (mSnoozedNotifications.size() == 0) { + return; + } + IntArray userIds = new IntArray(); + userIds.add(userId); if (includeCurrentProfiles) { - userIds = mUserProfiles.getCurrentProfileIds().toArray(); + userIds = mUserProfiles.getCurrentProfileIds(); } - final int N = userIds.length; - for (int i = 0; i < N; i++) { - final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = - mSnoozedNotifications.get(userIds[i]); - if (snoozedPkgs != null) { - final int M = snoozedPkgs.size(); - for (int j = 0; j < M; j++) { - final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j); - if (records != null) { - int P = records.size(); - for (int k = 0; k < P; k++) { - records.valueAt(k).isCanceled = true; - } - } + for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) { + for (NotificationRecord r : snoozedRecords.values()) { + if (userIds.binarySearch(r.getUserId()) >= 0) { + r.isCanceled = true; } - return true; } } - return false; } protected boolean cancel(int userId, String pkg) { - if (mSnoozedNotifications.containsKey(userId)) { - if (mSnoozedNotifications.get(userId).containsKey(pkg)) { - ArrayMap<String, NotificationRecord> records = - mSnoozedNotifications.get(userId).get(pkg); - int N = records.size(); - for (int i = 0; i < N; i++) { - records.valueAt(i).isCanceled = true; - } - return true; - } + ArrayMap<String, NotificationRecord> records = + mSnoozedNotifications.get(getPkgKey(userId, pkg)); + if (records == null) { + return false; } - return false; + int N = records.size(); + for (int i = 0; i < N; i++) { + records.valueAt(i).isCanceled = true; + } + return true; } /** * Updates the notification record so the most up to date information is shown on re-post. */ protected void update(int userId, NotificationRecord record) { - ArrayMap<String, ArrayMap<String, NotificationRecord>> records = - mSnoozedNotifications.get(userId); + ArrayMap<String, NotificationRecord> records = + mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName())); if (records == null) { return; } - ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName()); - if (pkgRecords == null) { - return; - } - NotificationRecord existing = pkgRecords.get(record.getKey()); - pkgRecords.put(record.getKey(), record); + records.put(record.getKey(), record); } protected void repost(String key) { @@ -386,20 +342,18 @@ public class SnoozeHelper { protected void repost(String key, int userId) { final String pkg = mPackages.remove(key); - ArrayMap<String, ArrayMap<String, NotificationRecord>> records = - mSnoozedNotifications.get(userId); + ArrayMap<String, NotificationRecord> records = + mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return; } - ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); - if (pkgRecords == null) { - return; - } - final NotificationRecord record = pkgRecords.remove(key); + final NotificationRecord record = records.remove(key); mPackages.remove(key); mUsers.remove(key); if (record != null && !record.isCanceled) { + final PendingIntent pi = createPendingIntent(pkg, record.getKey(), userId); + mAm.cancel(pi); MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); @@ -408,55 +362,46 @@ public class SnoozeHelper { } protected void repostGroupSummary(String pkg, int userId, String groupKey) { - if (mSnoozedNotifications.containsKey(userId)) { - ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage - = mSnoozedNotifications.get(userId); - - if (keysByPackage != null && keysByPackage.containsKey(pkg)) { - ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg); - - if (recordsByKey != null) { - String groupSummaryKey = null; - int N = recordsByKey.size(); - for (int i = 0; i < N; i++) { - final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i); - if (potentialGroupSummary.getSbn().isGroup() - && potentialGroupSummary.getNotification().isGroupSummary() - && groupKey.equals(potentialGroupSummary.getGroupKey())) { - groupSummaryKey = potentialGroupSummary.getKey(); - break; - } - } + ArrayMap<String, NotificationRecord> recordsByKey + = mSnoozedNotifications.get(getPkgKey(userId, pkg)); + if (recordsByKey == null) { + return; + } - if (groupSummaryKey != null) { - NotificationRecord record = recordsByKey.remove(groupSummaryKey); - mPackages.remove(groupSummaryKey); - mUsers.remove(groupSummaryKey); + String groupSummaryKey = null; + int N = recordsByKey.size(); + for (int i = 0; i < N; i++) { + final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i); + if (potentialGroupSummary.getSbn().isGroup() + && potentialGroupSummary.getNotification().isGroupSummary() + && groupKey.equals(potentialGroupSummary.getGroupKey())) { + groupSummaryKey = potentialGroupSummary.getKey(); + break; + } + } - if (record != null && !record.isCanceled) { - MetricsLogger.action(record.getLogMaker() - .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) - .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); - mCallback.repost(userId, record); - } - } - } + if (groupSummaryKey != null) { + NotificationRecord record = recordsByKey.remove(groupSummaryKey); + mPackages.remove(groupSummaryKey); + mUsers.remove(groupSummaryKey); + + if (record != null && !record.isCanceled) { + MetricsLogger.action(record.getLogMaker() + .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) + .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); + mCallback.repost(userId, record); } } } protected void clearData(int userId, String pkg) { - ArrayMap<String, ArrayMap<String, NotificationRecord>> records = - mSnoozedNotifications.get(userId); + ArrayMap<String, NotificationRecord> records = + mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { return; } - ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); - if (pkgRecords == null) { - return; - } - for (int i = pkgRecords.size() - 1; i >= 0; i--) { - final NotificationRecord r = pkgRecords.removeAt(i); + for (int i = records.size() - 1; i >= 0; i--) { + final NotificationRecord r = records.removeAt(i); if (r != null) { mPackages.remove(r.getKey()); mUsers.remove(r.getKey()); @@ -495,22 +440,36 @@ public class SnoozeHelper { public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) { pw.println("\n Snoozed notifications:"); - for (int userId : mSnoozedNotifications.keySet()) { + for (String userPkgKey : mSnoozedNotifications.keySet()) { pw.print(INDENT); - pw.println("user: " + userId); - ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs = - mSnoozedNotifications.get(userId); - for (String pkg : snoozedPkgs.keySet()) { + pw.println("key: " + userPkgKey); + ArrayMap<String, NotificationRecord> snoozedRecords = + mSnoozedNotifications.get(userPkgKey); + Set<String> snoozedKeys = snoozedRecords.keySet(); + for (String key : snoozedKeys) { pw.print(INDENT); pw.print(INDENT); - pw.println("package: " + pkg); - Set<String> snoozedKeys = snoozedPkgs.get(pkg).keySet(); - for (String key : snoozedKeys) { - pw.print(INDENT); - pw.print(INDENT); - pw.print(INDENT); - pw.println(key); - } + pw.print(INDENT); + pw.println(key); + } + } + pw.println("\n Pending snoozed notifications"); + for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) { + pw.print(INDENT); + pw.println("key: " + userPkgKey); + ArrayMap<String, Long> snoozedRecords = + mPersistedSnoozedNotifications.get(userPkgKey); + if (snoozedRecords == null) { + continue; + } + Set<String> snoozedKeys = snoozedRecords.keySet(); + for (String key : snoozedKeys) { + pw.print(INDENT); + pw.print(INDENT); + pw.print(INDENT); + pw.print(key); + pw.print(INDENT); + pw.println(snoozedRecords.get(key)); } } } @@ -538,41 +497,34 @@ public class SnoozeHelper { void insert(T t) throws IOException; } private <T> void writeXml(XmlSerializer out, - ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag, + ArrayMap<String, ArrayMap<String, T>> targets, String tag, Inserter<T> attributeInserter) throws IOException { synchronized (targets) { final int M = targets.size(); for (int i = 0; i < M; i++) { - final ArrayMap<String, ArrayMap<String, T>> packages = - targets.valueAt(i); - if (packages == null) { - continue; - } - final int N = packages.size(); - for (int j = 0; j < N; j++) { - final ArrayMap<String, T> keyToValue = packages.valueAt(j); - if (keyToValue == null) { - continue; - } - final int O = keyToValue.size(); - for (int k = 0; k < O; k++) { - T value = keyToValue.valueAt(k); + String userIdPkgKey = targets.keyAt(i); + // T is a String (snoozed until context) or Long (snoozed until time) + ArrayMap<String, T> keyToValue = targets.valueAt(i); + for (int j = 0; j < keyToValue.size(); j++) { + String key = keyToValue.keyAt(j); + T value = keyToValue.valueAt(j); - out.startTag(null, tag); + out.startTag(null, tag); - attributeInserter.insert(value); + attributeInserter.insert(value); - out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, - XML_SNOOZED_NOTIFICATION_VERSION); - out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k)); - out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j)); - out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, - targets.keyAt(i).toString()); + out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, + XML_SNOOZED_NOTIFICATION_VERSION); + out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - out.endTag(null, tag); + String pkg = mPackages.get(key); + int userId = mUsers.get(key); + out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); + out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, + String.valueOf(userId)); - } + out.endTag(null, tag); } } } @@ -606,7 +558,6 @@ public class SnoozeHelper { } scheduleRepost(pkg, key, userId, time - System.currentTimeMillis()); } - continue; } if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) { final String creationId = parser.getAttributeValue( @@ -615,18 +566,9 @@ public class SnoozeHelper { storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext, creationId); } - continue; } - - } catch (Exception e) { - //we dont cre if it is a number format exception or a null pointer exception. - //we just want to debug it and continue with our lives - if (DEBUG) { - Slog.d(TAG, - "Exception in reading snooze data from policy xml: " - + e.getMessage()); - } + Slog.e(TAG, "Exception in reading snooze data from policy xml", e); } } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index d629b547992b..0fb889c8da22 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -69,7 +69,7 @@ public class AppsFilter { // Logs all filtering instead of enforcing private static final boolean DEBUG_ALLOW_ALL = false; private static final boolean DEBUG_LOGGING = false; - private static final boolean FEATURE_ENABLED_BY_DEFAULT = false; + private static final boolean FEATURE_ENABLED_BY_DEFAULT = true; /** * This contains a list of app UIDs that are implicitly queryable because another app explicitly diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index c267cea163d5..f1e403b1bc63 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -119,6 +119,7 @@ import java.io.PrintWriter; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Base64; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -135,7 +136,6 @@ import java.util.concurrent.TimeUnit; class PackageManagerShellCommand extends ShellCommand { /** Path for streaming APK content */ private static final String STDIN_PATH = "-"; - private static final byte[] STDIN_PATH_BYTES = "-".getBytes(StandardCharsets.UTF_8); /** Path where ART profiles snapshots are dumped for the shell user */ private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; private static final int DEFAULT_WAIT_MS = 60 * 1000; @@ -2988,8 +2988,10 @@ class PackageManagerShellCommand extends ShellCommand { try { // 1. Single file from stdin. if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) { - String name = "base." + (isApex ? "apex" : "apk"); - session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes, STDIN_PATH_BYTES, null); + final String name = "base." + (isApex ? "apex" : "apk"); + final String metadata = "-" + name; + session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes, + metadata.getBytes(StandardCharsets.UTF_8), null); return 0; } @@ -2998,24 +3000,58 @@ class PackageManagerShellCommand extends ShellCommand { // 2. File with specified size read from stdin. if (delimLocation != -1) { - String name = arg.substring(0, delimLocation); - String sizeStr = arg.substring(delimLocation + 1); - long sizeBytes; + final String[] fileDesc = arg.split(":"); + String name = null; + long sizeBytes = -1; + String metadata; + byte[] signature = null; + + try { + if (fileDesc.length > 0) { + name = fileDesc[0]; + } + if (fileDesc.length > 1) { + sizeBytes = Long.parseUnsignedLong(fileDesc[1]); + } + if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) { + metadata = fileDesc[2]; + } else { + metadata = name; + } + if (fileDesc.length > 3) { + signature = Base64.getDecoder().decode(fileDesc[3]); + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Unable to parse file parameters: " + arg + ", reason: " + e); + return 1; + } if (TextUtils.isEmpty(name)) { getErrPrintWriter().println("Empty file name in: " + arg); return 1; } + + if (signature != null) { + // Streaming/adb mode. + metadata = "+" + metadata; + } else { + // Singleshot read from stdin. + metadata = "-" + metadata; + } + try { - sizeBytes = Long.parseUnsignedLong(sizeStr); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Unable to parse size from: " + arg); + if (V4Signature.readFrom(signature) == null) { + getErrPrintWriter().println("V4 signature is invalid in: " + arg); + return 1; + } + } catch (Exception e) { + getErrPrintWriter().println("V4 signature is invalid: " + e + " in " + arg); return 1; } - // Incremental requires unique metadatas, let's add a name to the dash. session.addFile(LOCATION_DATA_APP, name, sizeBytes, - ("-" + name).getBytes(StandardCharsets.UTF_8), null); + metadata.getBytes(StandardCharsets.UTF_8), signature); continue; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 0fb4cb036282..832c9b788b0b 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4393,6 +4393,7 @@ public final class Settings { ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT, "SYSTEM_EXT", ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD", ApplicationInfo.PRIVATE_FLAG_ODM, "ODM", + ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING, "PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING", }; void dumpVersionLPr(IndentingPrintWriter pw) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 67a22d3e477c..39093aec07c0 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -866,6 +866,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { } } + default int getMaxWindowLayer() { + return 35; + } + /** * Return how to Z-order sub-windows in relation to the window they are attached to. * Return positive to have them ordered in front, negative for behind. diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 9e150fd6a8b3..f9981d0d9c96 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -802,7 +802,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { newRollback = getRollbackForSessionLocked(packageSession.getSessionId()); if (newRollback == null) { newRollback = createNewRollbackLocked(parentSession); - mRollbacks.add(newRollback); } } newRollback.addToken(token); @@ -1002,11 +1001,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } - Rollback rollback = completeEnableRollback(newRollback); - if (rollback == null) { + if (!completeEnableRollback(newRollback)) { result.offer(-1); } else { - result.offer(rollback.info.getRollbackId()); + result.offer(newRollback.info.getRollbackId()); } }); @@ -1158,19 +1156,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Rollback rollback; synchronized (mLock) { rollback = getRollbackForSessionLocked(sessionId); - if (rollback == null || rollback.isStaged() || !rollback.isEnabling() - || !rollback.notifySessionWithSuccess()) { - return; - } - // All child sessions finished with success. We can enable this rollback now. - // TODO: refactor #completeEnableRollback so we won't remove 'rollback' from - // mRollbacks here and add it back in #completeEnableRollback later. - mRollbacks.remove(rollback); } - // TODO: Now #completeEnableRollback returns the same rollback object as the - // parameter on success. It would be more readable to return a boolean to indicate - // success or failure. - if (completeEnableRollback(rollback) != null) { + if (rollback != null && !rollback.isStaged() && rollback.isEnabling() + && rollback.notifySessionWithSuccess() + && completeEnableRollback(rollback)) { makeRollbackAvailable(rollback); } } else { @@ -1188,13 +1177,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } /** - * Add a rollback to the list of rollbacks. It does not make the rollback available yet. + * Persist a rollback as enable-completed. It does not make the rollback available yet. + * This rollback will be deleted and removed from {@link #mRollbacks} should any error happens. * - * @return the Rollback instance for a successfully enable-completed rollback, - * or null on error. + * @return {code true} if {code rollback} is successfully enable-completed, + * or {code false} otherwise. */ @WorkerThread - private Rollback completeEnableRollback(Rollback rollback) { + private boolean completeEnableRollback(Rollback rollback) { if (LOCAL_LOGV) { Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId()); } @@ -1205,26 +1195,24 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // rollback for the embedded apk-in-apex, if any. if (!rollback.allPackagesEnabled()) { Slog.e(TAG, "Failed to enable rollback for all packages in session."); + mRollbacks.remove(rollback); rollback.delete(mAppDataRollbackHelper); - return null; + return false; } + // Note: There is a small window of time between when + // the session has been committed by the package + // manager and when we make the rollback available + // here. Presumably the window is small enough that + // nobody will want to roll back the newly installed + // package before we make the rollback available. + // TODO: We'll lose the rollback if the + // device reboots between when the session is + // committed and this point. Revisit this after + // adding support for rollback of staged installs. rollback.saveRollback(); - synchronized (mLock) { - // Note: There is a small window of time between when - // the session has been committed by the package - // manager and when we make the rollback available - // here. Presumably the window is small enough that - // nobody will want to roll back the newly installed - // package before we make the rollback available. - // TODO: We'll lose the rollback if the - // device reboots between when the session is - // committed and this point. Revisit this after - // adding support for rollback of staged installs. - mRollbacks.add(rollback); - } - return rollback; + return true; } @WorkerThread @@ -1304,6 +1292,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } + /** + * Creates and returns a Rollback according to the given SessionInfo + * and adds it to {@link #mRollbacks}. + */ @WorkerThread @GuardedBy("mLock") private Rollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) { @@ -1338,6 +1330,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { installerPackageName, packageSessionIds); } + mRollbacks.add(rollback); return rollback; } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index b3edc91a4129..0d365b16e228 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -102,6 +102,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override + public String toString() { + return mName + "@" + System.identityHashCode(this); + } + + @Override public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) { final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 9e93e1455f2c..0ec0c7b53875 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; - import android.content.res.Resources; import android.text.TextUtils; @@ -43,7 +41,7 @@ public abstract class DisplayAreaPolicy { /** * The Tasks container. Tasks etc. are automatically added to this container. */ - protected final TaskContainers mTaskContainers; + protected final DisplayArea<? extends ActivityStack> mTaskContainers; /** * Construct a new {@link DisplayAreaPolicy} @@ -58,7 +56,8 @@ public abstract class DisplayAreaPolicy { */ protected DisplayAreaPolicy(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, - DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) { + DisplayArea<? extends WindowContainer> imeContainer, + DisplayArea<? extends ActivityStack> taskContainers) { mWmService = wmService; mContent = content; mRoot = root; @@ -83,67 +82,15 @@ public abstract class DisplayAreaPolicy { */ public abstract void addWindow(WindowToken token); - /** - * Default policy that has no special features. - */ - public static class Default extends DisplayAreaPolicy { - - public Default(WindowManagerService wmService, DisplayContent content, - DisplayArea.Root root, + /** Provider for platform-default display area policy. */ + static final class DefaultProvider implements DisplayAreaPolicy.Provider { + @Override + public DisplayAreaPolicy instantiate(WindowManagerService wmService, + DisplayContent content, DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) { - super(wmService, content, root, imeContainer, taskContainers); - } - - private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService, - DisplayArea.Type.BELOW_TASKS, "BelowTasks"); - private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService, - DisplayArea.Type.ABOVE_TASKS, "AboveTasks"); - private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService, - DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme"); - private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService, - DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme"); - - @Override - public void attachDisplayAreas() { - mRoot.addChild(mBelow, 0); - mRoot.addChild(mTaskContainers, 1); - mRoot.addChild(mAbove, 2); - - mAbove.addChild(mAboveBelowIme, 0); - mAbove.addChild(mImeContainer, 1); - mAbove.addChild(mAboveAboveIme, 2); - } - - @Override - public void addWindow(WindowToken token) { - switch (DisplayArea.Type.typeOf(token)) { - case ABOVE_TASKS: - if (token.getWindowLayerFromType() - < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) { - mAboveBelowIme.addChild(token); - } else { - mAboveAboveIme.addChild(token); - } - break; - case BELOW_TASKS: - mBelow.addChild(token); - break; - default: - throw new IllegalArgumentException("don't know how to sort " + token); - } - } - - /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */ - static class Provider implements DisplayAreaPolicy.Provider { - @Override - public DisplayAreaPolicy instantiate(WindowManagerService wmService, - DisplayContent content, DisplayArea.Root root, - DisplayArea<? extends WindowContainer> imeContainer, - TaskContainers taskContainers) { - return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer, - taskContainers); - } + return new DisplayAreaPolicyBuilder() + .build(wmService, content, root, imeContainer, taskContainers); } } @@ -172,7 +119,7 @@ public abstract class DisplayAreaPolicy { String name = res.getString( com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider); if (TextUtils.isEmpty(name)) { - return new DisplayAreaPolicy.Default.Provider(); + return new DisplayAreaPolicy.DefaultProvider(); } try { return (Provider) Class.forName(name).newInstance(); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java new file mode 100644 index 000000000000..885456a8488c --- /dev/null +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -0,0 +1,371 @@ +/* + * 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.policy.WindowManagerPolicy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A builder for instantiating a complex {@link DisplayAreaPolicy} + * + * <p>Given a set of features (that each target a set of window types), it builds the necessary + * DisplayArea hierarchy. + * + * <p>Example: <br /> + * + * <pre> + * // Feature for targeting everything below the magnification overlay: + * new DisplayAreaPolicyBuilder(...) + * .addFeature(new Feature.Builder(..., "Magnification") + * .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + * .build()) + * .build(...) + * + * // Builds a policy with the following hierarchy: + * - DisplayArea.Root + * - Magnification + * - DisplayArea.Tokens (Wallpapers are attached here) + * - TaskContainers + * - DisplayArea.Tokens (windows above Tasks up to IME are attached here) + * - ImeContainers + * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here) + * - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here) + * + * </pre> + * + * // TODO(display-area): document more complex scenarios where we need multiple areas per feature. + */ +class DisplayAreaPolicyBuilder { + + private final ArrayList<Feature> mFeatures = new ArrayList<>(); + + /** + * A feature that requires {@link DisplayArea DisplayArea(s)}. + */ + static class Feature { + private final String mName; + private final boolean[] mWindowLayers; + + private Feature(String name, boolean[] windowLayers) { + mName = name; + mWindowLayers = windowLayers; + } + + static class Builder { + private final WindowManagerPolicy mPolicy; + private final String mName; + private final boolean[] mLayers; + + /** + * Build a new feature that applies to a set of window types as specified by the builder + * methods. + * + * <p>The set of types is updated iteratively in the order of the method invocations. + * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should + * apply to all types except TYPE_STATUS_BAR. + * + * The builder starts out with the feature not applying to any types. + * + * @param name the name of the feature. + */ + Builder(WindowManagerPolicy policy, String name) { + mPolicy = policy; + mName = name; + mLayers = new boolean[mPolicy.getMaxWindowLayer()]; + } + + /** + * Set that the feature applies to all window types. + */ + Builder all() { + Arrays.fill(mLayers, true); + return this; + } + + /** + * Set that the feature applies to the given window types. + */ + Builder and(int... types) { + for (int i = 0; i < types.length; i++) { + int type = types[i]; + set(type, true); + } + return this; + } + + /** + * Set that the feature does not apply to the given window types. + */ + Builder except(int... types) { + for (int i = 0; i < types.length; i++) { + int type = types[i]; + set(type, false); + } + return this; + } + + /** + * Set that the feature applies window types that are layerd at or below the layer of + * the given window type. + */ + Builder upTo(int typeInclusive) { + final int max = layerFromType(typeInclusive, false); + for (int i = 0; i < max; i++) { + mLayers[i] = true; + } + set(typeInclusive, true); + return this; + } + + Feature build() { + return new Feature(mName, mLayers.clone()); + } + + private void set(int type, boolean value) { + mLayers[layerFromType(type, true)] = value; + if (type == TYPE_APPLICATION_OVERLAY) { + mLayers[layerFromType(type, true)] = value; + mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value; + mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value; + mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value; + } + } + + private int layerFromType(int type, boolean internalWindows) { + return mPolicy.getWindowLayerFromTypeLw(type, internalWindows); + } + } + } + + static class Result extends DisplayAreaPolicy { + private static final int LEAF_TYPE_TASK_CONTAINERS = 1; + private static final int LEAF_TYPE_IME_CONTAINERS = 2; + private static final int LEAF_TYPE_TOKENS = 0; + + private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer(); + + private final ArrayList<Feature> mFeatures; + private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas; + private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer]; + + Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, + DisplayArea<? extends WindowContainer> imeContainer, + DisplayArea<? extends ActivityStack> taskStacks, ArrayList<Feature> features) { + super(wmService, content, root, imeContainer, taskStacks); + mFeatures = features; + mAreas = new HashMap<>(features.size()); + for (int i = 0; i < mFeatures.size(); i++) { + mAreas.put(mFeatures.get(i), new ArrayList<>()); + } + } + + @Override + public void attachDisplayAreas() { + // This method constructs the layer hierarchy with the following properties: + // (1) Every feature maps to a set of DisplayAreas + // (2) After adding a window, for every feature the window's type belongs to, + // it is a descendant of one of the corresponding DisplayAreas of the feature. + // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows + // within a DisplayArea: + // for every pair of DisplayArea siblings (a,b), where a is below b, it holds that + // max(z-range(a)) <= min(z-range(b)) + // + // The algorithm below iteratively creates such a hierarchy: + // - Initially, all windows are attached to the root. + // - For each feature we create a set of DisplayAreas, by looping over the layers + // - if the feature does apply to the current layer, we need to find a DisplayArea + // for it to satisfy (2) + // - we can re-use the previous layer's area if: + // the current feature also applies to the previous layer, (to satisfy (3)) + // and the last feature that applied to the previous layer is the same as + // the last feature that applied to the current layer (to satisfy (2)) + // - otherwise we create a new DisplayArea below the last feature that applied + // to the current layer + + + PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer]; + final PendingArea root = new PendingArea(null, 0, null); + Arrays.fill(areaForLayer, root); + + final int size = mFeatures.size(); + for (int i = 0; i < size; i++) { + PendingArea featureArea = null; + for (int layer = 0; layer < mMaxWindowLayer; layer++) { + final Feature feature = mFeatures.get(i); + if (feature.mWindowLayers[layer]) { + if (featureArea == null || featureArea.mParent != areaForLayer[layer]) { + // No suitable DisplayArea - create a new one under the previous area + // for this layer. + featureArea = new PendingArea(feature, layer, areaForLayer[layer]); + areaForLayer[layer].mChildren.add(featureArea); + } + areaForLayer[layer] = featureArea; + } else { + featureArea = null; + } + } + } + + PendingArea leafArea = null; + int leafType = LEAF_TYPE_TOKENS; + for (int layer = 0; layer < mMaxWindowLayer; layer++) { + int type = typeOfLayer(mWmService.mPolicy, layer); + if (leafArea == null || leafArea.mParent != areaForLayer[layer] + || type != leafType) { + leafArea = new PendingArea(null, layer, areaForLayer[layer]); + areaForLayer[layer].mChildren.add(leafArea); + leafType = type; + if (leafType == LEAF_TYPE_TASK_CONTAINERS) { + leafArea.mExisting = mTaskContainers; + } else if (leafType == LEAF_TYPE_IME_CONTAINERS) { + leafArea.mExisting = mImeContainer; + } + } + leafArea.mMaxLayer = layer; + } + root.computeMaxLayer(); + root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas); + } + + @Override + public void addWindow(WindowToken token) { + DisplayArea.Tokens area = findAreaForToken(token); + area.addChild(token); + } + + @VisibleForTesting + DisplayArea.Tokens findAreaForToken(WindowToken token) { + int windowLayerFromType = token.getWindowLayerFromType(); + if (windowLayerFromType == APPLICATION_LAYER) { + // TODO(display-area): Better handle AboveAppWindows in APPLICATION_LAYER + windowLayerFromType += 1; + } else if (token.mRoundedCornerOverlay) { + windowLayerFromType = mMaxWindowLayer - 1; + } + return mAreaForLayer[windowLayerFromType]; + } + + public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) { + return mAreas.get(feature); + } + + private static int typeOfLayer(WindowManagerPolicy policy, int layer) { + if (layer == APPLICATION_LAYER) { + return LEAF_TYPE_TASK_CONTAINERS; + } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD) + || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) { + return LEAF_TYPE_IME_CONTAINERS; + } else { + return LEAF_TYPE_TOKENS; + } + } + } + + DisplayAreaPolicyBuilder addFeature(Feature feature) { + mFeatures.add(feature); + return this; + } + + Result build(WindowManagerService wmService, + DisplayContent content, DisplayArea.Root root, + DisplayArea<? extends WindowContainer> imeContainer, + DisplayArea<? extends ActivityStack> taskContainers) { + + return new Result(wmService, content, root, imeContainer, taskContainers, new ArrayList<>( + mFeatures)); + } + + static class PendingArea { + final int mMinLayer; + final ArrayList<PendingArea> mChildren = new ArrayList<>(); + final Feature mFeature; + final PendingArea mParent; + int mMaxLayer; + DisplayArea mExisting; + + PendingArea(Feature feature, + int minLayer, + PendingArea parent) { + mMinLayer = minLayer; + mFeature = feature; + mParent = parent; + } + + int computeMaxLayer() { + for (int i = 0; i < mChildren.size(); i++) { + mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer()); + } + return mMaxLayer; + } + + void instantiateChildren(DisplayArea<DisplayArea> parent, + DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<? + extends WindowContainer>>> areas) { + mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer)); + for (int i = 0; i < mChildren.size(); i++) { + final PendingArea child = mChildren.get(i); + final DisplayArea area = child.createArea(parent, areaForLayer); + parent.addChild(area, WindowContainer.POSITION_TOP); + if (mFeature != null) { + areas.get(mFeature).add(area); + } + child.instantiateChildren(area, areaForLayer, level + 1, areas); + } + } + + private DisplayArea createArea(DisplayArea<DisplayArea> parent, + DisplayArea.Tokens[] areaForLayer) { + if (mExisting != null) { + return mExisting; + } + DisplayArea.Type type; + if (mMinLayer > APPLICATION_LAYER) { + type = DisplayArea.Type.ABOVE_TASKS; + } else if (mMaxLayer < APPLICATION_LAYER) { + type = DisplayArea.Type.BELOW_TASKS; + } else { + type = DisplayArea.Type.ANY; + } + if (mFeature == null) { + final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type, + "Leaf:" + mMinLayer + ":" + mMaxLayer); + for (int i = mMinLayer; i <= mMaxLayer; i++) { + areaForLayer[i] = leaf; + } + return leaf; + } else { + return new DisplayArea(parent.mWmService, type, mFeature.mName + ":" + + mMinLayer + ":" + mMaxLayer); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2196d899406d..e3d85c84b50c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2008,10 +2008,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = display.getStackAt(stackNdx); stack.switchUser(userId); - Task task = stack.getTopMostTask(); - if (task != null && task != stack) { - stack.positionChildAtTop(task); - } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8f9caea26534..2bb67035f44b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7317,9 +7317,11 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken + " imeTargetWindowToken=" + imeTargetWindowToken); } - final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); - if (imeTarget != null) { - imeTarget.getDisplayContent().updateImeControlTarget(imeTarget); + synchronized (mGlobalLock) { + final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); + if (imeTarget != null) { + imeTarget.getDisplayContent().updateImeControlTarget(imeTarget); + } } } diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 7e6f79f0d407..70a9c09c815d 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -18,18 +18,27 @@ #define LOG_TAG "PackageManagerShellCommandDataLoader-jni" #include <android-base/logging.h> +#include <android-base/file.h> +#include <android-base/stringprintf.h> #include <android-base/unique_fd.h> -#include <nativehelper/JNIHelp.h> -#include "android-base/file.h" +#include <cutils/trace.h> +#include <sys/eventfd.h> +#include <sys/poll.h> -#include <endian.h> +#include <nativehelper/JNIHelp.h> #include <core_jni_helpers.h> +#include <endian.h> #include "dataloader.h" +#include <charconv> #include <chrono> +#include <span> +#include <string> #include <thread> +#include <unordered_map> +#include <unordered_set> namespace android { @@ -39,9 +48,26 @@ using android::base::borrowed_fd; using android::base::ReadFully; using android::base::unique_fd; +using namespace std::literals; + +using BlockSize = int16_t; +using FileIdx = int16_t; +using BlockIdx = int32_t; +using NumBlocks = int32_t; +using CompressionType = int16_t; +using RequestType = int16_t; +using MagicType = uint32_t; + static constexpr int BUFFER_SIZE = 256 * 1024; static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE; +static constexpr int COMMAND_SIZE = 4 + 2 + 2 + 4; // bytes +static constexpr int HEADER_SIZE = 2 + 2 + 4 + 2; // bytes +static constexpr std::string_view OKAY = "OKAY"sv; +static constexpr MagicType INCR = 0x52434e49; // BE INCR + +static constexpr auto PollTimeoutMs = 5000; + struct JniIds { jclass packageManagerShellCommandDataLoader; jmethodID pmscdLookupShellCommand; @@ -85,6 +111,70 @@ const JniIds& jniIds(JNIEnv* env) { return ids; } +struct BlockHeader { + FileIdx fileIdx = -1; + CompressionType compressionType = -1; + BlockIdx blockIdx = -1; + BlockSize blockSize = -1; +} __attribute__((packed)); + +static_assert(sizeof(BlockHeader) == HEADER_SIZE); + +static constexpr RequestType EXIT = 0; +static constexpr RequestType BLOCK_MISSING = 1; +static constexpr RequestType PREFETCH = 2; + +struct RequestCommand { + MagicType magic; + RequestType requestType; + FileIdx fileIdx; + BlockIdx blockIdx; +} __attribute__((packed)); + +static_assert(COMMAND_SIZE == sizeof(RequestCommand)); + +static bool sendRequest(int fd, RequestType requestType, FileIdx fileIdx = -1, + BlockIdx blockIdx = -1) { + const RequestCommand command{.magic = INCR, + .requestType = static_cast<int16_t>(be16toh(requestType)), + .fileIdx = static_cast<int16_t>(be16toh(fileIdx)), + .blockIdx = static_cast<int32_t>(be32toh(blockIdx))}; + return android::base::WriteFully(fd, &command, sizeof(command)); +} + +static int waitForDataOrSignal(int fd, int event_fd) { + struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}}; + // Wait indefinitely until either data is ready or stop signal is received + int res = poll(pfds, 2, PollTimeoutMs); + if (res <= 0) { + return res; + } + // First check if there is a stop signal + if (pfds[1].revents == POLLIN) { + return event_fd; + } + // Otherwise check if incoming data is ready + if (pfds[0].revents == POLLIN) { + return fd; + } + return -1; +} + +static bool readChunk(int fd, std::vector<uint8_t>& data) { + int32_t size; + if (!android::base::ReadFully(fd, &size, sizeof(size))) { + return false; + } + size = int32_t(be32toh(size)); + if (size <= 0) { + return false; + } + data.resize(size); + return android::base::ReadFully(fd, data.data(), data.size()); +} + +BlockHeader readHeader(std::span<uint8_t>& data); + static inline int32_t readBEInt32(borrowed_fd fd) { int32_t result; ReadFully(fd, &result, sizeof(result)); @@ -106,6 +196,22 @@ static inline int32_t skipIdSigHeaders(borrowed_fd fd) { return readBEInt32(fd); // size of the verity tree } +static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) { + constexpr int SHA256_DIGEST_SIZE = 32; + constexpr int digest_size = SHA256_DIGEST_SIZE; + constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size; + + IncFsSize total_tree_block_count = 0; + + auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE; + auto hash_block_count = block_count; + for (auto i = 0; hash_block_count > 1; i++) { + hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block; + total_tree_block_count += hash_block_count; + } + return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE; +} + static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) { if (!pfd) { ALOGE("Missing In ParcelFileDescriptor."); @@ -125,8 +231,9 @@ static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, job struct InputDesc { unique_fd fd; IncFsSize size; - IncFsBlockKind kind; - bool waitOnEof; + IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA; + bool waitOnEof = false; + bool streaming = false; }; using InputDescs = std::vector<InputDesc>; @@ -135,8 +242,7 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel InputDescs result; result.reserve(2); - const bool fromStdin = (metadata.size == 0 || *metadata.data == '-'); - if (fromStdin) { + if (metadata.size == 0 || *metadata.data == '-') { // stdin auto fd = convertPfdToFdAndDup( env, jni, @@ -146,12 +252,29 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel result.push_back(InputDesc{ .fd = std::move(fd), .size = size, - .kind = INCFS_BLOCK_KIND_DATA, .waitOnEof = true, }); } return result; } + if (*metadata.data == '+') { + // verity tree from stdin, rest is streaming + auto fd = convertPfdToFdAndDup( + env, jni, + env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdGetStdInPFD, shellCommand)); + if (fd.ok()) { + auto treeSize = verityTreeSizeForFile(size); + result.push_back(InputDesc{ + .fd = std::move(fd), + .size = treeSize, + .kind = INCFS_BLOCK_KIND_HASH, + .waitOnEof = true, + .streaming = true, + }); + } + return result; + } // local file and possibly signature const std::string filePath(metadata.data, metadata.size); @@ -163,13 +286,17 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel jni.pmscdGetLocalFile, shellCommand, env->NewStringUTF(idsigPath.c_str()))); if (idsigFd.ok()) { - ALOGE("idsig found, skipping to the tree"); - auto treeSize = skipIdSigHeaders(idsigFd); + auto treeSize = verityTreeSizeForFile(size); + auto actualTreeSize = skipIdSigHeaders(idsigFd); + if (treeSize != actualTreeSize) { + ALOGE("Verity tree size mismatch: %d vs .idsig: %d.", int(treeSize), + int(actualTreeSize)); + return {}; + } result.push_back(InputDesc{ .fd = std::move(idsigFd), .size = treeSize, .kind = INCFS_BLOCK_KIND_HASH, - .waitOnEof = false, }); } @@ -182,8 +309,6 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel result.push_back(InputDesc{ .fd = std::move(fileFd), .size = size, - .kind = INCFS_BLOCK_KIND_DATA, - .waitOnEof = false, }); } @@ -226,19 +351,32 @@ private: android::dataloader::StatusListenerPtr statusListener, android::dataloader::ServiceConnectorPtr, android::dataloader::ServiceParamsPtr) final { + CHECK(ifs) << "ifs can't be null"; + CHECK(statusListener) << "statusListener can't be null"; mArgs = params.arguments(); mIfs = ifs; + mStatusListener = statusListener; return true; } bool onStart() final { return true; } - void onStop() final {} - void onDestroy() final {} - - // IFS callbacks. - void onPendingReads(const dataloader::PendingReads& pendingReads) final {} - void onPageReads(const dataloader::PageReads& pageReads) final {} + void onStop() final { + mStopReceiving = true; + eventfd_write(mEventFd, 1); + if (mReceiverThread.joinable()) { + mReceiverThread.join(); + } + } + void onDestroy() final { + ALOGE("Sending EXIT to server."); + sendRequest(mOutFd, EXIT); + // Make sure the receiver thread stopped. + CHECK(!mReceiverThread.joinable()); + + mInFd.reset(); + mOutFd.reset(); + } - // FS callbacks. + // Installation. bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final { JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); const auto& jni = jniIds(env); @@ -257,6 +395,7 @@ private: std::vector<IncFsDataBlock> blocks; blocks.reserve(BLOCKS_COUNT); + unique_fd streamingFd; for (auto&& file : addedFiles) { auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata); if (inputs.empty()) { @@ -267,7 +406,6 @@ private: } const auto fileId = IncFs_FileIdFromMetadata(file.metadata); - const auto incfsFd(mIfs->openWrite(fileId)); if (incfsFd < 0) { ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. " @@ -277,6 +415,9 @@ private: } for (auto&& input : inputs) { + if (input.streaming && !streamingFd.ok()) { + streamingFd.reset(dup(input.fd)); + } if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof, &buffer, &blocks)) { ALOGE("Failed to copy data to IncFS file for metadata: %.*s, final file name " @@ -288,7 +429,12 @@ private: } } - ALOGE("All done."); + if (streamingFd.ok()) { + ALOGE("onPrepareImage: done, proceeding to streaming."); + return initStreaming(std::move(streamingFd)); + } + + ALOGE("onPrepareImage: done."); return true; } @@ -378,11 +524,253 @@ private: return true; } + // Read tracing. + struct TracedRead { + uint64_t timestampUs; + android::dataloader::FileId fileId; + uint32_t firstBlockIdx; + uint32_t count; + }; + + void onPageReads(const android::dataloader::PageReads& pageReads) final { + auto trace = atrace_is_tag_enabled(ATRACE_TAG); + if (CC_LIKELY(!trace)) { + return; + } + + TracedRead last = {}; + for (auto&& read : pageReads) { + if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) { + traceRead(last); + last = TracedRead{ + .timestampUs = read.bootClockTsUs, + .fileId = read.id, + .firstBlockIdx = (uint32_t)read.block, + .count = 1, + }; + } else { + ++last.count; + } + } + traceRead(last); + } + + void traceRead(const TracedRead& read) { + if (!read.count) { + return; + } + + FileIdx fileIdx = convertFileIdToFileIndex(read.fileId); + auto str = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d", + static_cast<long long>(read.firstBlockIdx), + static_cast<long long>(read.count), + static_cast<int>(fileIdx)); + ATRACE_BEGIN(str.c_str()); + ATRACE_END(); + } + + // Streaming. + bool initStreaming(unique_fd inout) { + mInFd.reset(dup(inout)); + mOutFd.reset(dup(inout)); + if (mInFd < 0 || mOutFd < 0) { + ALOGE("Failed to dup FDs."); + return false; + } + + mEventFd.reset(eventfd(0, EFD_CLOEXEC)); + if (mEventFd < 0) { + ALOGE("Failed to create eventfd."); + return false; + } + + // Awaiting adb handshake. + char okay_buf[OKAY.size()]; + if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) { + ALOGE("Failed to receive OKAY. Abort."); + return false; + } + if (std::string_view(okay_buf, OKAY.size()) != OKAY) { + ALOGE("Received '%.*s', expecting '%.*s'", (int)OKAY.size(), okay_buf, (int)OKAY.size(), + OKAY.data()); + return false; + } + + mReceiverThread = std::thread([this]() { receiver(); }); + ALOGI("Started streaming..."); + return true; + } + + // IFS callbacks. + void onPendingReads(const dataloader::PendingReads& pendingReads) final { + CHECK(mIfs); + for (auto&& pendingRead : pendingReads) { + const android::dataloader::FileId& fileId = pendingRead.id; + const auto blockIdx = static_cast<BlockIdx>(pendingRead.block); + /* + ALOGI("Missing: %d", (int) blockIdx); + */ + FileIdx fileIdx = convertFileIdToFileIndex(fileId); + if (fileIdx < 0) { + ALOGE("Failed to handle event for fileid=%s. Ignore.", + android::incfs::toString(fileId).c_str()); + continue; + } + if (mRequestedFiles.insert(fileIdx).second) { + if (!sendRequest(mOutFd, PREFETCH, fileIdx, blockIdx)) { + ALOGE("Failed to request prefetch for fileid=%s. Ignore.", + android::incfs::toString(fileId).c_str()); + mRequestedFiles.erase(fileIdx); + mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION); + } + } + sendRequest(mOutFd, BLOCK_MISSING, fileIdx, blockIdx); + } + } + + void receiver() { + std::vector<uint8_t> data; + std::vector<IncFsDataBlock> instructions; + std::unordered_map<FileIdx, unique_fd> writeFds; + while (!mStopReceiving) { + const int res = waitForDataOrSignal(mInFd, mEventFd); + if (res == 0) { + continue; + } + if (res < 0) { + ALOGE("Failed to poll. Abort."); + mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION); + break; + } + if (res == mEventFd) { + ALOGE("Received stop signal. Exit."); + break; + } + if (!readChunk(mInFd, data)) { + ALOGE("Failed to read a message. Abort."); + mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION); + break; + } + auto remainingData = std::span(data); + while (!remainingData.empty()) { + auto header = readHeader(remainingData); + if (header.fileIdx == -1 && header.compressionType == 0 && header.blockIdx == 0 && + header.blockSize == 0) { + ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).", + int(remainingData.size())); + + sendRequest(mOutFd, EXIT); + mStopReceiving = true; + break; + } + if (header.fileIdx < 0 || header.blockSize <= 0 || header.compressionType < 0 || + header.blockIdx < 0) { + ALOGE("invalid header received. Abort."); + mStopReceiving = true; + break; + } + const FileIdx fileIdx = header.fileIdx; + const android::dataloader::FileId fileId = convertFileIndexToFileId(fileIdx); + if (!android::incfs::isValidFileId(fileId)) { + ALOGE("Unknown data destination for file ID %d. " + "Ignore.", + header.fileIdx); + continue; + } + + auto& writeFd = writeFds[fileIdx]; + if (writeFd < 0) { + writeFd = this->mIfs->openWrite(fileId); + if (writeFd < 0) { + ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileIdx, + -writeFd); + break; + } + } + + const auto inst = IncFsDataBlock{ + .fileFd = writeFd, + .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx), + .compression = static_cast<IncFsCompressionKind>(header.compressionType), + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = static_cast<uint16_t>(header.blockSize), + .data = (const char*)remainingData.data(), + }; + instructions.push_back(inst); + remainingData = remainingData.subspan(header.blockSize); + } + writeInstructions(instructions); + } + writeInstructions(instructions); + } + + void writeInstructions(std::vector<IncFsDataBlock>& instructions) { + auto res = this->mIfs->writeBlocks(instructions); + if (res != instructions.size()) { + ALOGE("Dailed to write data to Incfs (res=%d when expecting %d)", res, + int(instructions.size())); + } + instructions.clear(); + } + + FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) { + // FileId is a string in format '+FileIdx\0'. + const char* meta = (const char*)&fileId; + if (*meta != '+') { + return -1; + } + + int fileIdx; + auto res = std::from_chars(meta + 1, meta + sizeof(fileId), fileIdx); + if (res.ec != std::errc{} || fileIdx < std::numeric_limits<FileIdx>::min() || + fileIdx > std::numeric_limits<FileIdx>::max()) { + return -1; + } + + return FileIdx(fileIdx); + } + + android::dataloader::FileId convertFileIndexToFileId(FileIdx fileIdx) { + IncFsFileId fileId = {}; + char* meta = (char*)&fileId; + *meta = '+'; + if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx); + ec != std::errc()) { + return {}; + } + return fileId; + } + JavaVM* const mJvm; std::string mArgs; - android::dataloader::FilesystemConnectorPtr mIfs; + android::dataloader::FilesystemConnectorPtr mIfs = nullptr; + android::dataloader::StatusListenerPtr mStatusListener = nullptr; + android::base::unique_fd mInFd; + android::base::unique_fd mOutFd; + android::base::unique_fd mEventFd; + std::thread mReceiverThread; + std::atomic<bool> mStopReceiving = false; + /** Tracks which files have been requested */ + std::unordered_set<FileIdx> mRequestedFiles; }; +BlockHeader readHeader(std::span<uint8_t>& data) { + BlockHeader header; + if (data.size() < sizeof(header)) { + return header; + } + + header.fileIdx = static_cast<FileIdx>(be16toh(*reinterpret_cast<const uint16_t*>(&data[0]))); + header.compressionType = + static_cast<CompressionType>(be16toh(*reinterpret_cast<const uint16_t*>(&data[2]))); + header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<const uint32_t*>(&data[4]))); + header.blockSize = + static_cast<BlockSize>(be16toh(*reinterpret_cast<const uint16_t*>(&data[8]))); + data = data.subspan(sizeof(header)); + + return header; +} + static void nativeInitialize(JNIEnv* env, jclass klass) { jniIds(env); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 93662c91af90..1936f13ca6e1 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1741,7 +1741,7 @@ public final class SystemServer { mSystemServiceManager.startService(SensorNotificationService.class); t.traceEnd(); - if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXTHUB)) { + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXT_HUB)) { t.traceBegin("StartContextHubSystemService"); mSystemServiceManager.startService(ContextHubSystemService.class); t.traceEnd(); diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java index bf09681e7027..6dcfaa00dfd5 100644 --- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java @@ -59,7 +59,7 @@ class UsageStatsQueryHelper { */ boolean querySince(long sinceTime) { UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser( - mUserId, sinceTime, System.currentTimeMillis(), false, false, false); + mUserId, sinceTime, System.currentTimeMillis(), UsageEvents.SHOW_ALL_EVENT_DATA); if (usageEvents == null) { return false; } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 64da6f6b8590..d7a3cfd8aeca 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -19,6 +19,12 @@ package com.android.server.job.controllers; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; +import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; +import static com.android.server.job.JobSchedulerService.NEVER_INDEX; +import static com.android.server.job.JobSchedulerService.RARE_INDEX; +import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; +import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; @@ -34,13 +40,16 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_WITHIN_QUO import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; import android.app.job.JobInfo; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.pm.PackageManagerInternal; +import android.net.Uri; import android.os.SystemClock; import android.provider.MediaStore; +import android.util.SparseIntArray; import androidx.test.runner.AndroidJUnit4; @@ -52,6 +61,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; @@ -61,7 +71,12 @@ import java.time.ZoneOffset; @RunWith(AndroidJUnit4.class) public class JobStatusTest { private static final double DELTA = 0.00001; + private static final String TEST_PACKAGE = "job.test.package"; + private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(TEST_PACKAGE, "test"); + private static final Uri TEST_MEDIA_URI = Uri.parse("content://media/path/to/media"); + @Mock + private JobSchedulerInternal mJobSchedulerInternal; private MockitoSession mMockingSession; @Before @@ -71,7 +86,7 @@ public class JobStatusTest { .strictness(Strictness.LENIENT) .mockStatic(LocalServices.class) .startMocking(); - doReturn(mock(JobSchedulerInternal.class)) + doReturn(mJobSchedulerInternal) .when(() -> LocalServices.getService(JobSchedulerInternal.class)); doReturn(mock(PackageManagerInternal.class)) .when(() -> LocalServices.getService(PackageManagerInternal.class)); @@ -94,6 +109,82 @@ public class JobStatusTest { } } + private static void assertEffectiveBucketForMediaExemption(JobStatus jobStatus, + boolean exemptionGranted) { + final SparseIntArray effectiveBucket = new SparseIntArray(); + effectiveBucket.put(ACTIVE_INDEX, ACTIVE_INDEX); + effectiveBucket.put(WORKING_INDEX, WORKING_INDEX); + effectiveBucket.put(FREQUENT_INDEX, exemptionGranted ? WORKING_INDEX : FREQUENT_INDEX); + effectiveBucket.put(RARE_INDEX, exemptionGranted ? WORKING_INDEX : RARE_INDEX); + effectiveBucket.put(NEVER_INDEX, NEVER_INDEX); + effectiveBucket.put(RESTRICTED_INDEX, RESTRICTED_INDEX); + for (int i = 0; i < effectiveBucket.size(); i++) { + jobStatus.setStandbyBucket(effectiveBucket.keyAt(i)); + assertEquals(effectiveBucket.valueAt(i), jobStatus.getEffectiveStandbyBucket()); + } + } + + @Test + public void testMediaBackupExemption_lateConstraint() { + final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .setOverrideDeadline(12) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); + assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false); + } + + @Test + public void testMediaBackupExemption_noConnectivityConstraint() { + final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .build(); + when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); + assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false); + } + + @Test + public void testMediaBackupExemption_noContentTriggerConstraint() { + final JobInfo networkJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); + assertEffectiveBucketForMediaExemption(createJobStatus(networkJob), false); + } + + @Test + public void testMediaBackupExemption_wrongSourcePackage() { + final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package"); + assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false); + } + + @Test + public void testMediaBackupExemption_nonMediaUri() { + final Uri nonMediaUri = Uri.parse("content://not-media/any/path"); + final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(nonMediaUri, 0)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); + assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false); + } + + @Test + public void testMediaBackupExemptionGranted() { + final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); + assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), true); + } + @Test public void testFraction() throws Exception { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java new file mode 100644 index 000000000000..607cd816d7dd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java @@ -0,0 +1,67 @@ +/* + * 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.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Locale; + +/** + * Tests for static methods of {@link HdmiControlService} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class HdmiControlServiceStaticTest { + + @Test + public void localToMenuLanguage_english() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.ENGLISH)).isEqualTo("eng"); + } + + @Test + public void localToMenuLanguage_german() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.GERMAN)).isEqualTo("ger"); + } + + @Test + public void localToMenuLanguage_taiwan() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.TAIWAN)).isEqualTo("chi"); + } + + @Test + public void localToMenuLanguage_macau() { + assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "MO"))).isEqualTo( + "chi"); + } + + @Test + public void localToMenuLanguage_hongkong() { + assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "HK"))).isEqualTo( + "chi"); + } + + @Test + public void localToMenuLanguage_chinese() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.CHINESE)).isEqualTo("zho"); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index b1cdea2060b8..dc4876b665f7 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -19,9 +19,9 @@ package com.android.server.people.data; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -189,7 +189,7 @@ public final class UsageStatsQueryHelperTest { private void addUsageEvents(UsageEvents.Event... events) { UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{}); when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(), - anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(usageEvents); + eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents); } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 75efdd717d02..bc33f0824214 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -51,8 +51,6 @@ import static android.os.Build.VERSION_CODES.P; import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; -import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; @@ -1144,14 +1142,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testEnqueueNotificationWithTag_WritesExpectedLog() throws Exception { + public void testEnqueueNotificationWithTag_WritesExpectedLogs() throws Exception { final String tag = "testEnqueueNotificationWithTag_WritesExpectedLog"; mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, generateNotificationRecord(null).getNotification(), 0); waitForIdle(); assertEquals(1, mNotificationRecordLogger.getCalls().size()); + NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0); - assertTrue(call.shouldLog); + assertTrue(call.shouldLogReported); assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, call.event); assertNotNull(call.r); @@ -1161,7 +1160,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(PKG, call.r.getSbn().getPackageName()); assertEquals(0, call.r.getSbn().getId()); assertEquals(tag, call.r.getSbn().getTag()); - assertNotNull(call.r.getSbn().getInstanceId()); assertEquals(0, call.getInstanceId()); // Fake instance IDs are assigned in order } @@ -1180,13 +1178,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { waitForIdle(); assertEquals(2, mNotificationRecordLogger.getCalls().size()); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); + assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(0).event); assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); - assertTrue(mNotificationRecordLogger.get(1).shouldLog); + assertTrue(mNotificationRecordLogger.get(1).shouldLogReported); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED, mNotificationRecordLogger.get(1).event); @@ -1195,16 +1193,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates() throws Exception { - final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates"; + public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate() throws Exception { + final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate"; mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, generateNotificationRecord(null).getNotification(), 0); mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, generateNotificationRecord(null).getNotification(), 0); waitForIdle(); assertEquals(2, mNotificationRecordLogger.getCalls().size()); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); - assertFalse(mNotificationRecordLogger.get(1).shouldLog); + assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); + assertEquals( + NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, + mNotificationRecordLogger.get(0).event); + assertFalse(mNotificationRecordLogger.get(1).shouldLogReported); + assertNull(mNotificationRecordLogger.get(1).event); + } + + @Test + public void testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate() throws Exception { + final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate"; + mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, + generateNotificationRecord(null).getNotification(), + 0); + final Notification notif = generateNotificationRecord(null).getNotification(); + notif.extras.putString(Notification.EXTRA_TITLE, "Changed title"); + mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0); + waitForIdle(); + assertEquals(2, mNotificationRecordLogger.getCalls().size()); + assertEquals( + NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, + mNotificationRecordLogger.get(0).event); + assertNull(mNotificationRecordLogger.get(1).event); } @Test @@ -1224,20 +1243,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(0).event); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); + assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); - assertEquals(REASON_APP_CANCEL, mNotificationRecordLogger.get(1).reason); assertEquals( NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL, mNotificationRecordLogger.get(1).event); - assertTrue(mNotificationRecordLogger.get(1).shouldLog); assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(2).event); - assertTrue(mNotificationRecordLogger.get(2).shouldLog); + assertTrue(mNotificationRecordLogger.get(2).shouldLogReported); // New instance ID because notification was canceled before re-post assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId()); } @@ -2605,6 +2622,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testSystemNotificationListenerCanUnsnooze() throws Exception { + final NotificationRecord nr = generateNotificationRecord( + mTestNotificationChannel, 2, "group", false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "testSystemNotificationListenerCanUnsnooze", + nr.getSbn().getId(), nr.getSbn().getNotification(), + nr.getSbn().getUserId()); + waitForIdle(); + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + nr.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + ManagedServices.ManagedServiceInfo listener = mListeners.new ManagedServiceInfo( + null, new ComponentName(PKG, "test_class"), mUid, true, null, 0); + listener.isSystem = true; + when(mListeners.checkServiceTokenLocked(any())).thenReturn(listener); + + mBinderService.unsnoozeNotificationFromSystemListener(null, nr.getKey()); + waitForIdle(); + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifs.length); + assertNotNull(notifs[0].getKey());//mService.getNotificationRecord(nr.getSbn().getKey())); + } + + @Test public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); @@ -3396,11 +3440,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // so we only get the cancel notification. assertEquals(1, mNotificationRecordLogger.getCalls().size()); - assertEquals(REASON_CANCEL, mNotificationRecordLogger.get(0).reason); assertEquals( NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD, mNotificationRecordLogger.get(0).event); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); } @@ -4326,6 +4368,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testOnNotificationVisibilityChanged_triggersVisibilityLog() { + final NotificationRecord r = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + r.setTextChanged(true); + mService.addNotification(r); + + mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[] + {NotificationVisibility.obtain(r.getKey(), 1, 1, true)}, + new NotificationVisibility[]{}); + + assertEquals(1, mNotificationRecordLogger.getCalls().size()); + assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN, + mNotificationRecordLogger.get(0).event); + assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); + + mService.mNotificationDelegate.onNotificationVisibilityChanged( + new NotificationVisibility[]{}, + new NotificationVisibility[] + {NotificationVisibility.obtain(r.getKey(), 1, 1, true)} + ); + + assertEquals(2, mNotificationRecordLogger.getCalls().size()); + assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE, + mNotificationRecordLogger.get(1).event); + assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); + } + + @Test public void testSetNotificationsShownFromListener_triggersInterruptionUsageStat() throws RemoteException { final NotificationRecord r = generateNotificationRecord( diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java index b120dbee03c5..2a17bae57c8e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java @@ -26,24 +26,26 @@ import java.util.List; */ class NotificationRecordLoggerFake implements NotificationRecordLogger { static class CallRecord extends NotificationRecordPair { - static final int INVALID = -1; - public int position = INVALID, buzzBeepBlink = INVALID, reason = INVALID; - public boolean shouldLog; public UiEventLogger.UiEventEnum event; + + // The following fields are only relevant to maybeLogNotificationPosted() calls. + static final int INVALID = -1; + public int position = INVALID, buzzBeepBlink = INVALID; + public boolean shouldLogReported; + CallRecord(NotificationRecord r, NotificationRecord old, int position, int buzzBeepBlink) { super(r, old); - this.position = position; this.buzzBeepBlink = buzzBeepBlink; - shouldLog = shouldLog(buzzBeepBlink); - event = NotificationReportedEvent.fromRecordPair(this); + shouldLogReported = shouldLogReported(buzzBeepBlink); + event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null; } - CallRecord(NotificationRecord r, int reason, int dismissalSurface) { + + CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) { super(r, null); - this.reason = reason; - shouldLog = true; - event = NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface); + shouldLogReported = false; + this.event = event; } } private List<CallRecord> mCalls = new ArrayList<>(); @@ -57,14 +59,19 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger { } @Override - public void logNotificationReported(NotificationRecord r, NotificationRecord old, + public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old, int position, int buzzBeepBlink) { mCalls.add(new CallRecord(r, old, position, buzzBeepBlink)); } @Override public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) { - mCalls.add(new CallRecord(r, reason, dismissalSurface)); + mCalls.add(new CallRecord(r, + NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface))); } + @Override + public void logNotificationVisibility(NotificationRecord r, boolean visible) { + mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible))); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 3186d539b817..1dd0b1a6f359 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -15,15 +15,18 @@ */ package com.android.server.notification; +import static com.android.server.notification.SnoozeHelper.EXTRA_KEY; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -322,8 +325,12 @@ public class SnoozeHelperTest extends UiServiceTestCase { mSnoozeHelper.snooze(r, 1000); NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL); mSnoozeHelper.snooze(r2, 1000); + reset(mAm); mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM); verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r); + ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).cancel(captor.capture()); + assertEquals(r.getKey(), captor.getValue().getIntent().getStringExtra(EXTRA_KEY)); } @Test @@ -332,8 +339,10 @@ public class SnoozeHelperTest extends UiServiceTestCase { mSnoozeHelper.snooze(r, 1000); NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL); mSnoozeHelper.snooze(r2, 1000); + reset(mAm); mSnoozeHelper.repost(r.getKey()); verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r); + verify(mAm).cancel(any(PendingIntent.class)); } @Test @@ -370,31 +379,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test - public void testGetSnoozedByUser() throws Exception { - NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); - NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM); - NotificationRecord r4 = getNotificationRecord("pkg2", 3, "three", UserHandle.CURRENT); - mSnoozeHelper.snooze(r, 1000); - mSnoozeHelper.snooze(r2, 1000); - mSnoozeHelper.snooze(r3, 1000); - mSnoozeHelper.snooze(r4, 1000); - IntArray profileIds = new IntArray(); - profileIds.add(UserHandle.USER_SYSTEM); - when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); - assertEquals(3, mSnoozeHelper.getSnoozed().size()); - profileIds = new IntArray(); - profileIds.add(UserHandle.USER_CURRENT); - when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); - assertEquals(1, mSnoozeHelper.getSnoozed().size()); - } - - @Test - public void testGetSnoozedByUser_managedProfiles() throws Exception { - IntArray profileIds = new IntArray(); - profileIds.add(UserHandle.USER_CURRENT); - profileIds.add(UserHandle.USER_SYSTEM); - when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); + public void testGetSnoozedBy() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java new file mode 100644 index 000000000000..8ac1d24333be --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -0,0 +1,214 @@ +/* + * 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; + +import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; +import static com.android.server.wm.DisplayArea.Type.ANY; +import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import static java.util.stream.Collectors.toList; + +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; + +import org.hamcrest.CustomTypeSafeMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Presubmit +public class DisplayAreaPolicyBuilderTest { + + @Rule + public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); + + private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null); + + @Test + public void testBuilder() { + WindowManagerService wms = mSystemServices.getWindowManagerService(); + DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms); + DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks"); + + final Feature foo; + final Feature bar; + + DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .addFeature(foo = new Feature.Builder(mPolicy, "Foo") + .upTo(TYPE_STATUS_BAR) + .and(TYPE_NAVIGATION_BAR) + .build()) + .addFeature(bar = new Feature.Builder(mPolicy, "Bar") + .all() + .except(TYPE_STATUS_BAR) + .build()) + .build(wms, mock(DisplayContent.class), root, ime, tasks); + + policy.attachDisplayAreas(); + + assertThat(policy.getDisplayAreas(foo), is(not(empty()))); + assertThat(policy.getDisplayAreas(bar), is(not(empty()))); + + assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), + is(not(decendantOfOneOf(policy.getDisplayAreas(bar))))); + + assertThat(tasks, + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(tasks, + is(decendantOfOneOf(policy.getDisplayAreas(bar)))); + + assertThat(ime, + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(ime, + is(decendantOfOneOf(policy.getDisplayAreas(bar)))); + + List<DisplayArea<?>> actualOrder = collectLeafAreas(root); + Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks); + actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList()); + + Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets, + v -> v.stream().min(Integer::compareTo).get()); + Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets, + v -> v.stream().max(Integer::compareTo).get()); + + assertThat(expectedByMinLayer, is(equalTo(expectedByMaxLayer))); + assertThat(actualOrder, is(equalTo(expectedByMaxLayer))); + } + + private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) { + return zSets.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> f.apply(e.getValue()))); + } + + private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) { + ArrayList<DisplayArea<?>> leafs = new ArrayList<>(); + traverseLeafAreas(root, leafs::add); + return leafs; + } + + private Map<DisplayArea<?>, Set<Integer>> calculateZSets( + DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root, + DisplayArea<WindowContainer> ime, + DisplayArea<ActivityStack> tasks) { + Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>(); + int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION, + TYPE_APPLICATION_OVERLAY}; + for (int type : types) { + WindowToken token = tokenOfType(type); + recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets); + } + recordLayer(tasks, APPLICATION_LAYER, zSets); + recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets); + return zSets; + } + + private void recordLayer(DisplayArea<?> area, int layer, + Map<DisplayArea<?>, Set<Integer>> zSets) { + zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer); + } + + private Matcher<WindowContainer> decendantOfOneOf(List<? extends WindowContainer> expected) { + return new CustomTypeSafeMatcher<WindowContainer>("descendant of one of " + expected) { + @Override + protected boolean matchesSafely(WindowContainer actual) { + for (WindowContainer expected : expected) { + WindowContainer candidate = actual; + while (candidate != null && candidate.getParent() != candidate) { + if (candidate.getParent() == expected) { + return true; + } + candidate = candidate.getParent(); + } + } + return false; + } + + @Override + protected void describeMismatchSafely(WindowContainer item, + Description description) { + description.appendText("was ").appendValue(item); + while (item != null && item.getParent() != item) { + item = item.getParent(); + description.appendText(", child of ").appendValue(item); + } + } + }; + } + + private WindowToken tokenOfType(int type) { + WindowToken m = mock(WindowToken.class); + when(m.getWindowLayerFromType()).thenReturn(mPolicy.getWindowLayerFromTypeLw(type)); + return m; + } + + private static void traverseLeafAreas(DisplayArea<?> root, Consumer<DisplayArea<?>> consumer) { + boolean leaf = true; + for (int i = 0; i < root.getChildCount(); i++) { + WindowContainer child = root.getChildAt(i); + if (child instanceof DisplayArea<?>) { + traverseLeafAreas((DisplayArea<?>) child, consumer); + leaf = false; + } + } + if (leaf) { + consumer.accept(root); + } + } + + private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root { + + SurfacelessDisplayAreaRoot(WindowManagerService wms) { + super(wms); + } + + @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceControlBuilder(); + } + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index c1a1d5ecd3c8..31206315618e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -34,13 +34,13 @@ public class DisplayAreaProviderTest { @Test public void testFromResources_emptyProvider() { Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")), - Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class)); + Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class)); } @Test public void testFromResources_nullProvider() { Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)), - Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class)); + Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index f517881d835b..8ad75053060f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -56,6 +56,7 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.StrictMode; import android.os.UserHandle; +import android.util.Log; import android.view.InputChannel; import android.view.Surface; import android.view.SurfaceControl; @@ -120,11 +121,22 @@ public class SystemServicesTestRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + Throwable throwable = null; try { runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp); base.evaluate(); + } catch (Throwable t) { + throwable = t; } finally { - tearDown(); + try { + tearDown(); + } catch (Throwable t) { + if (throwable != null) { + Log.e("SystemServicesTestRule", "Suppressed: ", throwable); + t.addSuppressed(throwable); + } + throw t; + } } } }; diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 420695dc51e4..df5b311bbab1 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -500,6 +500,19 @@ public class UsageStatsService extends SystemService implements == PackageManager.PERMISSION_GRANTED); } + /** + * Obfuscate both {@link UsageEvents.Event#NOTIFICATION_SEEN} and + * {@link UsageEvents.Event#NOTIFICATION_INTERRUPTION} events if the provided calling uid does + * not hold the {@link android.Manifest.permission.MANAGE_NOTIFICATIONS} permission. + */ + private boolean shouldObfuscateNotificationEvents(int callingPid, int callingUid) { + if (callingUid == Process.SYSTEM_UID) { + return false; + } + return !(getContext().checkPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS, + callingPid, callingUid) == PackageManager.PERMISSION_GRANTED); + } + private static void deleteRecursively(File f) { File[] files = f.listFiles(); if (files != null) { @@ -1038,9 +1051,7 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - UsageEvents queryEvents(int userId, long beginTime, long endTime, - boolean shouldObfuscateInstantApps, boolean shouldHideShortcutInvocationEvents, - boolean shouldHideLocusIdEvents) { + UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) { synchronized (mLock) { if (!mUserUnlockedStates.get(userId)) { Slog.w(TAG, "Failed to query events for locked user " + userId); @@ -1051,8 +1062,7 @@ public class UsageStatsService extends SystemService implements if (service == null) { return null; // user was stopped or removed } - return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps, - shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents); + return service.queryEvents(beginTime, endTime, flags); } } @@ -1475,10 +1485,15 @@ public class UsageStatsService extends SystemService implements try { final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( userId, callingPackage, callingPid, callingUid); - boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); - return UsageStatsService.this.queryEvents(userId, beginTime, endTime, - obfuscateInstantApps, hideShortcutInvocationEvents, - shouldHideLocusIdEvents); + final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); + final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents( + callingPid, callingUid); + int flags = UsageEvents.SHOW_ALL_EVENT_DATA; + if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS; + if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS; + if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS; + if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS; + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags); } finally { Binder.restoreCallingIdentity(token); } @@ -1525,10 +1540,15 @@ public class UsageStatsService extends SystemService implements try { final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( userId, callingPackage, callingPid, callingUid); - boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); - return UsageStatsService.this.queryEvents(userId, beginTime, endTime, - obfuscateInstantApps, hideShortcutInvocationEvents, - shouldHideLocusIdEvents); + final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents( + callingPid, callingUid); + boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); + int flags = UsageEvents.SHOW_ALL_EVENT_DATA; + if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS; + if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS; + if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS; + if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS; + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags); } finally { Binder.restoreCallingIdentity(token); } @@ -2144,12 +2164,8 @@ public class UsageStatsService extends SystemService implements } @Override - public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime, - boolean obfuscateInstantApps, boolean shouldHideShortcutInvocationEvents, - boolean shouldHideLocusIdEvents) { - return UsageStatsService.this.queryEvents( - userId, beginTime, endTime, obfuscateInstantApps, - shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents); + public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime, int flags) { + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags); } @Override diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index d9317ace6e24..db26d88dbfbb 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -18,6 +18,10 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.DEVICE_STARTUP; +import static android.app.usage.UsageEvents.HIDE_LOCUS_EVENTS; +import static android.app.usage.UsageEvents.HIDE_SHORTCUT_EVENTS; +import static android.app.usage.UsageEvents.OBFUSCATE_INSTANT_APPS; +import static android.app.usage.UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS; import static android.app.usage.UsageStatsManager.INTERVAL_BEST; import static android.app.usage.UsageStatsManager.INTERVAL_COUNT; import static android.app.usage.UsageStatsManager.INTERVAL_DAILY; @@ -481,8 +485,7 @@ class UserUsageStatsService { return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner); } - UsageEvents queryEvents(final long beginTime, final long endTime, boolean obfuscateInstantApps, - boolean hideShortcutInvocationEvents, boolean hideLocusIdEvents) { + UsageEvents queryEvents(final long beginTime, final long endTime, int flags) { if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) { return null; } @@ -500,15 +503,22 @@ class UserUsageStatsService { } Event event = stats.events.get(i); - if (hideShortcutInvocationEvents - && event.mEventType == Event.SHORTCUT_INVOCATION) { + final int eventType = event.mEventType; + if (eventType == Event.SHORTCUT_INVOCATION + && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) { continue; } - if (hideLocusIdEvents - && event.mEventType == Event.LOCUS_ID_SET) { + if (eventType == Event.LOCUS_ID_SET + && (flags & HIDE_LOCUS_EVENTS) == HIDE_LOCUS_EVENTS) { continue; } - if (obfuscateInstantApps) { + if ((eventType == Event.NOTIFICATION_SEEN + || eventType == Event.NOTIFICATION_INTERRUPTION) + && (flags & OBFUSCATE_NOTIFICATION_EVENTS) + == OBFUSCATE_NOTIFICATION_EVENTS) { + event = event.getObfuscatedNotificationEvent(); + } + if ((flags & OBFUSCATE_INSTANT_APPS) == OBFUSCATE_INSTANT_APPS) { event = event.getObfuscatedIfInstantApp(); } if (event.mPackage != null) { diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml index 41109b43ab82..ef56fc827420 100644 --- a/startop/iorap/functional_tests/AndroidTest.xml +++ b/startop/iorap/functional_tests/AndroidTest.xml @@ -34,6 +34,11 @@ <option name="run-command" value="rm -r /data/misc/iorapd/*" /> <option name="run-command" value="sleep 1" /> + <!-- Set system properties to enable perfetto tracing, readahead and detailed logging. --> + <option name="run-command" value="setprop iorapd.perfetto.enable true" /> + <option name="run-command" value="setprop iorapd.readahead.enable true" /> + <option name="run-command" value="setprop iorapd.log.verbose true" /> + <option name="run-command" value="start iorapd" /> <!-- give it some time to restart the service; otherwise the first unit test might fail --> @@ -45,9 +50,5 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> </test> - <!-- using DeviceSetup again does not work. we simply leave the device in a semi-bad - state. there is no way to clean this up as far as I know. - --> - </configuration> diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java index bd8a45c2ca00..40023878af19 100644 --- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java +++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java @@ -67,7 +67,7 @@ public class IorapWorkFlowTest { private static final String TEST_ACTIVITY_NAME = "com.android.settings.Settings"; private static final String DB_PATH = "/data/misc/iorapd/sqlite.db"; - private static final Duration TIMEOUT = Duration.ofSeconds(20L); + private static final Duration TIMEOUT = Duration.ofSeconds(300L); private static final String READAHEAD_INDICATOR = "Description = /data/misc/iorapd/com.android.settings/none/com.android.settings.Settings/compiled_traces/compiled_trace.pb"; @@ -88,7 +88,7 @@ public class IorapWorkFlowTest { mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds()); } - @Test + @Test (timeout = 300000) public void testApp() throws Exception { assertThat(mDevice, notNullValue()); @@ -247,7 +247,7 @@ public class IorapWorkFlowTest { if (supplier.getAsBoolean()) { return true; } - TimeUnit.SECONDS.sleep(totalSleepTimeSeconds); + TimeUnit.SECONDS.sleep(sleepIntervalSeconds); totalSleepTimeSeconds += sleepIntervalSeconds; if (totalSleepTimeSeconds > timeout.getSeconds()) { return false; @@ -367,7 +367,7 @@ public class IorapWorkFlowTest { * * <p> This should be run as root.</p> */ - private String executeShellCommand(String cmd) throws Exception { + private static String executeShellCommand(String cmd) throws Exception { Log.i(TAG, "Execute: " + cmd); return UiDevice.getInstance( InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 51b4a31ea8b2..c51a8520ad3b 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2377,7 +2377,7 @@ public class CarrierConfigManager { * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}. * * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1). - * If the key is invalid or not configured, a default value (RSRP | RSSNR = 1 << 0 | 1 << 2) + * If the key is invalid or not configured, a default value (RSRP = 1 << 0) * will apply. * * @hide @@ -4334,7 +4334,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000); sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, - CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR); + CellSignalStrengthLte.USE_RSRP); // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 1cd45e93a52a..2529387b19b3 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -181,7 +181,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mCqi = CellInfo.UNAVAILABLE; mTimingAdvance = CellInfo.UNAVAILABLE; mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; - mParametersUseForLevel = USE_RSRP | USE_RSSNR; + mParametersUseForLevel = USE_RSRP; } /** {@inheritDoc} */ @@ -236,7 +236,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P int[] rsrpThresholds, rsrqThresholds, rssnrThresholds; boolean rsrpOnly; if (cc == null) { - mParametersUseForLevel = USE_RSRP | USE_RSSNR; + mParametersUseForLevel = USE_RSRP; rsrpThresholds = sRsrpThresholds; rsrqThresholds = sRsrqThresholds; rssnrThresholds = sRssnrThresholds; diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index c40573b25068..6fdc13e6a31b 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -492,6 +492,7 @@ public interface RILConstants { int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208; int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209; int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210; + int RIL_REQUEST_GET_BARRING_INFO = 211; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java index 20d0e9690e8e..ae20cae36839 100644 --- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java +++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java @@ -430,10 +430,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException { String apkPath = getApkPath(TARGET_PACKAGE); String appDir = apkPath.substring(0, apkPath.lastIndexOf("/")); + // Exclude directories since we only care about files. HashSet<String> actualFiles = new HashSet<>(Arrays.asList( - expectRemoteCommandToSucceed("ls " + appDir).split("\n"))); - assertTrue(actualFiles.remove("lib")); - assertTrue(actualFiles.remove("oat")); + expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n"))); HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames)); assertEquals(expectedFiles, actualFiles); diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 95b8f6700c76..da45d9a258bd 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -296,6 +296,8 @@ public class AppLaunch extends InstrumentationTestCase { AppLaunchResult launchResults = null; if (hasFailureOnFirstLaunch(launch)) { // skip if the app has failures while launched first + Log.w(TAG, "Has failures on first launch: " + launch.getApp()); + forceStopApp(launch.getApp()); continue; } AtraceLogger atraceLogger = null; diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index 957216e17925..37842de92e79 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -45,6 +45,7 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.InsetsSourceTest", "android.view.InsetsSourceConsumerTest", "android.view.InsetsStateTest", + "android.view.WindowMetricsTest" }; public FrameworksTestsFilter(Bundle testArgs) { diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 99901808ec3e..9c01d3643c19 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -738,7 +738,16 @@ public final class Credential implements Parcelable { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("IMSI: ").append(mImsi).append("\n"); + String imsi; + if (mImsi != null) { + if (mImsi.length() > 6 && mImsi.charAt(6) != '*') { + // Truncate the full IMSI from the log + imsi = mImsi.substring(0, 6) + "****"; + } else { + imsi = mImsi; + } + builder.append("IMSI: ").append(imsi).append("\n"); + } builder.append("EAPType: ").append(mEapType).append("\n"); return builder.toString(); } diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java index 61f18e0b7191..89f642fdbb66 100644 --- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java +++ b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java @@ -49,15 +49,16 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; /** - * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The + * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework - used + * to encapsulate the Wi-Fi 80211nl management interface. The * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions. * * @hide */ @SystemApi -@SystemService(Context.WIFI_COND_SERVICE) -public class WifiCondManager { - private static final String TAG = "WifiCondManager"; +@SystemService(Context.WIFI_NL80211_SERVICE) +public class WifiNl80211Manager { + private static final String TAG = "WifiNl80211Manager"; private boolean mVerboseLoggingEnabled = false; /** @@ -316,14 +317,14 @@ public class WifiCondManager { public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5; /** @hide */ - public WifiCondManager(Context context) { + public WifiNl80211Manager(Context context) { mAlarmManager = context.getSystemService(AlarmManager.class); mEventHandler = new Handler(context.getMainLooper()); } /** @hide */ @VisibleForTesting - public WifiCondManager(Context context, IWificond wificond) { + public WifiNl80211Manager(Context context, IWificond wificond) { this(context); mWificond = wificond; } @@ -485,7 +486,7 @@ public class WifiCondManager { } /** - * Enable or disable verbose logging of the WifiCondManager module. + * Enable or disable verbose logging of the WifiNl80211Manager module. * @param enable True to enable verbose logging. False to disable verbose logging. */ public void enableVerboseLogging(boolean enable) { @@ -493,7 +494,7 @@ public class WifiCondManager { } /** - * Register a death notification for the WifiCondManager which acts as a proxy for the + * Register a death notification for the WifiNl80211Manager which acts as a proxy for the * wificond daemon (i.e. the death listener will be called when and if the wificond daemon * dies). * @@ -518,7 +519,7 @@ public class WifiCondManager { // We already have a wificond handle. return true; } - IBinder binder = ServiceManager.getService(Context.WIFI_COND_SERVICE); + IBinder binder = ServiceManager.getService(Context.WIFI_NL80211_SERVICE); mWificond = IWificond.Stub.asInterface(binder); if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java index b745a341b459..a8184068ff5a 100644 --- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java +++ b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java @@ -71,10 +71,10 @@ import java.util.List; import java.util.Set; /** - * Unit tests for {@link android.net.wifi.WifiCondManager}. + * Unit tests for {@link android.net.wifi.wificond.WifiNl80211Manager}. */ @SmallTest -public class WifiCondManagerTest { +public class WifiNl80211ManagerTest { @Mock private IWificond mWificond; @Mock @@ -86,21 +86,21 @@ public class WifiCondManagerTest { @Mock private IApInterface mApInterface; @Mock - private WifiCondManager.SoftApCallback mSoftApListener; + private WifiNl80211Manager.SoftApCallback mSoftApListener; @Mock - private WifiCondManager.SendMgmtFrameCallback mSendMgmtFrameCallback; + private WifiNl80211Manager.SendMgmtFrameCallback mSendMgmtFrameCallback; @Mock - private WifiCondManager.ScanEventCallback mNormalScanCallback; + private WifiNl80211Manager.ScanEventCallback mNormalScanCallback; @Mock - private WifiCondManager.ScanEventCallback mPnoScanCallback; + private WifiNl80211Manager.ScanEventCallback mPnoScanCallback; @Mock - private WifiCondManager.PnoScanRequestCallback mPnoScanRequestCallback; + private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback; @Mock private Context mContext; private TestLooper mLooper; private TestAlarmManager mTestAlarmManager; private AlarmManager mAlarmManager; - private WifiCondManager mWificondControl; + private WifiNl80211Manager mWificondControl; private static final String TEST_INTERFACE_NAME = "test_wlan_if"; private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1"; private static final String TEST_INVALID_INTERFACE_NAME = "asdf"; @@ -180,7 +180,7 @@ public class WifiCondManagerTest { when(mWificond.tearDownApInterface(any())).thenReturn(true); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); - mWificondControl = new WifiCondManager(mContext, mWificond); + mWificondControl = new WifiNl80211Manager(mContext, mWificond); assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, mNormalScanCallback, mPnoScanCallback)); @@ -492,7 +492,7 @@ public class WifiCondManagerTest { // getScanResults should fail. assertEquals(0, mWificondControl.getScanResults(TEST_INTERFACE_NAME, - WifiCondManager.SCAN_TYPE_SINGLE_SCAN).size()); + WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN).size()); } /** @@ -786,10 +786,10 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameCalledTwiceBeforeFinished() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb1 = mock( - WifiCondManager.SendMgmtFrameCallback.class); - WifiCondManager.SendMgmtFrameCallback cb2 = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb1 = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb2 = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, Runnable::run, cb1); @@ -800,7 +800,7 @@ public class WifiCondManagerTest { mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, Runnable::run, cb2); - verify(cb2).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED); + verify(cb2).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED); // verify SendMgmtFrame() still was only called once i.e. not called again verify(mClientInterface, times(1)) .SendMgmtFrame(any(), any(), anyInt()); @@ -811,8 +811,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameThrowsException() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -834,7 +834,7 @@ public class WifiCondManagerTest { verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); sendMgmtFrameEventCaptor.getValue().OnFailure( - WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); mLooper.dispatchAll(); handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); @@ -848,8 +848,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameSuccess() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -882,8 +882,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameFailure() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -898,10 +898,10 @@ public class WifiCondManagerTest { Runnable::run, cb); sendMgmtFrameEventCaptor.getValue().OnFailure( - WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); mLooper.dispatchAll(); verify(cb, never()).onAck(anyInt()); - verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); // verify that even if timeout is triggered afterwards, SendMgmtFrameCallback is not @@ -917,8 +917,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameTimeout() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -935,7 +935,7 @@ public class WifiCondManagerTest { handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); mLooper.dispatchAll(); verify(cb, never()).onAck(anyInt()); - verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); // verify that even if onAck() callback is triggered after timeout, // SendMgmtFrameCallback is not triggered again @@ -1006,7 +1006,8 @@ public class WifiCondManagerTest { sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS); mLooper.dispatchAll(); verify(mSendMgmtFrameCallback, never()).onAck(anyInt()); - verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + verify(mSendMgmtFrameCallback).onFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); } /** @@ -1032,9 +1033,10 @@ public class WifiCondManagerTest { handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); // OnFailure posts to the handler sendMgmtFrameEventCaptor.getValue().OnFailure( - WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); mLooper.dispatchAll(); - verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + verify(mSendMgmtFrameCallback).onFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); } /** |