summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java2
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java37
-rw-r--r--apex/statsd/Android.bp3
-rw-r--r--api/current.txt28
-rwxr-xr-xapi/system-current.txt37
-rw-r--r--api/system-lint-baseline.txt2
-rw-r--r--cmds/statsd/Android.bp3
-rw-r--r--cmds/statsd/src/atoms.proto2
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java12
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java58
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java13
-rw-r--r--core/java/android/app/AppOpsManager.java4
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/ITaskStackListener.aidl8
-rw-r--r--core/java/android/app/SystemServiceRegistry.java10
-rw-r--r--core/java/android/app/TaskStackListener.java4
-rw-r--r--core/java/android/app/admin/FactoryResetProtectionPolicy.java50
-rw-r--r--core/java/android/app/usage/UsageEvents.java44
-rw-r--r--core/java/android/content/Context.java13
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java17
-rw-r--r--core/java/android/content/pm/PackageManager.java24
-rw-r--r--core/java/android/content/pm/PackageParser.java5
-rw-r--r--core/java/android/content/pm/parsing/ApkParseUtils.java3
-rw-r--r--core/java/android/content/pm/parsing/PackageImpl.java10
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java36
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java2
-rw-r--r--core/java/android/inputmethodservice/OWNERS3
-rw-r--r--core/java/android/os/incremental/V4Signature.java10
-rw-r--r--core/java/android/os/storage/StorageManager.java14
-rw-r--r--core/java/android/util/FeatureFlagUtils.java1
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java26
-rw-r--r--core/java/android/view/SurfaceView.java143
-rw-r--r--core/java/android/view/ViewRootImpl.java11
-rw-r--r--core/java/android/view/inputmethod/OWNERS3
-rw-r--r--core/java/android/webkit/UserPackage.java2
-rw-r--r--core/java/android/webkit/WebViewFactory.java2
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java57
-rw-r--r--core/java/com/android/internal/inputmethod/OWNERS3
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java29
-rw-r--r--core/java/com/android/internal/os/Zygote.java19
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java4
-rw-r--r--core/jni/AndroidRuntime.cpp10
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp12
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/layout/resolver_empty_states.xml4
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/attrs_manifest.xml9
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java152
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java41
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java239
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java32
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--graphics/java/android/graphics/ColorSpace.java3
-rw-r--r--graphics/java/android/graphics/ParcelableColorSpace.java183
-rw-r--r--libs/hwui/Readback.cpp47
-rw-r--r--libs/hwui/Readback.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp3
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp3
-rw-r--r--libs/hwui/renderthread/RenderProxy.h4
-rw-r--r--media/java/android/media/MediaRouter2.java22
-rw-r--r--media/java/android/media/MediaRouter2Manager.java18
-rw-r--r--media/java/android/media/MediaScannerConnection.java25
-rw-r--r--media/java/android/media/RouteDiscoveryPreference.java64
-rw-r--r--media/java/android/media/RoutingSessionInfo.java48
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java17
-rw-r--r--media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java6
-rw-r--r--media/jni/android_media_tv_Tuner.cpp138
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java (renamed from media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java)4
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java4
-rw-r--r--media/tests/TunerTest/Android.bp18
-rw-r--r--media/tests/TunerTest/AndroidManifest.xml29
-rw-r--r--media/tests/TunerTest/AndroidTest.xml17
-rw-r--r--media/tests/TunerTest/res/values/strings.xml5
-rw-r--r--media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java61
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml66
-rw-r--r--packages/SettingsLib/LayoutPreference/res/values/styles.xml31
-rw-r--r--packages/SettingsLib/SchedulesProvider/Android.bp12
-rw-r--r--packages/SettingsLib/SchedulesProvider/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java167
-rw-r--r--packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java133
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java59
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java42
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java58
-rw-r--r--packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java11
-rw-r--r--packages/SettingsProvider/res/values/strings.xml9
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java2
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java12
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java26
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java71
-rw-r--r--services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java126
-rw-r--r--services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java74
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java19
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java4
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java7
-rw-r--r--services/core/java/com/android/server/inputmethod/OWNERS6
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java4
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java31
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java34
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java23
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java398
-rw-r--r--services/core/java/com/android/server/people/PeopleServiceInternal.java13
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java58
-rw-r--r--services/core/java/com/android/server/pm/Settings.java1
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java4
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java63
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java75
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java371
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskChangeNotificationController.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java8
-rw-r--r--services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp434
-rw-r--r--services/java/com/android/server/SystemServer.java2
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java26
-rw-r--r--services/people/java/com/android/server/people/data/ConversationStore.java6
-rw-r--r--services/people/java/com/android/server/people/data/DataMaintenanceService.java92
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java92
-rw-r--r--services/people/java/com/android/server/people/data/EventHistoryImpl.java11
-rw-r--r--services/people/java/com/android/server/people/data/EventList.java4
-rw-r--r--services/people/java/com/android/server/people/data/EventStore.java150
-rw-r--r--services/people/java/com/android/server/people/data/PackageData.java72
-rw-r--r--services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java14
-rw-r--r--services/people/java/com/android/server/people/data/UserData.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java93
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java19
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java104
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java31
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java37
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java214
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java14
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java54
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java24
-rw-r--r--startop/iorap/functional_tests/AndroidTest.xml9
-rw-r--r--startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java8
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java4
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java4
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java1
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java2
-rw-r--r--wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java (renamed from wifi/java/android/net/wifi/wificond/WifiCondManager.java)19
-rw-r--r--wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java (renamed from wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java)62
180 files changed, 4890 insertions, 1406 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..5e8974fb82d7 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();
}
@@ -7238,7 +7242,7 @@ package android.app.admin {
public final class FactoryResetProtectionPolicy implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getFactoryResetProtectionAccounts();
- method public boolean isFactoryResetProtectionDisabled();
+ method public boolean isFactoryResetProtectionEnabled();
method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FactoryResetProtectionPolicy> CREATOR;
}
@@ -7247,7 +7251,7 @@ package android.app.admin {
ctor public FactoryResetProtectionPolicy.Builder();
method @NonNull public android.app.admin.FactoryResetProtectionPolicy build();
method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionAccounts(@NonNull java.util.List<java.lang.String>);
- method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionDisabled(boolean);
+ method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionEnabled(boolean);
}
public class FreezePeriod {
@@ -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";
@@ -27041,7 +27044,7 @@ package android.media {
method @NonNull public String getId();
method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectableRoutes();
method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectedRoutes();
- method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferrableRoutes();
+ method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferableRoutes();
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
@@ -27421,7 +27424,7 @@ package android.media {
public final class RouteDiscoveryPreference implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getPreferredFeatures();
- method public boolean isActiveScan();
+ method public boolean shouldPerformActiveScan();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR;
}
@@ -27430,8 +27433,8 @@ package android.media {
ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean);
ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
method @NonNull public android.media.RouteDiscoveryPreference build();
- method @NonNull public android.media.RouteDiscoveryPreference.Builder setActiveScan(boolean);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
}
public final class RoutingSessionInfo implements android.os.Parcelable {
@@ -27442,7 +27445,7 @@ package android.media {
method @NonNull public String getId();
method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
- method @NonNull public java.util.List<java.lang.String> getTransferrableRoutes();
+ method @NonNull public java.util.List<java.lang.String> getTransferableRoutes();
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
@@ -27456,16 +27459,16 @@ package android.media {
method @NonNull public android.media.RoutingSessionInfo.Builder addDeselectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder addSelectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder addSelectedRoute(@NonNull String);
- method @NonNull public android.media.RoutingSessionInfo.Builder addTransferrableRoute(@NonNull String);
+ method @NonNull public android.media.RoutingSessionInfo.Builder addTransferableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo build();
method @NonNull public android.media.RoutingSessionInfo.Builder clearDeselectableRoutes();
method @NonNull public android.media.RoutingSessionInfo.Builder clearSelectableRoutes();
method @NonNull public android.media.RoutingSessionInfo.Builder clearSelectedRoutes();
- method @NonNull public android.media.RoutingSessionInfo.Builder clearTransferrableRoutes();
+ method @NonNull public android.media.RoutingSessionInfo.Builder clearTransferableRoutes();
method @NonNull public android.media.RoutingSessionInfo.Builder removeDeselectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectedRoute(@NonNull String);
- method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferrableRoute(@NonNull String);
+ method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
@@ -37311,6 +37314,7 @@ package android.os.storage {
method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
method public boolean unmountObb(String, boolean, android.os.storage.OnObbStateChangeListener);
method public void unregisterStorageVolumeCallback(@NonNull android.os.storage.StorageManager.StorageVolumeCallback);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
field public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
field public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
field public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
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/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 82c7635fa5e3..c1e21959a667 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -551,11 +551,6 @@ public class AccessibilityServiceInfo implements Parcelable {
private int mHtmlDescriptionRes;
/**
- * Non localized html description of the accessibility service.
- */
- private String mNonLocalizedHtmlDescription;
-
- /**
* Creates a new instance.
*/
public AccessibilityServiceInfo() {
@@ -683,10 +678,6 @@ public class AccessibilityServiceInfo implements Parcelable {
com.android.internal.R.styleable.AccessibilityService_htmlDescription);
if (peekedValue != null) {
mHtmlDescriptionRes = peekedValue.resourceId;
- final CharSequence nonLocalizedHtmlDescription = peekedValue.coerceToString();
- if (nonLocalizedHtmlDescription != null) {
- mNonLocalizedHtmlDescription = nonLocalizedHtmlDescription.toString().trim();
- }
}
asAttributes.recycle();
} catch (NameNotFoundException e) {
@@ -919,7 +910,7 @@ public class AccessibilityServiceInfo implements Parcelable {
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
if (mHtmlDescriptionRes == 0) {
- return mNonLocalizedHtmlDescription;
+ return null;
}
final ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
@@ -1017,7 +1008,6 @@ public class AccessibilityServiceInfo implements Parcelable {
parcel.writeInt(mAnimatedImageRes);
parcel.writeInt(mHtmlDescriptionRes);
parcel.writeString(mNonLocalizedDescription);
- parcel.writeString(mNonLocalizedHtmlDescription);
}
private void initFromParcel(Parcel parcel) {
@@ -1039,7 +1029,6 @@ public class AccessibilityServiceInfo implements Parcelable {
mAnimatedImageRes = parcel.readInt();
mHtmlDescriptionRes = parcel.readInt();
mNonLocalizedDescription = parcel.readString();
- mNonLocalizedHtmlDescription = parcel.readString();
}
@Override
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/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 06288c0794b0..37bdda0b0393 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -204,4 +204,12 @@ oneway interface ITaskStackListener {
* @param frozen if true, Recents Tasks list is currently frozen, false otherwise
*/
void onRecentTaskListFrozenChanged(boolean frozen);
+
+ /**
+ * Called when a task gets or loses focus.
+ *
+ * @param taskId id of the task.
+ * @param {@code true} if the task got focus, {@code false} if it lost it.
+ */
+ void onTaskFocusChanged(int taskId, boolean focused);
}
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/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 343b3869d354..da0aadb3ea48 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
}
+
+ @Override
+ public void onTaskFocusChanged(int taskId, boolean focused) {
+ }
}
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index ed7477936f9c..954db0459f99 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -53,17 +53,17 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT =
"factory_reset_protection_account";
- private static final String KEY_FACTORY_RESET_PROTECTION_DISABLED =
- "factory_reset_protection_disabled";
+ private static final String KEY_FACTORY_RESET_PROTECTION_ENABLED =
+ "factory_reset_protection_enabled";
private static final String ATTR_VALUE = "value";
private final List<String> mFactoryResetProtectionAccounts;
- private final boolean mFactoryResetProtectionDisabled;
+ private final boolean mFactoryResetProtectionEnabled;
private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts,
- boolean factoryResetProtectionDisabled) {
+ boolean factoryResetProtectionEnabled) {
mFactoryResetProtectionAccounts = factoryResetProtectionAccounts;
- mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+ mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
}
/**
@@ -74,10 +74,10 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
}
/**
- * Return whether factory reset protection for the device is disabled or not.
+ * Return whether factory reset protection for the device is enabled or not.
*/
- public boolean isFactoryResetProtectionDisabled() {
- return mFactoryResetProtectionDisabled;
+ public boolean isFactoryResetProtectionEnabled() {
+ return mFactoryResetProtectionEnabled;
}
/**
@@ -85,12 +85,13 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
*/
public static class Builder {
private List<String> mFactoryResetProtectionAccounts;
- private boolean mFactoryResetProtectionDisabled;
+ private boolean mFactoryResetProtectionEnabled;
/**
* Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}.
*/
public Builder() {
+ mFactoryResetProtectionEnabled = true;
};
/**
@@ -113,18 +114,19 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
}
/**
- * Sets whether factory reset protection is disabled or not.
+ * Sets whether factory reset protection is enabled or not.
* <p>
* Once disabled, factory reset protection will not kick in all together when the device
* goes through untrusted factory reset. This applies to both the consumer unlock flow and
- * the admin account overrides via {@link #setFactoryResetProtectionAccounts}
+ * the admin account overrides via {@link #setFactoryResetProtectionAccounts}. By default,
+ * factory reset protection is enabled.
*
- * @param factoryResetProtectionDisabled Whether the policy is disabled or not.
+ * @param factoryResetProtectionEnabled Whether the policy is enabled or not.
* @return the same Builder instance.
*/
@NonNull
- public Builder setFactoryResetProtectionDisabled(boolean factoryResetProtectionDisabled) {
- mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+ public Builder setFactoryResetProtectionEnabled(boolean factoryResetProtectionEnabled) {
+ mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
return this;
}
@@ -136,7 +138,7 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
@NonNull
public FactoryResetProtectionPolicy build() {
return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
- mFactoryResetProtectionDisabled);
+ mFactoryResetProtectionEnabled);
}
}
@@ -144,7 +146,7 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
public String toString() {
return "FactoryResetProtectionPolicy{"
+ "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
- + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled
+ + ", mFactoryResetProtectionEnabled=" + mFactoryResetProtectionEnabled
+ '}';
}
@@ -155,7 +157,7 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
for (String account: mFactoryResetProtectionAccounts) {
dest.writeString(account);
}
- dest.writeBoolean(mFactoryResetProtectionDisabled);
+ dest.writeBoolean(mFactoryResetProtectionEnabled);
}
@Override
@@ -173,10 +175,10 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
for (int i = 0; i < accountsCount; i++) {
factoryResetProtectionAccounts.add(in.readString());
}
- boolean factoryResetProtectionDisabled = in.readBoolean();
+ boolean factoryResetProtectionEnabled = in.readBoolean();
return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
- factoryResetProtectionDisabled);
+ factoryResetProtectionEnabled);
}
@Override
@@ -195,8 +197,8 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
@Nullable
public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) {
try {
- boolean factoryResetProtectionDisabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_DISABLED));
+ boolean factoryResetProtectionEnabled = Boolean.parseBoolean(
+ parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_ENABLED));
List<String> factoryResetProtectionAccounts = new ArrayList<>();
int outerDepth = parser.getDepth();
@@ -214,7 +216,7 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
}
return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
- factoryResetProtectionDisabled);
+ factoryResetProtectionEnabled);
} catch (XmlPullParserException | IOException e) {
Log.w(LOG_TAG, "Reading from xml failed", e);
}
@@ -225,8 +227,8 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
* @hide
*/
public void writeToXml(@NonNull XmlSerializer out) throws IOException {
- out.attribute(null, KEY_FACTORY_RESET_PROTECTION_DISABLED,
- Boolean.toString(mFactoryResetProtectionDisabled));
+ out.attribute(null, KEY_FACTORY_RESET_PROTECTION_ENABLED,
+ Boolean.toString(mFactoryResetProtectionEnabled));
for (String account : mFactoryResetProtectionAccounts) {
out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
out.attribute(null, ATTR_VALUE, account);
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/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bb1dafc50484..8d04df0560f5 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -215,6 +215,20 @@ public class StorageManager {
public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
/**
+ * Activity Action: Allows the user to free up space by clearing app external cache directories.
+ * The intent doesn't automatically clear cache, but shows a dialog and lets the user decide.
+ * <p>
+ * This intent should be launched using
+ * {@link Activity#startActivityForResult(Intent, int)} so that the user
+ * knows which app is requesting to clear cache. The returned result will
+ * be {@link Activity#RESULT_OK} if the activity was launched and the user accepted to clear
+ * cache, or {@link Activity#RESULT_CANCELED} otherwise.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
+
+ /**
* Extra {@link UUID} used to indicate the storage volume where an
* application is interested in allocating or managing disk space.
*
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/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/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/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 78e85180f3d2..efcd54f623ee 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -61,6 +61,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
private Set<Integer> mLoadedPages;
private final UserHandle mPersonalProfileUserHandle;
private final UserHandle mWorkProfileUserHandle;
+ private Injector mInjector;
AbstractMultiProfilePagerAdapter(Context context, int currentPage,
UserHandle personalProfileUserHandle,
@@ -70,6 +71,33 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
mLoadedPages = new HashSet<>();
mPersonalProfileUserHandle = personalProfileUserHandle;
mWorkProfileUserHandle = workProfileUserHandle;
+ UserManager userManager = context.getSystemService(UserManager.class);
+ mInjector = new Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return AbstractMultiProfilePagerAdapter.this
+ .hasCrossProfileIntents(intents, sourceUserId, targetUserId);
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return userManager.isQuietModeEnabled(workProfileUserHandle);
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
+ userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ }
+ };
+ }
+
+ /**
+ * Overrides the default {@link Injector} for testing purposes.
+ */
+ @VisibleForTesting
+ public void setInjector(Injector injector) {
+ mInjector = injector;
}
void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
@@ -252,19 +280,18 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
- UserManager userManager = mContext.getSystemService(UserManager.class);
if (listUserHandle == mWorkProfileUserHandle
- && userManager.isQuietModeEnabled(mWorkProfileUserHandle)) {
+ && mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
R.string.resolver_turn_on_work_apps,
R.string.resolver_turn_on_work_apps_explanation,
(View.OnClickListener) v ->
- userManager.requestQuietModeEnabled(false, mWorkProfileUserHandle));
+ mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle));
return false;
}
if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
- if (!hasCrossProfileIntents(activeListAdapter.getIntents(),
+ if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
if (listUserHandle == mPersonalProfileUserHandle) {
showEmptyState(activeListAdapter,
@@ -366,4 +393,26 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
*/
void onProfileSelected(int profileIndex);
}
+
+ /**
+ * Describes an injector to be used for cross profile functionality. Overridable for testing.
+ */
+ @VisibleForTesting
+ public interface Injector {
+ /**
+ * Returns {@code true} if at least one of the provided {@code intents} can be forwarded
+ * from {@code sourceUserId} to {@code targetUserId}.
+ */
+ boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId, int targetUserId);
+
+ /**
+ * Returns whether the given profile is in quiet mode or not.
+ */
+ boolean isQuietModeEnabled(UserHandle workProfileUserHandle);
+
+ /**
+ * Enables or disables quiet mode for a managed profile.
+ */
+ void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle);
+ }
} \ No newline at end of file
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 1414cd645092..bbc23fec87ea 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -241,14 +241,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.
*/
@@ -260,8 +252,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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60b3a19c4cf3..9dc40c80f18b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5344,6 +5344,10 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.people.data.DataMaintenanceService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
<service
android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 6959e9c0f2bb..5ed4c5301011 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -34,7 +34,7 @@
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textColor="@color/resolver_empty_state_text"
android:textSize="18sp"/>
<TextView
@@ -52,7 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textSize="14sp"
android:textColor="@color/resolver_tabs_active_color"/>
</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7d8b8db9d4a0..7fc041c8dbd4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3774,7 +3774,7 @@
<attr name="animatedImageDrawable" format="reference"/>
<!-- Html description of the accessibility service, to help users understand
how the service can help them.-->
- <attr name="htmlDescription" format="string"/>
+ <attr name="htmlDescription" format="reference"/>
<!-- Short description of the accessibility service purpose or behavior.-->
<attr name="description" />
@@ -3795,7 +3795,7 @@
<attr name="animatedImageDrawable" format="reference"/>
<!-- Html description of the target of accessibility shortcut purpose or behavior, to help
users understand how the target of accessibility shortcut can help them. -->
- <attr name="htmlDescription" format="string"/>
+ <attr name="htmlDescription" format="reference"/>
</declare-styleable>
<!-- Use <code>print-service</code> as the root tag of the XML resource that
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/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index ce71bebfc455..6d0e58bc89be 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
@@ -355,18 +356,14 @@ public class ChooserActivityTest {
public void hasOtherProfileOneOption() throws Exception {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
-
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
markWorkProfileUserAvailable();
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
+ Intent sendIntent = createSendTextIntent();
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
@@ -382,9 +379,11 @@ public class ChooserActivityTest {
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2);
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
waitForIdle();
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+ onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
.perform(click());
waitForIdle();
assertThat(chosen[0], is(toChoose));
@@ -1218,17 +1217,7 @@ public class ChooserActivityTest {
int workProfileTargets = 4;
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
workProfileTargets);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
markWorkProfileUserAvailable();
@@ -1245,7 +1234,7 @@ public class ChooserActivityTest {
}
@Test
- public void testWorkTab_workProfileHasExpectedNumberOfTargets() {
+ public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
@@ -1254,18 +1243,7 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
@@ -1284,12 +1262,12 @@ public class ChooserActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
int workProfileTargets = 4;
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
ResolveInfo[] chosen = new ResolveInfo[1];
@@ -1312,6 +1290,85 @@ public class ChooserActivityTest {
assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
}
+ @Test
+ public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.hasCrossProfileIntents = false;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+
+ onView(withText(R.string.resolver_cant_share_with_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_workProfileDisabled_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.isQuietModeEnabled = true;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_turn_on_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_noWorkTargets_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(0);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_no_apps_available))
+ .check(matches(isDisplayed()));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -1486,4 +1543,21 @@ public class ChooserActivityTest {
private void markWorkProfileUserAvailable() {
sOverrides.workProfileUserHandle = UserHandle.of(10);
}
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos,
+ List<ResolvedComponentInfo> workResolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index a68b59086d42..363551bc92fc 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,15 +16,10 @@
package com.android.internal.app;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
import android.app.usage.UsageStatsManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -35,7 +30,6 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Bundle;
import android.os.UserHandle;
import android.util.Size;
@@ -45,8 +39,7 @@ import com.android.internal.app.chooser.TargetInfo;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import org.mockito.Mockito;
-
+import java.util.List;
import java.util.function.Function;
public class ChooserWrapperActivity extends ChooserActivity {
@@ -56,6 +49,15 @@ public class ChooserWrapperActivity extends ChooserActivity {
static final OverrideData sOverrides = new OverrideData();
private UsageStatsManager mUsm;
+ @Override
+ protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+ Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
+ AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
+ super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
+ multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
+ return multiProfilePagerAdapter;
+ }
+
ChooserListAdapter getAdapter() {
return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
}
@@ -206,6 +208,9 @@ public class ChooserWrapperActivity extends ChooserActivity {
public int alternateProfileSetting;
public Resources resources;
public UserHandle workProfileUserHandle;
+ public boolean hasCrossProfileIntents;
+ public boolean isQuietModeEnabled;
+ public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
public void reset() {
onSafelyStartCallback = null;
@@ -221,6 +226,26 @@ public class ChooserWrapperActivity extends ChooserActivity {
alternateProfileSetting = 0;
resources = null;
workProfileUserHandle = null;
+ hasCrossProfileIntents = true;
+ isQuietModeEnabled = false;
+ multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return hasCrossProfileIntents;
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return isQuietModeEnabled;
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled,
+ UserHandle workProfileUserHandle) {
+ isQuietModeEnabled = enabled;
+ }
+ };
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 5f4194aa51e3..a7bf48858e42 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -18,16 +18,16 @@ package com.android.internal.app;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.internal.app.MatcherUtils.first;
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
-import static com.android.internal.app.ResolverDataProvider.createResolvedComponentInfoWithOtherId;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
import static org.hamcrest.CoreMatchers.allOf;
@@ -56,9 +56,6 @@ import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGett
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import com.android.internal.widget.ResolverDrawerLayout;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -224,17 +221,14 @@ public class ResolverActivityTest {
public void hasOtherProfileOneOption() throws Exception {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
-
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
markWorkProfileUserAvailable();
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
+ Intent sendIntent = createSendImageIntent();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource());
waitForIdle();
@@ -249,8 +243,9 @@ public class ResolverActivityTest {
};
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2);
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
+ // We pick the first one as there is another one in the work profile side
+ onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
.perform(click());
onView(withId(R.id.button_once))
.perform(click());
@@ -415,18 +410,14 @@ public class ResolverActivityTest {
}
@Test
- public void testWorkTab_workTabListEmptyBeforeGoingToTab() {
+ public void testWorkTab_workTabListPopulatedBeforeGoingToTab() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ setupResolverControllers(personalResolvedComponentInfos,
+ new ArrayList<>(workResolvedComponentInfos));
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -434,8 +425,8 @@ public class ResolverActivityTest {
waitForIdle();
assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- // The work list adapter must only be filled when we open the work tab
- assertThat(activity.getWorkListAdapter().getCount(), is(0));
+ // The work list adapter must be populated in advance before tapping the other tab
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
}
@Test
@@ -445,17 +436,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -474,34 +455,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -521,18 +475,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -552,18 +495,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -597,35 +529,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(1);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -644,10 +548,10 @@ public class ResolverActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -672,6 +576,82 @@ public class ResolverActivityTest {
assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
}
+ @Test
+ public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.hasCrossProfileIntents = false;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+
+ onView(withText(R.string.resolver_cant_share_with_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_workProfileDisabled_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.isQuietModeEnabled = true;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_turn_on_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_noWorkTargets_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(0);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_no_apps_available))
+ .check(matches(isDisplayed()));
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -722,4 +702,21 @@ public class ResolverActivityTest {
private void markWorkProfileUserAvailable() {
ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10);
}
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos,
+ List<ResolvedComponentInfo> workResolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 36c8724e522e..208710498e1c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -47,6 +47,15 @@ public class ResolverWrapperActivity extends ResolverActivity {
filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this);
}
+ @Override
+ protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+ Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
+ AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
+ super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
+ multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
+ return multiProfilePagerAdapter;
+ }
+
ResolverWrapperAdapter getAdapter() {
return (ResolverWrapperAdapter) mMultiProfilePagerAdapter.getActiveListAdapter();
}
@@ -124,6 +133,9 @@ public class ResolverWrapperActivity extends ResolverActivity {
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
public UserHandle workProfileUserHandle;
+ public boolean hasCrossProfileIntents;
+ public boolean isQuietModeEnabled;
+ public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
public void reset() {
onSafelyStartCallback = null;
@@ -132,6 +144,26 @@ public class ResolverWrapperActivity extends ResolverActivity {
resolverListController = mock(ResolverListController.class);
workResolverListController = mock(ResolverListController.class);
workProfileUserHandle = null;
+ hasCrossProfileIntents = true;
+ isQuietModeEnabled = false;
+ multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return hasCrossProfileIntents;
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return isQuietModeEnabled;
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled,
+ UserHandle workProfileUserHandle) {
+ isQuietModeEnabled = enabled;
+ }
+ };
}
}
} \ No newline at end of file
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/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 91f9447a3d59..1df3336bb5e5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -204,8 +204,7 @@ void CanvasContext::setStopped(bool stopped) {
void CanvasContext::allocateBuffers() {
if (mNativeSurface) {
- ANativeWindow* anw = mNativeSurface->getNativeWindow();
- ANativeWindow_allocateBuffers(anw);
+ ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
}
}
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/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 0e6ade539e35..2178393cdd5a 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -875,12 +875,12 @@ public class MediaRouter2 {
}
/**
- * @return the unmodifiable list of transferrable routes for the session.
+ * @return the unmodifiable list of transferable routes for the session.
*/
@NonNull
- public List<MediaRoute2Info> getTransferrableRoutes() {
+ public List<MediaRoute2Info> getTransferableRoutes() {
synchronized (mControllerLock) {
- return getRoutesWithIdsLocked(mSessionInfo.getTransferrableRoutes());
+ return getRoutesWithIdsLocked(mSessionInfo.getTransferableRoutes());
}
}
@@ -1033,12 +1033,12 @@ public class MediaRouter2 {
* all of the following conditions:
* <ul>
* <li>ID should not be included in {@link #getSelectedRoutes()}</li>
- * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+ * <li>ID should be included in {@link #getTransferableRoutes()}</li>
* </ul>
* If the route doesn't meet any of above conditions, it will be ignored.
*
* @see #getSelectedRoutes()
- * @see #getTransferrableRoutes()
+ * @see #getTransferableRoutes()
* @see RoutingControllerCallback#onControllerUpdated
*/
public void transferToRoute(@NonNull MediaRoute2Info route) {
@@ -1057,9 +1057,9 @@ public class MediaRouter2 {
return;
}
- List<MediaRoute2Info> transferrableRoutes = getTransferrableRoutes();
- if (!checkRouteListContainsRouteId(transferrableRoutes, route.getId())) {
- Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+ List<MediaRoute2Info> transferableRoutes = getTransferableRoutes();
+ if (!checkRouteListContainsRouteId(transferableRoutes, route.getId())) {
+ Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
return;
}
@@ -1156,7 +1156,7 @@ public class MediaRouter2 {
.map(MediaRoute2Info::getId).collect(Collectors.toList());
List<String> deselectableRoutes = getDeselectableRoutes().stream()
.map(MediaRoute2Info::getId).collect(Collectors.toList());
- List<String> transferrableRoutes = getTransferrableRoutes().stream()
+ List<String> transferableRoutes = getTransferableRoutes().stream()
.map(MediaRoute2Info::getId).collect(Collectors.toList());
StringBuilder result = new StringBuilder()
@@ -1171,8 +1171,8 @@ public class MediaRouter2 {
.append(", deselectableRoutes={")
.append(deselectableRoutes)
.append("}")
- .append(", transferrableRoutes={")
- .append(transferrableRoutes)
+ .append(", transferableRoutes={")
+ .append(transferableRoutes)
.append("}")
.append(" }");
return result.toString();
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5ce291c06ade..4801d44bdb10 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -259,7 +259,7 @@ public class MediaRouter2Manager {
/**
* Selects media route for the specified package name.
*
- * If the given route is {@link RoutingController#getTransferrableRoutes() a transferrable
+ * If the given route is {@link RoutingController#getTransferableRoutes() a transferable
* route} of a routing session of the application, the session will be transferred to
* the route. If not, a new routing session will be created.
*
@@ -274,7 +274,7 @@ public class MediaRouter2Manager {
//TODO: instead of release all controllers, add an API to specify controllers that
// should be released (or is the system controller).
for (RoutingSessionInfo sessionInfo : getRoutingSessions(packageName)) {
- if (!transferred && sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+ if (!transferred && sessionInfo.getTransferableRoutes().contains(route.getId())) {
new RoutingController(sessionInfo).transferToRoute(route);
transferred = true;
} else if (!sessionInfo.isSystemSession()) {
@@ -543,13 +543,13 @@ public class MediaRouter2Manager {
}
/**
- * @return the unmodifiable list of transferrable routes for the session.
+ * @return the unmodifiable list of transferable routes for the session.
*/
@NonNull
- public List<MediaRoute2Info> getTransferrableRoutes() {
+ public List<MediaRoute2Info> getTransferableRoutes() {
List<String> routeIds;
synchronized (mControllerLock) {
- routeIds = mSessionInfo.getTransferrableRoutes();
+ routeIds = mSessionInfo.getTransferableRoutes();
}
return getRoutesWithIds(routeIds);
}
@@ -643,12 +643,12 @@ public class MediaRouter2Manager {
* all of the following conditions:
* <ul>
* <li>ID should not be included in {@link #getSelectedRoutes()}</li>
- * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+ * <li>ID should be included in {@link #getTransferableRoutes()}</li>
* </ul>
* If the route doesn't meet any of above conditions, it will be ignored.
*
* @see #getSelectedRoutes()
- * @see #getTransferrableRoutes()
+ * @see #getTransferableRoutes()
*/
public void transferToRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
@@ -663,8 +663,8 @@ public class MediaRouter2Manager {
return;
}
- if (!sessionInfo.getTransferrableRoutes().contains(route.getId())) {
- Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+ if (!sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
return;
}
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/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index ebcb9ed342e5..2e038e665520 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -31,8 +31,16 @@ import java.util.Objects;
import java.util.Set;
/**
- * A media route discovery preference describing the kinds of routes that media router
+ * A media route discovery preference describing the features of routes that media router
* would like to discover and whether to perform active scanning.
+ * <p>
+ * When {@link MediaRouter2} instances set discovery preferences by calling
+ * {@link MediaRouter2#registerRouteCallback}, they are merged into a single discovery preference
+ * and it is delivered to call {@link MediaRoute2ProviderService#onDiscoveryPreferenceChanged}.
+ * </p><p>
+ * According to the given discovery preference, {@link MediaRoute2ProviderService} discovers
+ * routes and publishes them.
+ * </p>
*
* @see MediaRouter2#registerRouteCallback
*/
@@ -53,12 +61,12 @@ public final class RouteDiscoveryPreference implements Parcelable {
@NonNull
private final List<String> mPreferredFeatures;
- private final boolean mActiveScan;
+ private final boolean mShouldPerformActiveScan;
@Nullable
private final Bundle mExtras;
/**
- * An empty discovery preference.
+ * An empty discovery preference
* @hide
*/
public static final RouteDiscoveryPreference EMPTY =
@@ -66,23 +74,39 @@ public final class RouteDiscoveryPreference implements Parcelable {
RouteDiscoveryPreference(@NonNull Builder builder) {
mPreferredFeatures = builder.mPreferredFeatures;
- mActiveScan = builder.mActiveScan;
+ mShouldPerformActiveScan = builder.mActiveScan;
mExtras = builder.mExtras;
}
RouteDiscoveryPreference(@NonNull Parcel in) {
mPreferredFeatures = in.createStringArrayList();
- mActiveScan = in.readBoolean();
+ mShouldPerformActiveScan = in.readBoolean();
mExtras = in.readBundle();
}
+ /**
+ * Gets the features of routes that media router would like to discover.
+ * <p>
+ * Routes that have at least one of the features will be discovered.
+ * They may include predefined features such as
+ * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+ * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
+ * </p>
+ */
@NonNull
public List<String> getPreferredFeatures() {
return mPreferredFeatures;
}
- public boolean isActiveScan() {
- return mActiveScan;
+ /**
+ * Gets whether active scanning should be performed.
+ * <p>
+ * If any of discovery preferences sets this as {@code true}, active scanning will
+ * be performed regardless of other discovery preferences.
+ * </p>
+ */
+ public boolean shouldPerformActiveScan() {
+ return mShouldPerformActiveScan;
}
/**
@@ -100,7 +124,7 @@ public final class RouteDiscoveryPreference implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mPreferredFeatures);
- dest.writeBoolean(mActiveScan);
+ dest.writeBoolean(mShouldPerformActiveScan);
dest.writeBundle(mExtras);
}
@@ -112,7 +136,7 @@ public final class RouteDiscoveryPreference implements Parcelable {
.append(String.join(", ", mPreferredFeatures))
.append("}")
.append(", activeScan=")
- .append(mActiveScan)
+ .append(mShouldPerformActiveScan)
.append(" }");
return result.toString();
@@ -128,12 +152,12 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
- && mActiveScan == other.mActiveScan;
+ && mShouldPerformActiveScan == other.mShouldPerformActiveScan;
}
@Override
public int hashCode() {
- return Objects.hash(mPreferredFeatures, mActiveScan);
+ return Objects.hash(mPreferredFeatures, mShouldPerformActiveScan);
}
/**
@@ -154,12 +178,12 @@ public final class RouteDiscoveryPreference implements Parcelable {
Objects.requireNonNull(preference, "preference must not be null");
mPreferredFeatures = preference.getPreferredFeatures();
- mActiveScan = preference.isActiveScan();
+ mActiveScan = preference.shouldPerformActiveScan();
mExtras = preference.getExtras();
}
/**
- * A constructor to combine all of the preferences into a single preference .
+ * A constructor to combine all of the preferences into a single preference.
* It ignores extras of preferences.
*
* @hide
@@ -171,13 +195,19 @@ public final class RouteDiscoveryPreference implements Parcelable {
mActiveScan = false;
for (RouteDiscoveryPreference preference : preferences) {
routeFeatureSet.addAll(preference.mPreferredFeatures);
- mActiveScan |= preference.mActiveScan;
+ mActiveScan |= preference.mShouldPerformActiveScan;
}
mPreferredFeatures = new ArrayList<>(routeFeatureSet);
}
/**
* Sets preferred route features to discover.
+ * @param preferredFeatures features of routes that media router would like to discover.
+ * May include predefined features
+ * such as {@link MediaRoute2Info#FEATURE_LIVE_AUDIO},
+ * {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+ * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK}
+ * or custom features defined by a provider.
*/
@NonNull
public Builder setPreferredFeatures(@NonNull List<String> preferredFeatures) {
@@ -188,9 +218,13 @@ public final class RouteDiscoveryPreference implements Parcelable {
/**
* Sets if active scanning should be performed.
+ * <p>
+ * Since active scanning uses more system resources, set this as {@code true} only
+ * when it's necessary.
+ * </p>
*/
@NonNull
- public Builder setActiveScan(boolean activeScan) {
+ public Builder setShouldPerformActiveScan(boolean activeScan) {
mActiveScan = activeScan;
return this;
}
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 0d4e666de51b..19a46ce6570d 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -55,7 +55,7 @@ public final class RoutingSessionInfo implements Parcelable {
final List<String> mSelectedRoutes;
final List<String> mSelectableRoutes;
final List<String> mDeselectableRoutes;
- final List<String> mTransferrableRoutes;
+ final List<String> mTransferableRoutes;
final int mVolumeHandling;
final int mVolumeMax;
@@ -79,8 +79,8 @@ public final class RoutingSessionInfo implements Parcelable {
convertToUniqueRouteIds(builder.mSelectableRoutes));
mDeselectableRoutes = Collections.unmodifiableList(
convertToUniqueRouteIds(builder.mDeselectableRoutes));
- mTransferrableRoutes = Collections.unmodifiableList(
- convertToUniqueRouteIds(builder.mTransferrableRoutes));
+ mTransferableRoutes = Collections.unmodifiableList(
+ convertToUniqueRouteIds(builder.mTransferableRoutes));
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
@@ -100,7 +100,7 @@ public final class RoutingSessionInfo implements Parcelable {
mSelectedRoutes = ensureList(src.createStringArrayList());
mSelectableRoutes = ensureList(src.createStringArrayList());
mDeselectableRoutes = ensureList(src.createStringArrayList());
- mTransferrableRoutes = ensureList(src.createStringArrayList());
+ mTransferableRoutes = ensureList(src.createStringArrayList());
mVolumeHandling = src.readInt();
mVolumeMax = src.readInt();
@@ -193,11 +193,11 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
- * Gets the list of ids of transferrable routes for the session.
+ * Gets the list of ids of transferable routes for the session.
*/
@NonNull
- public List<String> getTransferrableRoutes() {
- return mTransferrableRoutes;
+ public List<String> getTransferableRoutes() {
+ return mTransferableRoutes;
}
/**
@@ -260,7 +260,7 @@ public final class RoutingSessionInfo implements Parcelable {
dest.writeStringList(mSelectedRoutes);
dest.writeStringList(mSelectableRoutes);
dest.writeStringList(mDeselectableRoutes);
- dest.writeStringList(mTransferrableRoutes);
+ dest.writeStringList(mTransferableRoutes);
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
@@ -284,7 +284,7 @@ public final class RoutingSessionInfo implements Parcelable {
&& Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
&& Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
&& Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes)
- && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes)
+ && Objects.equals(mTransferableRoutes, other.mTransferableRoutes)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume);
@@ -293,7 +293,7 @@ public final class RoutingSessionInfo implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mId, mClientPackageName, mProviderId,
- mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes,
+ mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
mVolumeMax, mVolumeHandling, mVolume);
}
@@ -311,8 +311,8 @@ public final class RoutingSessionInfo implements Parcelable {
.append(", deselectableRoutes={")
.append(String.join(",", mDeselectableRoutes))
.append("}")
- .append(", transferrableRoutes={")
- .append(String.join(",", mTransferrableRoutes))
+ .append(", transferableRoutes={")
+ .append(String.join(",", mTransferableRoutes))
.append("}")
.append(", volumeHandling=").append(getVolumeHandling())
.append(", volumeMax=").append(getVolumeMax())
@@ -350,7 +350,7 @@ public final class RoutingSessionInfo implements Parcelable {
final List<String> mSelectedRoutes;
final List<String> mSelectableRoutes;
final List<String> mDeselectableRoutes;
- final List<String> mTransferrableRoutes;
+ final List<String> mTransferableRoutes;
int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
@@ -381,7 +381,7 @@ public final class RoutingSessionInfo implements Parcelable {
mSelectedRoutes = new ArrayList<>();
mSelectableRoutes = new ArrayList<>();
mDeselectableRoutes = new ArrayList<>();
- mTransferrableRoutes = new ArrayList<>();
+ mTransferableRoutes = new ArrayList<>();
}
/**
@@ -400,7 +400,7 @@ public final class RoutingSessionInfo implements Parcelable {
mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
mSelectableRoutes = new ArrayList<>(sessionInfo.mSelectableRoutes);
mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
- mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
+ mTransferableRoutes = new ArrayList<>(sessionInfo.mTransferableRoutes);
mVolumeHandling = sessionInfo.mVolumeHandling;
mVolumeMax = sessionInfo.mVolumeMax;
@@ -524,35 +524,35 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
- * Clears the transferrable routes.
+ * Clears the transferable routes.
*/
@NonNull
- public Builder clearTransferrableRoutes() {
- mTransferrableRoutes.clear();
+ public Builder clearTransferableRoutes() {
+ mTransferableRoutes.clear();
return this;
}
/**
- * Adds a route to the transferrable routes. The {@code routeId} must not be empty.
+ * Adds a route to the transferable routes. The {@code routeId} must not be empty.
*/
@NonNull
- public Builder addTransferrableRoute(@NonNull String routeId) {
+ public Builder addTransferableRoute(@NonNull String routeId) {
if (TextUtils.isEmpty(routeId)) {
throw new IllegalArgumentException("routeId must not be empty");
}
- mTransferrableRoutes.add(routeId);
+ mTransferableRoutes.add(routeId);
return this;
}
/**
- * Removes a route from the transferrable routes. The {@code routeId} must not be empty.
+ * Removes a route from the transferable routes. The {@code routeId} must not be empty.
*/
@NonNull
- public Builder removeTransferrableRoute(@NonNull String routeId) {
+ public Builder removeTransferableRoute(@NonNull String routeId) {
if (TextUtils.isEmpty(routeId)) {
throw new IllegalArgumentException("routeId must not be empty");
}
- mTransferrableRoutes.remove(routeId);
+ mTransferableRoutes.remove(routeId);
return this;
}
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 44f9b7c6eb39..ac59003de5e6 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -32,6 +32,7 @@ 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;
@@ -40,6 +41,7 @@ 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;
@@ -129,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) {}
@@ -1509,12 +1514,67 @@ static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const
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 filterSettingsObj) {
+ JNIEnv *env, int type, int subtype, jobject filterConfigObj) {
DemuxFilterSettings filterSettings;
jobject settingsObj =
env->GetObjectField(
- filterSettingsObj,
+ filterConfigObj,
env->GetFieldID(
env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"),
"mSettings",
@@ -1523,9 +1583,10 @@ static DemuxFilterSettings getFilterConfiguration(
switch (mainType) {
case DemuxFilterMainType::TS: {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
- int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I"));
+ 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,
};
DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
@@ -1553,7 +1614,12 @@ static DemuxFilterSettings getFilterConfiguration(
break;
}
case DemuxFilterMainType::MMTP: {
- DemuxMmtpFilterSettings mmtpFilterSettings;
+ 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:
@@ -1583,43 +1649,79 @@ static DemuxFilterSettings getFilterConfiguration(
break;
}
case DemuxFilterMainType::IP: {
- DemuxIpFilterSettings ipFilterSettings;
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
+
+ DemuxIpFilterSettings ipFilterSettings {
+ .ipAddr = ipAddr,
+ };
DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype);
switch (ipType) {
- case DemuxIpFilterType::SECTION:
+ case DemuxIpFilterType::SECTION: {
ipFilterSettings.filterSettings.section(
getFilterSectionSettings(env, settingsObj));
break;
- case DemuxIpFilterType::IP:
- // TODO: handle passthrough
- ipFilterSettings.filterSettings.bPassthrough(false);
+ }
+ 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:
+ }
+ default: {
break;
+ }
}
filterSettings.ip(ipFilterSettings);
break;
}
case DemuxFilterMainType::TLV: {
- DemuxTlvFilterSettings tlvFilterSettings;
+ 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:
+ case DemuxTlvFilterType::SECTION: {
tlvFilterSettings.filterSettings.section(
getFilterSectionSettings(env, settingsObj));
break;
- case DemuxTlvFilterType::TLV:
- // TODO: handle passthrough
- tlvFilterSettings.filterSettings.bPassthrough(false);
+ }
+ case DemuxTlvFilterType::TLV: {
+ bool bPassthrough = static_cast<bool>(
+ env->GetBooleanField(
+ filterConfigObj, env->GetFieldID(
+ clazz, "mPassthrough", "Z")));
+ tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
break;
- default:
+ }
+ default: {
break;
+ }
}
filterSettings.tlv(tlvFilterSettings);
break;
}
case DemuxFilterMainType::ALP: {
- DemuxAlpFilterSettings alpFilterSettings;
+ 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:
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/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 267927ff4a6e..f1dcf3dfd307 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -191,7 +191,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(sessionId, packageName)
.addSelectedRoute(routeId)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
- .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+ .addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
.setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(SESSION_VOLUME_MAX)
.setVolume(SESSION_VOLUME_INITIAL)
@@ -300,7 +300,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
.clearSelectedRoutes()
.addSelectedRoute(routeId)
.removeDeselectableRoute(routeId)
- .removeTransferrableRoute(routeId)
+ .removeTransferableRoute(routeId)
.build();
notifySessionUpdated(newSessionInfo);
publishRoutes();
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/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
index e6f8c01e22ac..dc6dddb431b4 100644
--- a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
+++ b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
@@ -18,75 +18,39 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/entity_header"
- style="@style/EntityHeader"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:orientation="horizontal">
+ style="@style/EntityHeader">
<LinearLayout
android:id="@+id/entity_header_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingTop="32dp"
+ android:paddingBottom="32dp"
android:layout_centerHorizontal="true"
- android:gravity="center_horizontal"
- android:orientation="horizontal">
+ android:orientation="vertical">
<LinearLayout
android:id="@+id/entity_header_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:gravity="center_horizontal"
- android:orientation="vertical">
+ android:layout_gravity="center"
+ android:orientation="horizontal">
<ImageView
android:id="@+id/entity_header_icon_personal"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:scaleType="fitCenter"
- android:antialias="true"/>
+ style="@style/CrossProfileEntityHeaderIcon" />
- <TextView
- android:id="@+id/install_type"
- style="@style/TextAppearance.EntityHeaderSummary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="2dp"
- android:text="Personal"/>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/entity_header_swap_horiz"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:scaleType="fitCenter"
- android:antialias="true"
- android:src="@drawable/ic_swap_horiz_grey"/>
-
- <LinearLayout
- android:id="@+id/entity_header_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:gravity="center_horizontal"
- android:orientation="vertical">
+ <ImageView
+ android:id="@+id/entity_header_swap_horiz"
+ style="@style/CrossProfileSwapHorizIcon "/>
<ImageView
android:id="@+id/entity_header_icon_work"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:scaleType="fitCenter"
- android:antialias="true"/>
- <TextView
- android:id="@+id/install_type"
- style="@style/TextAppearance.EntityHeaderSummary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="2dp"
- android:text="Work"/>
+ style="@style/CrossProfileEntityHeaderIcon" />
</LinearLayout>
- </LinearLayout>
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/CrossProfileEntityHeaderTitle" />
+ </LinearLayout>
</RelativeLayout>
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 6a2b729ff9eb..4a99e845a5fc 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -37,4 +37,35 @@
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
</style>
+
+ <style name="CrossProfileEntityHeaderIcon">
+ <item name="android:layout_width">48dp</item>
+ <item name="android:layout_height">48dp</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:antialias">true</item>
+ </style>
+
+ <style name="CrossProfileSwapHorizIcon">
+ <item name="android:layout_width">24dp</item>
+ <item name="android:layout_height">24dp</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:layout_marginStart">10dp</item>
+ <item name="android:layout_marginEnd">10dp</item>
+ <item name="android:src">@drawable/ic_swap_horiz_grey</item>
+ <item name="android:antialias">true</item>
+ </style>
+
+ <style name="CrossProfileEntityHeaderTitle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:textSize">18sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:layout_marginTop">8dp</item>
+ </style>
</resources> \ No newline at end of file
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/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 8037266ac92f..c5d4fa9f1b40 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -126,6 +126,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
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/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 18145939c384..530823a6a206 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -606,26 +606,21 @@ public class BugreportProgressService extends Service {
BugreportInfo info = new BugreportInfo(mContext, baseName, name,
shareTitle, shareDescription, bugreportType, mBugreportsDir);
- ParcelFileDescriptor bugreportFd;
- ParcelFileDescriptor screenshotFd;
-
- try {
- bugreportFd = info.createAndGetBugreportFd();
- if (bugreportFd == null) {
- Log.e(TAG, "Bugreport parcel file descriptor is null.");
- return;
- }
- screenshotFd = info.createAndGetDefaultScreenshotFd();
- if (screenshotFd == null) {
- Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
- FileUtils.closeQuietly(bugreportFd);
- info.bugreportFile.delete();
- return;
- }
- } catch (IOException e) {
- Log.e(TAG, "Error in generating bugreport files: ", e);
+ ParcelFileDescriptor bugreportFd = info.getBugreportFd();
+ if (bugreportFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as "
+ + " bugreport parcel file descriptor is null.");
return;
}
+ ParcelFileDescriptor screenshotFd = info.getDefaultScreenshotFd();
+ if (screenshotFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as"
+ + " screenshot parcel file descriptor is null. Deleting bugreport file");
+ FileUtils.closeQuietly(bugreportFd);
+ info.bugreportFile.delete();
+ return;
+ }
+
mBugreportManager = (BugreportManager) mContext.getSystemService(
Context.BUGREPORT_SERVICE);
final Executor executor = ActivityThread.currentActivityThread().getExecutor();
@@ -639,7 +634,7 @@ public class BugreportProgressService extends Service {
mBugreportManager.startBugreport(bugreportFd, screenshotFd,
new BugreportParams(bugreportType), executor, bugreportCallback);
} catch (RuntimeException e) {
- Log.i(TAG, "error in generating bugreports: ", e);
+ Log.i(TAG, "Error in generating bugreports: ", e);
// The binder call didn't go through successfully, so need to close the fds.
// If the calls went through API takes ownership.
FileUtils.closeQuietly(bugreportFd);
@@ -657,11 +652,15 @@ public class BugreportProgressService extends Service {
return null;
}
- private static void createReadWriteFile(File file) throws IOException {
- if (!file.exists()) {
- file.createNewFile();
- file.setReadable(true, true);
- file.setWritable(true, true);
+ private static void createReadWriteFile(File file) {
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ file.setReadable(true, true);
+ file.setWritable(true, true);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error in creating bugreport file: ", e);
}
}
@@ -1836,23 +1835,23 @@ public class BugreportProgressService extends Service {
void createBugreportFile(File bugreportsDir) {
bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
+ createReadWriteFile(bugreportFile);
}
void createScreenshotFile(File bugreportsDir) {
File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
addScreenshot(screenshotFile);
+ createReadWriteFile(screenshotFile);
}
- ParcelFileDescriptor createAndGetBugreportFd() throws IOException {
- createReadWriteFile(bugreportFile);
+ ParcelFileDescriptor getBugreportFd() {
return getFd(bugreportFile);
}
- ParcelFileDescriptor createAndGetDefaultScreenshotFd() throws IOException {
+ ParcelFileDescriptor getDefaultScreenshotFd() {
if (screenshotFiles.isEmpty()) {
return null;
}
- createReadWriteFile(screenshotFiles.get(0));
return getFd(screenshotFiles.get(0));
}
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/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d298b992d418..56cfe8010fef 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -64,12 +64,12 @@ import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -88,13 +88,13 @@ import com.android.internal.util.ScreenRecordHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.controls.ui.ControlsUiController;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -110,6 +110,7 @@ import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolat
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -155,6 +156,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final BroadcastDispatcher mBroadcastDispatcher;
private final ContentResolver mContentResolver;
private final Resources mResources;
+ private final ConfigurationController mConfigurationController;
private final UserManager mUserManager;
private final TrustManager mTrustManager;
private final IActivityManager mIActivityManager;
@@ -186,6 +188,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final NotificationShadeWindowController mNotificationShadeWindowController;
private GlobalActionsPanelPlugin mPanelPlugin;
private ControlsUiController mControlsUiController;
+ private final IWindowManager mIWindowManager;
+ private final Executor mBackgroundExecutor;
/**
* @param context everything needs a context :(
@@ -204,7 +208,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- ControlsUiController controlsUiController) {
+ ControlsUiController controlsUiController, IWindowManager iWindowManager,
+ @Background Executor backgroundExecutor) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -215,6 +220,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mBroadcastDispatcher = broadcastDispatcher;
mContentResolver = contentResolver;
mResources = resources;
+ mConfigurationController = configurationController;
mUserManager = userManager;
mTrustManager = trustManager;
mIActivityManager = iActivityManager;
@@ -225,6 +231,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
+ mIWindowManager = iWindowManager;
+ mBackgroundExecutor = backgroundExecutor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -249,7 +257,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mScreenshotHelper = new ScreenshotHelper(context);
mScreenRecordHelper = new ScreenRecordHelper(context);
- configurationController.addCallback(this);
+ mConfigurationController.addCallback(this);
mActivityStarter = activityStarter;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -495,7 +503,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
public void destroy() {
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ mConfigurationController.removeCallback(this);
}
private final class PowerAction extends SinglePressAction implements LongPressAction {
@@ -836,14 +844,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public void onPress() {
- new LockPatternUtils(mContext)
- .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
- UserHandle.USER_ALL);
+ mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+ UserHandle.USER_ALL);
try {
- WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ mIWindowManager.lockNow(null);
// Lock profiles (if any) on the background thread.
- final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
- bgHandler.post(() -> lockProfiles());
+ mBackgroundExecutor.execute(() -> lockProfiles());
} catch (RemoteException e) {
Log.e(TAG, "Error while trying to lock device.", e);
}
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/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 5a0a495e1f85..f1fba792a882 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -282,6 +282,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
.inflationCallback(any()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
when(mExpandableNotificationRowComponentBuilder
+ .rowContentBindStage(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
.onExpandClickListener(any()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
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/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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3ffe1be89060..0edc16022640 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -56,6 +56,7 @@ import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
@@ -3365,7 +3366,8 @@ public class AppOpsService extends IAppOpsService.Stub {
return false;
}
- return permInfo.getProtection() == PROTECTION_DANGEROUS;
+ return permInfo.getProtection() == PROTECTION_DANGEROUS
+ || (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0;
}
private void verifyIncomingUid(int uid) {
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/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/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 18383c431479..ae11c70da46a 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -257,12 +257,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
builder.addSelectedRoute(mSelectedRouteId);
if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
- builder.addTransferrableRoute(mDefaultRoute.getId());
+ builder.addTransferableRoute(mDefaultRoute.getId());
}
for (MediaRoute2Info route : mBluetoothRoutes) {
if (!TextUtils.equals(mSelectedRouteId, route.getId())) {
- builder.addTransferrableRoute(route.getId());
+ builder.addTransferableRoute(route.getId());
}
}
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/people/PeopleServiceInternal.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
index c5b868fbe99d..31fa4d192278 100644
--- a/services/core/java/com/android/server/people/PeopleServiceInternal.java
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -17,6 +17,8 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.CancellationSignal;
import android.service.appprediction.IPredictionService;
/**
@@ -25,16 +27,23 @@ import android.service.appprediction.IPredictionService;
public abstract class PeopleServiceInternal extends IPredictionService.Stub {
/**
+ * Prunes the data for the specified user. Called by {@link
+ * com.android.server.people.data.DataMaintenanceService} when the device is idle.
+ */
+ public abstract void pruneDataForUser(@UserIdInt int userId,
+ @NonNull CancellationSignal signal);
+
+ /**
* The number conversation infos will be dynamic, based on the currently installed apps on the
* device. All of which should be combined into a single blob to be backed up.
*/
- public abstract byte[] backupConversationInfos(@NonNull int userId);
+ public abstract byte[] backupConversationInfos(@UserIdInt int userId);
/**
* Multiple conversation infos may exist in the restore payload, child classes are required to
* manage the restoration based on how individual conversation infos were originally combined
* during backup.
*/
- public abstract void restoreConversationInfos(@NonNull int userId, @NonNull String key,
+ public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
@NonNull byte[] payload);
}
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 064fd3f4e0b2..8c6e6916ec86 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14707,8 +14707,7 @@ public class PackageManagerService extends IPackageManager.Stub
void handleVerificationFinished() {
if (!mVerificationCompleted) {
mVerificationCompleted = true;
- if (mIntegrityVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
- mIntegrityVerificationCompleted = true;
+ if (mIntegrityVerificationCompleted) {
handleReturnCode();
}
// integrity verification still pending.
@@ -14718,8 +14717,7 @@ public class PackageManagerService extends IPackageManager.Stub
void handleIntegrityVerificationFinished() {
if (!mIntegrityVerificationCompleted) {
mIntegrityVerificationCompleted = true;
- if (mVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
- mVerificationCompleted = true;
+ if (mVerificationCompleted) {
handleReturnCode();
}
// verifier still pending
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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1859fae5e7fc..f2917c54c3fa 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5568,11 +5568,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) {
mAmInternal.sendForegroundProfileChanged(r.mUserId);
}
+ final Task prevTask = mLastResumedActivity != null ? mLastResumedActivity.getTask() : null;
+
updateResumedAppTrace(r);
mLastResumedActivity = r;
r.getDisplay().setFocusedApp(r, true);
+ if (prevTask == null || task != prevTask) {
+ if (prevTask != null) {
+ mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false);
+ }
+ mTaskChangeNotificationController.notifyTaskFocusChanged(task.mTaskId, true);
+ }
+
applyUpdateLockStateLocked(r);
applyUpdateVrModeLocked(r);
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/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 2dde0bac74a8..f715d8f3529b 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -59,6 +59,7 @@ class TaskChangeNotificationController {
private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24;
private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
+ private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -179,6 +180,10 @@ class TaskChangeNotificationController {
l.onRecentTaskListFrozenChanged(m.arg1 != 0);
};
+ private final TaskStackConsumer mNotifyTaskFocusChanged = (l, m) -> {
+ l.onTaskFocusChanged(m.arg1, m.arg2 != 0);
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -273,6 +278,9 @@ class TaskChangeNotificationController {
case NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG:
forAllRemoteListeners(mNotifyTaskListFrozen, msg);
break;
+ case NOTIFY_TASK_FOCUS_CHANGED_MSG:
+ forAllRemoteListeners(mNotifyTaskFocusChanged, msg);
+ break;
}
}
}
@@ -565,4 +573,12 @@ class TaskChangeNotificationController {
forAllLocalListeners(mNotifyTaskListFrozen, msg);
msg.sendToTarget();
}
+
+ /** @see ITaskStackListener#onTaskFocusChanged(int, boolean) */
+ void notifyTaskFocusChanged(int taskId, boolean focused) {
+ final Message msg = mHandler.obtainMessage(NOTIFY_TASK_FOCUS_CHANGED_MSG,
+ taskId, focused ? 1 : 0);
+ forAllLocalListeners(mNotifyTaskFocusChanged, msg);
+ msg.sendToTarget();
+ }
}
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/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 2d18a2994135..663bf4f30708 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -17,6 +17,7 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
import android.app.prediction.AppTarget;
@@ -24,6 +25,7 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.IPredictionCallback;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.os.CancellationSignal;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
@@ -138,6 +140,21 @@ public class PeopleService extends SystemService {
});
}
+ @Override
+ public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
+ mDataManager.pruneDataForUser(userId, signal);
+ }
+
+ @Override
+ public byte[] backupConversationInfos(@UserIdInt int userId) {
+ return new byte[0];
+ }
+
+ @Override
+ public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
+ @NonNull byte[] payload) {
+ }
+
@VisibleForTesting
SessionInfo getSessionInfo(AppPredictionSessionId sessionId) {
return mSessions.get(sessionId);
@@ -160,14 +177,5 @@ public class PeopleService extends SystemService {
Slog.e(TAG, "Failed to calling callback" + e);
}
}
-
- @Override
- public byte[] backupConversationInfos(int userId) {
- return new byte[0];
- }
-
- @Override
- public void restoreConversationInfos(int userId, String key, byte[] payload) {
- }
}
}
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index ea36d38e5d4a..3afb209ae5cd 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -133,10 +133,11 @@ class ConversationStore {
}
@MainThread
- synchronized void deleteConversation(@NonNull String shortcutId) {
+ @Nullable
+ synchronized ConversationInfo deleteConversation(@NonNull String shortcutId) {
ConversationInfo conversationInfo = mConversationInfoMap.remove(shortcutId);
if (conversationInfo == null) {
- return;
+ return null;
}
LocusId locusId = conversationInfo.getLocusId();
@@ -159,6 +160,7 @@ class ConversationStore {
mNotifChannelIdToShortcutIdMap.remove(notifChannelId);
}
scheduleUpdateConversationsOnDisk();
+ return conversationInfo;
}
synchronized void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
diff --git a/services/people/java/com/android/server/people/data/DataMaintenanceService.java b/services/people/java/com/android/server/people/data/DataMaintenanceService.java
new file mode 100644
index 000000000000..58f0654c7b51
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/DataMaintenanceService.java
@@ -0,0 +1,92 @@
+/*
+ * 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.people.data;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.CancellationSignal;
+
+import com.android.server.LocalServices;
+import com.android.server.people.PeopleServiceInternal;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This service runs periodically to ensure the data consistency and the retention policy for a
+ * specific user's data.
+ */
+public class DataMaintenanceService extends JobService {
+
+ private static final long JOB_RUN_INTERVAL = TimeUnit.HOURS.toMillis(24);
+
+ /** This job ID must be unique within the system server. */
+ private static final int BASE_JOB_ID = 0xC315BD7; // 204561367
+
+ static void scheduleJob(Context context, @UserIdInt int userId) {
+ int jobId = getJobId(userId);
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler.getPendingJob(jobId) == null) {
+ ComponentName component = new ComponentName(context, DataMaintenanceService.class);
+ JobInfo newJob = new JobInfo.Builder(jobId, component)
+ .setRequiresDeviceIdle(true)
+ .setPeriodic(JOB_RUN_INTERVAL)
+ .build();
+ jobScheduler.schedule(newJob);
+ }
+ }
+
+ static void cancelJob(Context context, @UserIdInt int userId) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ jobScheduler.cancel(getJobId(userId));
+ }
+
+ private CancellationSignal mSignal;
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ int userId = getUserId(params.getJobId());
+ mSignal = new CancellationSignal();
+ new Thread(() -> {
+ PeopleServiceInternal peopleServiceInternal =
+ LocalServices.getService(PeopleServiceInternal.class);
+ peopleServiceInternal.pruneDataForUser(userId, mSignal);
+ jobFinished(params, mSignal.isCanceled());
+ }).start();
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ if (mSignal != null) {
+ mSignal.cancel();
+ }
+ return false;
+ }
+
+ private static int getJobId(@UserIdInt int userId) {
+ return BASE_JOB_ID + userId;
+ }
+
+ private static @UserIdInt int getUserId(int jobId) {
+ return jobId - BASE_JOB_ID;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index a904b42f47db..2edfd7487ec0 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
@@ -40,6 +41,7 @@ import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
@@ -53,16 +55,20 @@ import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -94,10 +100,12 @@ public class DataManager {
private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
private final SparseArray<NotificationListenerService> mNotificationListeners =
new SparseArray<>();
+ private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
private final ContentObserver mCallLogContentObserver;
private final ContentObserver mMmsSmsContentObserver;
private ShortcutServiceInternal mShortcutServiceInternal;
+ private PackageManagerInternal mPackageManagerInternal;
private ShortcutManager mShortcutManager;
private UserManager mUserManager;
@@ -120,6 +128,7 @@ public class DataManager {
/** Initialization. Called when the system services are up running. */
public void initialize() {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mShortcutManager = mContext.getSystemService(ShortcutManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
@@ -172,6 +181,10 @@ public class DataManager {
// Should never occur for local calls.
}
+ PackageMonitor packageMonitor = new PerUserPackageMonitor();
+ packageMonitor.register(mContext, null, UserHandle.of(userId), true);
+ mPackageMonitors.put(userId, packageMonitor);
+
if (userId == UserHandle.USER_SYSTEM) {
// The call log and MMS/SMS messages are shared across user profiles. So only need to
// register the content observers once for the primary user.
@@ -183,6 +196,8 @@ public class DataManager {
MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
}
+
+ DataMaintenanceService.scheduleJob(mContext, userId);
}
/** This method is called when a user is stopped. */
@@ -207,10 +222,15 @@ public class DataManager {
// Should never occur for local calls.
}
}
+ if (mPackageMonitors.indexOfKey(userId) >= 0) {
+ mPackageMonitors.get(userId).unregister();
+ }
if (userId == UserHandle.USER_SYSTEM) {
mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
}
+
+ DataMaintenanceService.cancelJob(mContext, userId);
}
/**
@@ -274,9 +294,8 @@ public class DataManager {
|| TextUtils.isEmpty(mimeType)) {
return;
}
- EventHistoryImpl eventHistory =
- packageData.getEventStore().getOrCreateShortcutEventHistory(
- shortcutInfo.getId());
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
@Event.EventType int eventType;
if (mimeType.startsWith("text/")) {
eventType = Event.TYPE_SHARE_TEXT;
@@ -291,6 +310,45 @@ public class DataManager {
}
}
+ /** Prunes the data for the specified user. */
+ public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null || signal.isCanceled()) {
+ return;
+ }
+ pruneUninstalledPackageData(userData);
+
+ long currentTimeMillis = System.currentTimeMillis();
+ userData.forAllPackages(packageData -> {
+ if (signal.isCanceled()) {
+ return;
+ }
+ packageData.getEventStore().pruneOldEvents(currentTimeMillis);
+ if (!packageData.isDefaultDialer()) {
+ packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_CALL);
+ }
+ if (!packageData.isDefaultSmsApp()) {
+ packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
+ }
+ packageData.pruneOrphanEvents();
+ });
+ }
+
+ private void pruneUninstalledPackageData(@NonNull UserData userData) {
+ Set<String> installApps = new ArraySet<>();
+ mPackageManagerInternal.forEachInstalledPackage(
+ pkg -> installApps.add(pkg.getPackageName()), userData.getUserId());
+ List<String> packagesToDelete = new ArrayList<>();
+ userData.forAllPackages(packageData -> {
+ if (!installApps.contains(packageData.getPackageName())) {
+ packagesToDelete.add(packageData.getPackageName());
+ }
+ });
+ for (String packageName : packagesToDelete) {
+ userData.deletePackageData(packageName);
+ }
+ }
+
/** Gets a list of {@link ShortcutInfo}s with the given shortcut IDs. */
private List<ShortcutInfo> getShortcuts(
@NonNull String packageName, @UserIdInt int userId,
@@ -347,7 +405,8 @@ public class DataManager {
|| packageData.getConversationStore().getConversation(shortcutId) == null) {
return null;
}
- return packageData.getEventStore().getOrCreateShortcutEventHistory(shortcutId);
+ return packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
}
@VisibleForTesting
@@ -413,6 +472,11 @@ public class DataManager {
}
@VisibleForTesting
+ PackageMonitor getPackageMonitorForTesting(@UserIdInt int userId) {
+ return mPackageMonitors.get(userId);
+ }
+
+ @VisibleForTesting
UserData getUserDataForTesting(@UserIdInt int userId) {
return mUserDataArray.get(userId);
}
@@ -499,7 +563,8 @@ public class DataManager {
return;
}
EventStore eventStore = defaultDialer.getEventStore();
- eventStore.getOrCreateCallEventHistory(phoneNumber).addEvent(event);
+ eventStore.getOrCreateEventHistory(
+ EventStore.CATEGORY_CALL, phoneNumber).addEvent(event);
});
}
}
@@ -544,7 +609,8 @@ public class DataManager {
return;
}
EventStore eventStore = defaultSmsApp.getEventStore();
- eventStore.getOrCreateSmsEventHistory(phoneNumber).addEvent(event);
+ eventStore.getOrCreateEventHistory(
+ EventStore.CATEGORY_SMS, phoneNumber).addEvent(event);
});
}
}
@@ -670,6 +736,20 @@ public class DataManager {
}
}
+ private class PerUserPackageMonitor extends PackageMonitor {
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ super.onPackageRemoved(packageName, uid);
+
+ int userId = getChangingUserId();
+ UserData userData = getUnlockedUserData(userId);
+ if (userData != null) {
+ userData.deletePackageData(packageName);
+ }
+ }
+ }
+
private class ShutdownBroadcastReceiver extends BroadcastReceiver {
@Override
diff --git a/services/people/java/com/android/server/people/data/EventHistoryImpl.java b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
index 6b6bd7e3cfb0..6bef1db5582e 100644
--- a/services/people/java/com/android/server/people/data/EventHistoryImpl.java
+++ b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
@@ -78,6 +78,17 @@ class EventHistoryImpl implements EventHistory {
mRecentEvents.add(event);
}
+ void onDestroy() {
+ mEventIndexArray.clear();
+ mRecentEvents.clear();
+ // TODO: STOPSHIP: Delete the data files.
+ }
+
+ /** Deletes the events data that exceeds the retention period. */
+ void pruneOldEvents(long currentTimeMillis) {
+ // TODO: STOPSHIP: Delete the old events data files.
+ }
+
@VisibleForTesting
static class Injector {
diff --git a/services/people/java/com/android/server/people/data/EventList.java b/services/people/java/com/android/server/people/data/EventList.java
index b267d667b422..d770f912ea40 100644
--- a/services/people/java/com/android/server/people/data/EventList.java
+++ b/services/people/java/com/android/server/people/data/EventList.java
@@ -69,6 +69,10 @@ class EventList {
return result;
}
+ void clear() {
+ mEvents.clear();
+ }
+
/** Returns the first index whose timestamp is greater or equal to the provided timestamp. */
private int firstIndexOnOrAfter(long timestamp) {
int result = mEvents.size();
diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java
index d6b7a863ca2d..c8d44ac07106 100644
--- a/services/people/java/com/android/server/people/data/EventStore.java
+++ b/services/people/java/com/android/server/people/data/EventStore.java
@@ -16,99 +16,129 @@
package com.android.server.people.data;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.LocusId;
import android.util.ArrayMap;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
/** The store that stores and accesses the events data for a package. */
class EventStore {
- private final EventHistoryImpl mPackageEventHistory = new EventHistoryImpl();
+ /** The events that are queryable with a shortcut ID. */
+ static final int CATEGORY_SHORTCUT_BASED = 0;
- // Shortcut ID -> Event History
- private final Map<String, EventHistoryImpl> mShortcutEventHistoryMap = new ArrayMap<>();
+ /** The events that are queryable with a {@link android.content.LocusId}. */
+ static final int CATEGORY_LOCUS_ID_BASED = 1;
- // Locus ID -> Event History
- private final Map<LocusId, EventHistoryImpl> mLocusEventHistoryMap = new ArrayMap<>();
+ /** The phone call events that are queryable with a phone number. */
+ static final int CATEGORY_CALL = 2;
- // Phone Number -> Event History
- private final Map<String, EventHistoryImpl> mCallEventHistoryMap = new ArrayMap<>();
+ /** The SMS or MMS events that are queryable with a phone number. */
+ static final int CATEGORY_SMS = 3;
- // Phone Number -> Event History
- private final Map<String, EventHistoryImpl> mSmsEventHistoryMap = new ArrayMap<>();
+ /** The events that are queryable with an {@link android.app.Activity} class name. */
+ static final int CATEGORY_CLASS_BASED = 4;
- /** Gets the package level {@link EventHistory}. */
- @NonNull
- EventHistory getPackageEventHistory() {
- return mPackageEventHistory;
- }
+ @IntDef(prefix = { "CATEGORY_" }, value = {
+ CATEGORY_SHORTCUT_BASED,
+ CATEGORY_LOCUS_ID_BASED,
+ CATEGORY_CALL,
+ CATEGORY_SMS,
+ CATEGORY_CLASS_BASED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EventCategory {}
- /** Gets the {@link EventHistory} for the specified {@code shortcutId} if exists. */
- @Nullable
- EventHistory getShortcutEventHistory(String shortcutId) {
- return mShortcutEventHistoryMap.get(shortcutId);
- }
+ private final List<Map<String, EventHistoryImpl>> mEventHistoryMaps = new ArrayList<>();
- /** Gets the {@link EventHistory} for the specified {@code locusId} if exists. */
- @Nullable
- EventHistory getLocusEventHistory(LocusId locusId) {
- return mLocusEventHistoryMap.get(locusId);
+ EventStore() {
+ mEventHistoryMaps.add(CATEGORY_SHORTCUT_BASED, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_LOCUS_ID_BASED, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_CALL, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_SMS, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_CLASS_BASED, new ArrayMap<>());
}
- /** Gets the phone call {@link EventHistory} for the specified {@code phoneNumber} if exists. */
- @Nullable
- EventHistory getCallEventHistory(String phoneNumber) {
- return mCallEventHistoryMap.get(phoneNumber);
- }
-
- /** Gets the SMS {@link EventHistory} for the specified {@code phoneNumber} if exists. */
+ /**
+ * Gets the {@link EventHistory} for the specified key if exists.
+ *
+ * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+ * name.
+ */
@Nullable
- EventHistory getSmsEventHistory(String phoneNumber) {
- return mSmsEventHistoryMap.get(phoneNumber);
+ EventHistory getEventHistory(@EventCategory int category, String key) {
+ return mEventHistoryMaps.get(category).get(key);
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code shortcutId} or creates a new
- * instance and put it into the store if not exists. The caller needs to verify if a
- * conversation with this shortcut ID exists before calling this method.
+ * Gets the {@link EventHistoryImpl} for the specified ID or creates a new instance and put it
+ * into the store if not exists. The caller needs to verify if the associated conversation
+ * exists before calling this method.
+ *
+ * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+ * name.
*/
@NonNull
- EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
- return mShortcutEventHistoryMap.computeIfAbsent(shortcutId, key -> new EventHistoryImpl());
+ EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+ return mEventHistoryMaps.get(category).computeIfAbsent(key, k -> new EventHistoryImpl());
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code locusId} or creates a new
- * instance and put it into the store if not exists. The caller needs to ensure a conversation
- * with this locus ID exists before calling this method.
+ * Deletes the events and index data for the specified key.
+ *
+ * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+ * name.
*/
- @NonNull
- EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
- return mLocusEventHistoryMap.computeIfAbsent(locusId, key -> new EventHistoryImpl());
+ void deleteEventHistory(@EventCategory int category, String key) {
+ EventHistoryImpl eventHistory = mEventHistoryMaps.get(category).remove(key);
+ if (eventHistory != null) {
+ eventHistory.onDestroy();
+ }
}
- /**
- * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for call events
- * or creates a new instance and put it into the store if not exists. The caller needs to ensure
- * a conversation with this phone number exists and this package is the default dialer
- * before calling this method.
- */
- @NonNull
- EventHistoryImpl getOrCreateCallEventHistory(String phoneNumber) {
- return mCallEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ /** Deletes all the events and index data for the specified category from disk. */
+ void deleteEventHistories(@EventCategory int category) {
+ mEventHistoryMaps.get(category).clear();
+ // TODO: Implement this method to delete the data from disk.
+ }
+
+ /** Deletes the events data that exceeds the retention period. */
+ void pruneOldEvents(long currentTimeMillis) {
+ for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) {
+ for (EventHistoryImpl eventHistory : map.values()) {
+ eventHistory.pruneOldEvents(currentTimeMillis);
+ }
+ }
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for SMS events
- * or creates a new instance and put it into the store if not exists. The caller needs to ensure
- * a conversation with this phone number exists and this package is the default SMS app
- * before calling this method.
+ * Prunes the event histories whose key (shortcut ID, locus ID or phone number) does not match
+ * any conversations.
+ *
+ * @param keyChecker Check whether there exists a conversation contains this key.
*/
- @NonNull
- EventHistoryImpl getOrCreateSmsEventHistory(String phoneNumber) {
- return mSmsEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ void pruneOrphanEventHistories(@EventCategory int category, Predicate<String> keyChecker) {
+ Set<String> keys = mEventHistoryMaps.get(category).keySet();
+ List<String> keysToDelete = new ArrayList<>();
+ for (String key : keys) {
+ if (!keyChecker.test(key)) {
+ keysToDelete.add(key);
+ }
+ }
+ Map<String, EventHistoryImpl> eventHistoryMap = mEventHistoryMaps.get(category);
+ for (String key : keysToDelete) {
+ EventHistoryImpl eventHistory = eventHistoryMap.remove(key);
+ if (eventHistory != null) {
+ eventHistory.onDestroy();
+ }
+ }
}
}
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index f67699c28531..c55f97205bc5 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -16,6 +16,12 @@
package com.android.server.people.data;
+import static com.android.server.people.data.EventStore.CATEGORY_CALL;
+import static com.android.server.people.data.EventStore.CATEGORY_CLASS_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_LOCUS_ID_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_SHORTCUT_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_SMS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -89,11 +95,6 @@ public class PackageData {
mConversationStore.forAllConversations(consumer);
}
- @NonNull
- public EventHistory getPackageLevelEventHistory() {
- return getEventStore().getPackageEventHistory();
- }
-
/**
* Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
* ConversationInfo} does not exist.
@@ -117,14 +118,16 @@ public class PackageData {
return result;
}
- EventHistory shortcutEventHistory = getEventStore().getShortcutEventHistory(shortcutId);
+ EventHistory shortcutEventHistory = getEventStore().getEventHistory(
+ CATEGORY_SHORTCUT_BASED, shortcutId);
if (shortcutEventHistory != null) {
result.addEventHistory(shortcutEventHistory);
}
LocusId locusId = conversationInfo.getLocusId();
if (locusId != null) {
- EventHistory locusEventHistory = getEventStore().getLocusEventHistory(locusId);
+ EventHistory locusEventHistory = getEventStore().getEventHistory(
+ CATEGORY_LOCUS_ID_BASED, locusId.getId());
if (locusEventHistory != null) {
result.addEventHistory(locusEventHistory);
}
@@ -135,13 +138,15 @@ public class PackageData {
return result;
}
if (isDefaultDialer()) {
- EventHistory callEventHistory = getEventStore().getCallEventHistory(phoneNumber);
+ EventHistory callEventHistory = getEventStore().getEventHistory(
+ CATEGORY_CALL, phoneNumber);
if (callEventHistory != null) {
result.addEventHistory(callEventHistory);
}
}
if (isDefaultSmsApp()) {
- EventHistory smsEventHistory = getEventStore().getSmsEventHistory(phoneNumber);
+ EventHistory smsEventHistory = getEventStore().getEventHistory(
+ CATEGORY_SMS, phoneNumber);
if (smsEventHistory != null) {
result.addEventHistory(smsEventHistory);
}
@@ -149,6 +154,14 @@ public class PackageData {
return result;
}
+ /** Gets the {@link EventHistory} for a given Activity class. */
+ @NonNull
+ public EventHistory getClassLevelEventHistory(String className) {
+ EventHistory eventHistory = getEventStore().getEventHistory(
+ CATEGORY_CLASS_BASED, className);
+ return eventHistory != null ? eventHistory : new AggregateEventHistoryImpl();
+ }
+
public boolean isDefaultDialer() {
return mIsDefaultDialerPredicate.test(mPackageName);
}
@@ -167,6 +180,47 @@ public class PackageData {
return mEventStore;
}
+ /**
+ * Deletes all the data (including conversation, events and index) for the specified
+ * conversation shortcut ID.
+ */
+ void deleteDataForConversation(String shortcutId) {
+ ConversationInfo conversationInfo = mConversationStore.deleteConversation(shortcutId);
+ if (conversationInfo == null) {
+ return;
+ }
+ mEventStore.deleteEventHistory(CATEGORY_SHORTCUT_BASED, shortcutId);
+ if (conversationInfo.getLocusId() != null) {
+ mEventStore.deleteEventHistory(
+ CATEGORY_LOCUS_ID_BASED, conversationInfo.getLocusId().getId());
+ }
+ String phoneNumber = conversationInfo.getContactPhoneNumber();
+ if (!TextUtils.isEmpty(phoneNumber)) {
+ if (isDefaultDialer()) {
+ mEventStore.deleteEventHistory(CATEGORY_CALL, phoneNumber);
+ }
+ if (isDefaultSmsApp()) {
+ mEventStore.deleteEventHistory(CATEGORY_SMS, phoneNumber);
+ }
+ }
+ }
+
+ /** Prunes the events and index data that don't have a associated conversation. */
+ void pruneOrphanEvents() {
+ mEventStore.pruneOrphanEventHistories(CATEGORY_SHORTCUT_BASED,
+ key -> mConversationStore.getConversation(key) != null);
+ mEventStore.pruneOrphanEventHistories(CATEGORY_LOCUS_ID_BASED,
+ key -> mConversationStore.getConversationByLocusId(new LocusId(key)) != null);
+ if (isDefaultDialer()) {
+ mEventStore.pruneOrphanEventHistories(CATEGORY_CALL,
+ key -> mConversationStore.getConversationByPhoneNumber(key) != null);
+ }
+ if (isDefaultSmsApp()) {
+ mEventStore.pruneOrphanEventHistories(CATEGORY_SMS,
+ key -> mConversationStore.getConversationByPhoneNumber(key) != null);
+ }
+ }
+
void onDestroy() {
// TODO: STOPSHIP: Implements this method for the case of package being uninstalled.
}
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 644c155b4158..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;
}
@@ -129,8 +129,8 @@ class UsageStatsQueryHelper {
if (packageData.getConversationStore().getConversation(shortcutId) == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
- shortcutId);
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
eventHistory.addEvent(event);
}
@@ -138,8 +138,8 @@ class UsageStatsQueryHelper {
if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory(
- locusId);
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
eventHistory.addEvent(event);
}
@@ -151,8 +151,8 @@ class UsageStatsQueryHelper {
if (conversationInfo == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
- conversationInfo.getShortcutId());
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, conversationInfo.getShortcutId());
eventHistory.addEvent(event);
}
}
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index aaa5db878e08..7ca4b6c76a36 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -104,6 +104,14 @@ class UserData {
return mPackageDataMap.get(packageName);
}
+ /** Deletes the specified package data. */
+ void deletePackageData(@NonNull String packageName) {
+ PackageData packageData = mPackageDataMap.remove(packageName);
+ if (packageData != null) {
+ packageData.onDestroy();
+ }
+ }
+
void setDefaultDialer(@Nullable String packageName) {
mDefaultDialer = packageName;
}
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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8f70ccaaa9ba..7b2b30b0280f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2087,7 +2087,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(new ArrayList<>())
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2105,7 +2105,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setupProfileOwner();
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
assertExpectException(SecurityException.class, null,
@@ -2157,7 +2157,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(new ArrayList<>())
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2177,8 +2177,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
FactoryResetProtectionPolicy actualPolicy) {
- assertThat(actualPolicy.isFactoryResetProtectionDisabled()).isEqualTo(
- expectedPolicy.isFactoryResetProtectionDisabled());
+ assertThat(actualPolicy.isFactoryResetProtectionEnabled()).isEqualTo(
+ expectedPolicy.isFactoryResetProtectionEnabled());
assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
actualPolicy.getFactoryResetProtectionAccounts());
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
index bc853c693b3a..e8818a3f4940 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -62,7 +62,7 @@ public class FactoryResetProtectionPolicyTest {
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(accounts)
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
testParcelAndUnparcel(policy);
@@ -77,7 +77,7 @@ public class FactoryResetProtectionPolicyTest {
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(accounts)
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
testParcelAndUnparcel(policy);
@@ -133,8 +133,8 @@ public class FactoryResetProtectionPolicyTest {
private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
FactoryResetProtectionPolicy actualPolicy) {
- assertEquals(expectedPolicy.isFactoryResetProtectionDisabled(),
- actualPolicy.isFactoryResetProtectionDisabled());
+ assertEquals(expectedPolicy.isFactoryResetProtectionEnabled(),
+ actualPolicy.isFactoryResetProtectionEnabled());
assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
actualPolicy.getFactoryResetProtectionAccounts());
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 6769faaa4c5d..0bb984ef164b 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -24,12 +24,14 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -41,6 +43,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
+import android.app.job.JobScheduler;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
@@ -49,12 +52,15 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.CancellationSignal;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
@@ -66,6 +72,7 @@ import android.telephony.TelephonyManager;
import android.util.Range;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
import org.junit.After;
@@ -84,6 +91,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
@RunWith(JUnit4.class)
public final class DataManagerTest {
@@ -101,12 +109,14 @@ public final class DataManagerTest {
@Mock private Context mContext;
@Mock private ShortcutServiceInternal mShortcutServiceInternal;
@Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private ShortcutManager mShortcutManager;
@Mock private UserManager mUserManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private TelecomManager mTelecomManager;
@Mock private ContentResolver mContentResolver;
@Mock private ScheduledExecutorService mExecutorService;
+ @Mock private JobScheduler mJobScheduler;
@Mock private ScheduledFuture mScheduledFuture;
@Mock private StatusBarNotification mStatusBarNotification;
@Mock private Notification mNotification;
@@ -114,6 +124,7 @@ public final class DataManagerTest {
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
private int mCallingUserId;
+ private CancellationSignal mCancellationSignal;
private TestInjector mInjector;
@Before
@@ -124,6 +135,15 @@ public final class DataManagerTest {
addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
+ addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternal);
+ AndroidPackage androidPackage = mock(AndroidPackage.class);
+ when(androidPackage.getPackageName()).thenReturn(TEST_PKG_NAME);
+ doAnswer(ans -> {
+ Consumer<AndroidPackage> callback = (Consumer<AndroidPackage>) ans.getArguments()[0];
+ callback.accept(androidPackage);
+ return null;
+ }).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
+
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
Context originalContext = getInstrumentation().getTargetContext();
@@ -145,6 +165,10 @@ public final class DataManagerTest {
when(mTelecomManager.getDefaultDialerPackage(any(UserHandle.class)))
.thenReturn(TEST_PKG_NAME);
+ when(mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).thenReturn(mJobScheduler);
+ when(mContext.getSystemServiceName(JobScheduler.class)).thenReturn(
+ Context.JOB_SCHEDULER_SERVICE);
+
when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
TimeUnit.class))).thenReturn(mScheduledFuture);
@@ -168,6 +192,8 @@ public final class DataManagerTest {
mCallingUserId = USER_ID_PRIMARY;
+ mCancellationSignal = new CancellationSignal();
+
mInjector = new TestInjector();
mDataManager = new DataManager(mContext, mInjector);
mDataManager.initialize();
@@ -177,6 +203,7 @@ public final class DataManagerTest {
public void tearDown() {
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@Test
@@ -454,6 +481,101 @@ public final class DataManagerTest {
assertEquals(2, activeTimeSlots.size());
}
+ @Test
+ public void testDeleteUninstalledPackageDataOnPackageRemoved() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+
+ PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_PRIMARY);
+ intent.setData(Uri.parse("package:" + TEST_PKG_NAME));
+ packageMonitor.onReceive(mContext, intent);
+ assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+ }
+
+ @Test
+ public void testPruneUninstalledPackageData() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+
+ doAnswer(ans -> null).when(mPackageManagerInternal)
+ .forEachInstalledPackage(any(Consumer.class), anyInt());
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+ }
+
+ @Test
+ public void testPruneCallEventsFromNonDialer() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ long currentTimestamp = System.currentTimeMillis();
+ mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
+
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.CALL_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeTimeSlots.size());
+
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ activeTimeSlots.clear();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.CALL_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertTrue(activeTimeSlots.isEmpty());
+ }
+
+ @Test
+ public void testPruneSmsEventsFromNonDefaultSmsApp() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
+
+ long currentTimestamp = System.currentTimeMillis();
+ mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
+
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.SMS_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeTimeSlots.size());
+
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ activeTimeSlots.clear();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.SMS_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertTrue(activeTimeSlots.isEmpty());
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
index 1ddc21e4ea4d..e52cdf59847c 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
@@ -81,8 +81,10 @@ public final class PackageDataTest {
@Test
public void testGetEventHistory() {
EventStore eventStore = mPackageData.getEventStore();
- eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
- eventStore.getOrCreateLocusEventHistory(LOCUS_ID).addEvent(mE2);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+ .addEvent(mE2);
EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
@@ -96,9 +98,10 @@ public final class PackageDataTest {
mIsDefaultDialer = true;
mIsDefaultSmsApp = true;
EventStore eventStore = mPackageData.getEventStore();
- eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
- eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
- eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
assertTrue(mPackageData.isDefaultDialer());
assertTrue(mPackageData.isDefaultSmsApp());
@@ -113,9 +116,10 @@ public final class PackageDataTest {
@Test
public void testGetEventHistoryNotDefaultDialerOrSmsApp() {
EventStore eventStore = mPackageData.getEventStore();
- eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
- eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
- eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
assertFalse(mPackageData.isDefaultDialer());
assertFalse(mPackageData.isDefaultSmsApp());
@@ -125,6 +129,61 @@ public final class PackageDataTest {
assertEventEquals(mE1, events.get(0));
}
+ @Test
+ public void testDeleteConversationData() {
+ mIsDefaultDialer = true;
+ mIsDefaultSmsApp = true;
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+ .addEvent(mE2);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
+
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(4, events.size());
+
+ mPackageData.deleteDataForConversation(SHORTCUT_ID);
+
+ eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertTrue(events.isEmpty());
+ }
+
+ @Test
+ public void testPruneOrphanEvents() {
+ mIsDefaultDialer = true;
+ mIsDefaultSmsApp = true;
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+ .addEvent(mE2);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
+
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(4, events.size());
+
+ ConversationInfo conversationInfo = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .setLocusId(null)
+ .setContactUri(null)
+ .setContactPhoneNumber(null)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .build();
+ mPackageData.getConversationStore().addOrUpdate(conversationInfo);
+ mPackageData.pruneOrphanEvents();
+ eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ // Only the shortcut based event is kept. All the other events are deleted.
+ assertEventEquals(mE1, events.get(0));
+ }
+
private void assertEventEquals(Event expected, Event actual) {
assertEquals(expected.getTimestamp(), actual.getTimestamp());
assertEquals(expected.getType(), actual.getType());
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 01d9dc00cf47..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) {
@@ -288,14 +288,13 @@ public final class UsageStatsQueryHelperTest {
@Override
@NonNull
- EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
- return mShortcutEventHistory;
- }
-
- @Override
- @NonNull
- EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
- return mLocusEventHistory;
+ EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+ if (category == EventStore.CATEGORY_SHORTCUT_BASED) {
+ return mShortcutEventHistory;
+ } else if (category == EventStore.CATEGORY_LOCUS_ID_BASED) {
+ return mLocusEventHistory;
+ }
+ throw new UnsupportedOperationException();
}
}
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/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 9647178e6f9b..e8c0362c9f32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -144,4 +144,33 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
anyInt() /* reason */, anyString() /* packageName */);
verify(taskChangeNotifier, never()).notifyActivityDismissingDockedStack();
}
+
+ /**
+ * Ensures that notify focus task changes.
+ */
+ @Test
+ public void testNotifyTaskFocusChanged() {
+ final ActivityRecord fullScreenActivityA = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final Task taskA = fullScreenActivityA.getTask();
+
+ final TaskChangeNotificationController taskChangeNotifier =
+ mService.getTaskChangeNotificationController();
+ spyOn(taskChangeNotifier);
+
+ mService.setResumedActivityUncheckLocked(fullScreenActivityA, "resumeA");
+ verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
+ eq(true) /* focused */);
+ reset(taskChangeNotifier);
+
+ final ActivityRecord fullScreenActivityB = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final Task taskB = fullScreenActivityB.getTask();
+
+ mService.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
+ verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
+ eq(false) /* focused */);
+ verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
+ eq(true) /* focused */);
+ }
}
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/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/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);
}
/**