Merge "SystemUI Split via TaskOrganizer"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 50d23ad2..4616ced 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -197,20 +197,17 @@
api_file: "api/module-lib-current.txt",
removed_api_file: "api/module-lib-removed.txt",
},
- // TODO(b/147559833) enable the compatibility check against the last release API
- // and the API lint
- //last_released: {
- // api_file: ":last-released-module-lib-api",
- // removed_api_file: "api/module-lib-removed.txt",
- // baseline_file: ":module-lib-api-incompatibilities-with-last-released"
- //},
- //api_lint: {
- // enabled: true,
- // new_since: ":last-released-module-lib-api",
- // baseline_file: "api/module-lib-lint-baseline.txt",
- //},
+ last_released: {
+ api_file: ":last-released-module-lib-api",
+ removed_api_file: "api/module-lib-removed.txt",
+ baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-module-lib-api",
+ baseline_file: "api/module-lib-lint-baseline.txt",
+ },
},
- //jdiff_enabled: true,
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 088cadb..8a9c774 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 @@
* @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 1072406..7833a03 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 @@
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 e4c6b52..ff7944d 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.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 @@
*/
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 @@
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 @@
}
@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 a775cf5..5fcd774 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 @@
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 1e89158..cf7f380 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 @@
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.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 @@
*/
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 c0f84a0..baa1c25 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -20,6 +20,7 @@
apex_defaults {
native_shared_libs: [
+ "libstatssocket",
"libstatspull",
"libstats_jni",
],
@@ -76,4 +77,4 @@
//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 0290586..6a918d9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2958,7 +2958,7 @@
}
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();
}
@@ -7242,7 +7242,7 @@
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;
}
@@ -7251,7 +7251,7 @@
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 {
@@ -12071,7 +12071,7 @@
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";
@@ -27044,7 +27044,7 @@
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();
@@ -27424,7 +27424,7 @@
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;
}
@@ -27433,8 +27433,8 @@
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 {
@@ -27445,7 +27445,7 @@
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();
@@ -27459,16 +27459,16 @@
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);
@@ -37314,6 +37314,7 @@
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";
@@ -52155,6 +52156,8 @@
field public static final int CONTEXT_CLICK = 6; // 0x6
field public static final int FLAG_IGNORE_GLOBAL_SETTING = 2; // 0x2
field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
+ field public static final int GESTURE_END = 13; // 0xd
+ field public static final int GESTURE_START = 12; // 0xc
field public static final int KEYBOARD_PRESS = 3; // 0x3
field public static final int KEYBOARD_RELEASE = 7; // 0x7
field public static final int KEYBOARD_TAP = 3; // 0x3
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..6e59596
--- /dev/null
+++ b/api/module-lib-lint-baseline.txt
@@ -0,0 +1,33 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+ Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+ Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+ Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+ Managers must always be obtained from Context; no direct constructors
+
+
+PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrections:
+ Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
+ Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+
+
+StaticUtils: android.net.TetheringConstants:
+ Fully-static utility classes must not have constructor
diff --git a/api/system-current.txt b/api/system-current.txt
index 8725538..55460bb 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7598,7 +7598,7 @@
field @Deprecated public int numAssociation;
field @Deprecated public int numScorerOverride;
field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
- field @Deprecated public boolean requirePMF;
+ field @Deprecated public boolean requirePmf;
field @Deprecated @Nullable public String saePasswordId;
field @Deprecated public boolean shared;
field @Deprecated public boolean useExternalScores;
@@ -9680,6 +9680,7 @@
field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
+ field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -14063,6 +14064,14 @@
}
+package android.view.inline {
+
+ public final class InlinePresentationSpec implements android.os.Parcelable {
+ method @Nullable public String getStyle();
+ }
+
+}
+
package android.webkit {
public abstract class CookieManager {
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index dfb0d74..f440381 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -206,7 +206,7 @@
MutableBareField: android.net.wifi.WifiConfiguration#meteredOverride:
-MutableBareField: android.net.wifi.WifiConfiguration#requirePMF:
+MutableBareField: android.net.wifi.WifiConfiguration#requirePmf:
MutableBareField: android.net.wifi.WifiConfiguration#saePasswordId:
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 956fd29..1bcf44e 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -120,13 +120,14 @@
"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 23a4437..27c4f4e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8000,6 +8000,18 @@
// of this pull. If this number grows prohibitively large, then this can
// cause jank due to resource contention.
optional int32 event_connection_count = 6;
+ // Set of timings measured from when SurfaceFlinger began compositing a
+ // frame, until the frame was requested to be presented to the display. This
+ // measures SurfaceFlinger's total CPU walltime on the critical path per
+ // frame.
+ optional FrameTimingHistogram frame_duration = 7
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when SurfaceFlinger first began using the
+ // GPU to composite a frame, until the GPU has finished compositing that
+ // frame. This measures the total additional time SurfaceFlinger needed to
+ // perform due to falling back into GPU composition.
+ optional FrameTimingHistogram render_engine_timing = 8
+ [(android.os.statsd.log_mode) = MODE_BYTES];
}
/**
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 1bac19e..4899b4a 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -690,14 +690,16 @@
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/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 0ed6b1f..b7a35f7 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -29,6 +29,7 @@
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.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;
@@ -600,8 +602,12 @@
"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.
@@ -1920,6 +1926,8 @@
* 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.
*/
@@ -1941,14 +1949,11 @@
}
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) {
@@ -2361,41 +2366,38 @@
}
/**
- * 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() {
@@ -2405,7 +2407,8 @@
/**
* 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 82c7635..c1e2195 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -551,11 +551,6 @@
private int mHtmlDescriptionRes;
/**
- * Non localized html description of the accessibility service.
- */
- private String mNonLocalizedHtmlDescription;
-
- /**
* Creates a new instance.
*/
public AccessibilityServiceInfo() {
@@ -683,10 +678,6 @@
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 @@
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
if (mHtmlDescriptionRes == 0) {
- return mNonLocalizedHtmlDescription;
+ return null;
}
final ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
@@ -1017,7 +1008,6 @@
parcel.writeInt(mAnimatedImageRes);
parcel.writeInt(mHtmlDescriptionRes);
parcel.writeString(mNonLocalizedDescription);
- parcel.writeString(mNonLocalizedHtmlDescription);
}
private void initFromParcel(Parcel parcel) {
@@ -1039,7 +1029,6 @@
mAnimatedImageRes = parcel.readInt();
mHtmlDescriptionRes = parcel.readInt();
mNonLocalizedDescription = parcel.readString();
- mNonLocalizedHtmlDescription = parcel.readString();
}
@Override
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2319dd2..395a196 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -126,6 +126,7 @@
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillPopupWindow;
import android.view.autofill.IAutofillWindowPresenter;
+import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.widget.AdapterView;
@@ -1056,7 +1057,10 @@
} catch (RemoteException re) {
re.rethrowFromSystemServer();
}
- // TODO(b/147750355): Pass locusId and bundle to the Content Capture.
+ // If locusId is not null pass it to the Content Capture.
+ if (locusId != null) {
+ setLocusContextToContentCapture(locusId, bundle);
+ }
}
/** Return the application that owns this activity. */
@@ -1209,6 +1213,19 @@
}
}
+ private void setLocusContextToContentCapture(LocusId locusId, @Nullable Bundle bundle) {
+ final ContentCaptureManager cm = getContentCaptureManager();
+ if (cm == null) return;
+
+ ContentCaptureContext.Builder contentCaptureContextBuilder =
+ new ContentCaptureContext.Builder(locusId);
+ if (bundle != null) {
+ contentCaptureContextBuilder.setExtras(bundle);
+ }
+ cm.getMainContentCaptureSession().setContentCaptureContext(
+ contentCaptureContextBuilder.build());
+ }
+
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9aa6b87..526c0b3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -162,6 +162,7 @@
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 06288c0..37bdda0 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -204,4 +204,12 @@
* @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/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 343b386..da0aadb 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@
@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 ed74779..954db04 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -53,17 +53,17 @@
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 @@
}
/**
- * 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 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 @@
}
/**
- * 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 @@
@NonNull
public FactoryResetProtectionPolicy build() {
return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
- mFactoryResetProtectionDisabled);
+ mFactoryResetProtectionEnabled);
}
}
@@ -144,7 +146,7 @@
public String toString() {
return "FactoryResetProtectionPolicy{"
+ "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
- + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled
+ + ", mFactoryResetProtectionEnabled=" + mFactoryResetProtectionEnabled
+ '}';
}
@@ -155,7 +157,7 @@
for (String account: mFactoryResetProtectionAccounts) {
dest.writeString(account);
}
- dest.writeBoolean(mFactoryResetProtectionDisabled);
+ dest.writeBoolean(mFactoryResetProtectionEnabled);
}
@Override
@@ -173,10 +175,10 @@
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 @@
@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 @@
}
return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
- factoryResetProtectionDisabled);
+ factoryResetProtectionEnabled);
} catch (XmlPullParserException | IOException e) {
Log.w(LOG_TAG, "Reading from xml failed", e);
}
@@ -225,8 +227,8 @@
* @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/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ae14748..7b484b7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1988,10 +1988,11 @@
/**
* 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)
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index db16d24..1ed791d 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -54,7 +54,7 @@
*/
@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/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 49e1d5e..a95938b 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -789,6 +789,7 @@
Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
requestCallback.onInlineSuggestionsUnsupported();
} else {
+ request.setHostInputToken(getHostInputToken());
final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
new InlineSuggestionsResponseCallbackImpl(this,
mInlineSuggestionsRequestInfo.mComponentName,
@@ -833,6 +834,18 @@
onInlineSuggestionsResponse(response);
}
+ /**
+ * Returns the {@link IBinder} input token from the host view root.
+ */
+ @Nullable
+ private IBinder getHostInputToken() {
+ ViewRootImpl viewRoot = null;
+ if (mRootView != null) {
+ viewRoot = mRootView.getViewRootImpl();
+ }
+ return viewRoot == null ? null : viewRoot.getInputToken();
+ }
+
private void notifyImeHidden() {
setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
onPreRenderedWindowVisibilityChanged(false /* setVisible */);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bb1dafc..8d04df0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -215,6 +215,20 @@
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 37efec3..a8f88d4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2055,6 +2055,19 @@
public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
/**
+ * Activity Action: Show screen that let user select enable (or disable) tethering.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
+
+ /**
* Broadcast to trigger notification of asking user to enable MMS.
* Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
*
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index decdcf5..c389b1a 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -16,6 +16,7 @@
package android.service.autofill;
+import android.os.IBinder;
import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
@@ -25,6 +26,7 @@
* @hide
*/
oneway interface IInlineSuggestionRenderService {
- void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation,
- int width, int height);
+ void renderSuggestion(in IInlineSuggestionUiCallback callback,
+ in InlinePresentation presentation, int width, int height,
+ in IBinder hostInputToken);
}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index a55a2ce..210f95f 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -16,6 +16,7 @@
package android.service.autofill;
+import android.os.IBinder;
import android.view.SurfaceControl;
/**
@@ -24,6 +25,8 @@
* @hide
*/
oneway interface IInlineSuggestionUiCallback {
- void autofill();
+ void onAutofill();
void onContent(in SurfaceControl surface);
+ void onError();
+ void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 2593aab..4aafb63 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -24,11 +24,16 @@
import android.app.Service;
import android.app.slice.Slice;
import android.content.Intent;
+import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.WindowManager;
/**
* A service that renders an inline presentation given the {@link InlinePresentation} containing
@@ -55,8 +60,40 @@
private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
- InlinePresentation presentation, int width, int height) {
- //TODO(b/146453086): implementation in ExtService
+ InlinePresentation presentation, int width, int height, IBinder hostInputToken) {
+ if (hostInputToken == null) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onError()");
+ }
+ return;
+ }
+ final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(),
+ hostInputToken);
+ final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
+
+ final View suggestionView = onRenderSuggestion(presentation, width, height);
+
+ final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
+ suggestionRoot.addView(suggestionView);
+ suggestionRoot.setOnClickListener((v) -> {
+ try {
+ callback.onAutofill();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onAutofill()");
+ }
+ });
+
+ WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+ host.addView(suggestionRoot, lp);
+ try {
+ callback.onContent(surface);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
+ }
}
@Override
@@ -66,11 +103,12 @@
return new IInlineSuggestionRenderService.Stub() {
@Override
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
- @NonNull InlinePresentation presentation, int width, int height) {
+ @NonNull InlinePresentation presentation, int width, int height,
+ @Nullable IBinder hostInputToken) {
mHandler.sendMessage(obtainMessage(
InlineSuggestionRenderService::handleRenderSuggestion,
InlineSuggestionRenderService.this, callback, presentation,
- width, height));
+ width, height, hostInputToken));
}
}.asBinder();
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
similarity index 65%
rename from services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
rename to core/java/android/service/autofill/InlineSuggestionRoot.java
index e813dae..bdcc253 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -14,39 +14,39 @@
* limitations under the License.
*/
-package com.android.server.autofill.ui;
+package android.service.autofill;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.RemoteException;
import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
-import com.android.server.LocalServices;
-import com.android.server.wm.WindowManagerInternal;
-
/**
* This class is the root view for an inline suggestion. It is responsible for
* detecting the click on the item and to also transfer input focus to the IME
* window if we detect the user is scrolling.
+ *
+ * @hide
*/
- // TODO(b/146453086) Move to ExtServices and add @SystemApi to transfer touch focus
@SuppressLint("ViewConstructor")
-class InlineSuggestionRoot extends FrameLayout {
- private static final String LOG_TAG = InlineSuggestionRoot.class.getSimpleName();
+public class InlineSuggestionRoot extends FrameLayout {
+ private static final String TAG = "InlineSuggestionRoot";
- private final @NonNull Runnable mOnErrorCallback;
+ private final @NonNull IInlineSuggestionUiCallback mCallback;
private final int mTouchSlop;
private float mDownX;
private float mDownY;
- InlineSuggestionRoot(@NonNull Context context, @NonNull Runnable onErrorCallback) {
+ public InlineSuggestionRoot(@NonNull Context context,
+ @NonNull IInlineSuggestionUiCallback callback) {
super(context);
- mOnErrorCallback = onErrorCallback;
+ mCallback = callback;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setFocusable(false);
}
@@ -64,20 +64,15 @@
final float distance = MathUtils.dist(mDownX, mDownY,
event.getX(), event.getY());
if (distance > mTouchSlop) {
- transferTouchFocusToImeWindow();
+ try {
+ mCallback.onTransferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
+ getContext().getDisplayId());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException transferring touch focus to IME");
+ }
}
} break;
}
return super.onTouchEvent(event);
}
-
- private void transferTouchFocusToImeWindow() {
- final WindowManagerInternal windowManagerInternal = LocalServices.getService(
- WindowManagerInternal.class);
- if (!windowManagerInternal.transferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
- getContext().getDisplayId())) {
- Log.e(LOG_TAG, "Cannot transfer touch focus from suggestion to IME");
- mOnErrorCallback.run();
- }
- }
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index afeb6c3..44e7ae6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -79,6 +79,7 @@
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/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 37b9eb3..c62e934 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -90,13 +90,11 @@
/**
* The user has started a gesture (e.g. on the soft keyboard).
- * @hide
*/
public static final int GESTURE_START = 12;
/**
* The user has finished a gesture (e.g. on the soft keyboard).
- * @hide
*/
public static final int GESTURE_END = 13;
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 0c1edac..31fdfb7 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -16,518 +16,52 @@
package android.view;
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
-import android.graphics.RecordingCanvas;
-import android.graphics.RenderNode;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.SparseIntArray;
-
-import com.android.internal.util.VirtualRefBasePtr;
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-
-import java.util.ArrayList;
/**
* @hide
*/
-public class RenderNodeAnimator extends Animator {
- // Keep in sync with enum RenderProperty in Animator.h
- public static final int TRANSLATION_X = 0;
- public static final int TRANSLATION_Y = 1;
- public static final int TRANSLATION_Z = 2;
- public static final int SCALE_X = 3;
- public static final int SCALE_Y = 4;
- public static final int ROTATION = 5;
- public static final int ROTATION_X = 6;
- public static final int ROTATION_Y = 7;
- public static final int X = 8;
- public static final int Y = 9;
- public static final int Z = 10;
- public static final int ALPHA = 11;
- // The last value in the enum, used for array size initialization
- public static final int LAST_VALUE = ALPHA;
+public class RenderNodeAnimator extends android.graphics.animation.RenderNodeAnimator
+ implements android.graphics.animation.RenderNodeAnimator.ViewListener {
- // Keep in sync with enum PaintFields in Animator.h
- public static final int PAINT_STROKE_WIDTH = 0;
-
- /**
- * Field for the Paint alpha channel, which should be specified as a value
- * between 0 and 255.
- */
- public static final int PAINT_ALPHA = 1;
-
- // ViewPropertyAnimator uses a mask for its values, we need to remap them
- // to the enum values here. RenderPropertyAnimator can't use the mask values
- // directly as internally it uses a lookup table so it needs the values to
- // be sequential starting from 0
- private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{
- put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X);
- put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y);
- put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z);
- put(ViewPropertyAnimator.SCALE_X, SCALE_X);
- put(ViewPropertyAnimator.SCALE_Y, SCALE_Y);
- put(ViewPropertyAnimator.ROTATION, ROTATION);
- put(ViewPropertyAnimator.ROTATION_X, ROTATION_X);
- put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y);
- put(ViewPropertyAnimator.X, X);
- put(ViewPropertyAnimator.Y, Y);
- put(ViewPropertyAnimator.Z, Z);
- put(ViewPropertyAnimator.ALPHA, ALPHA);
- }};
-
- private VirtualRefBasePtr mNativePtr;
-
- private Handler mHandler;
- private RenderNode mTarget;
private View mViewTarget;
- private int mRenderProperty = -1;
- private float mFinalValue;
- private TimeInterpolator mInterpolator;
-
- private static final int STATE_PREPARE = 0;
- private static final int STATE_DELAYED = 1;
- private static final int STATE_RUNNING = 2;
- private static final int STATE_FINISHED = 3;
- private int mState = STATE_PREPARE;
-
- private long mUnscaledDuration = 300;
- private long mUnscaledStartDelay = 0;
- // If this is true, we will run any start delays on the UI thread. This is
- // the safe default, and is necessary to ensure start listeners fire at
- // the correct time. Animators created by RippleDrawable (the
- // CanvasProperty<> ones) do not have this expectation, and as such will
- // set this to false so that the renderthread handles the startdelay instead
- private final boolean mUiThreadHandlesDelay;
- private long mStartDelay = 0;
- private long mStartTime;
-
- public static int mapViewPropertyToRenderProperty(int viewProperty) {
- return sViewPropertyAnimatorMap.get(viewProperty);
- }
public RenderNodeAnimator(int property, float finalValue) {
- mRenderProperty = property;
- mFinalValue = finalValue;
- mUiThreadHandlesDelay = true;
- init(nCreateAnimator(property, finalValue));
+ super(property, finalValue);
}
public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
- init(nCreateCanvasPropertyFloatAnimator(
- property.getNativeContainer(), finalValue));
- mUiThreadHandlesDelay = false;
+ super(property, finalValue);
}
- /**
- * Creates a new render node animator for a field on a Paint property.
- *
- * @param property The paint property to target
- * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
- * {@link #PAINT_STROKE_WIDTH}
- * @param finalValue The target value for the property
- */
public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
- init(nCreateCanvasPropertyPaintAnimator(
- property.getNativeContainer(), paintField, finalValue));
- mUiThreadHandlesDelay = false;
+ super(property, paintField, finalValue);
}
public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
- init(nCreateRevealAnimator(x, y, startRadius, endRadius));
- mUiThreadHandlesDelay = true;
- }
-
- private void init(long ptr) {
- mNativePtr = new VirtualRefBasePtr(ptr);
- }
-
- private void checkMutable() {
- if (mState != STATE_PREPARE) {
- throw new IllegalStateException("Animator has already started, cannot change it now!");
- }
- if (mNativePtr == null) {
- throw new IllegalStateException("Animator's target has been destroyed "
- + "(trying to modify an animation after activity destroy?)");
- }
- }
-
- static boolean isNativeInterpolator(TimeInterpolator interpolator) {
- return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
- }
-
- private void applyInterpolator() {
- if (mInterpolator == null || mNativePtr == null) return;
-
- long ni;
- if (isNativeInterpolator(mInterpolator)) {
- ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator();
- } else {
- long duration = nGetDuration(mNativePtr.get());
- ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
- }
- nSetInterpolator(mNativePtr.get(), ni);
+ super(x, y, startRadius, endRadius);
}
@Override
- public void start() {
- if (mTarget == null) {
- throw new IllegalStateException("Missing target!");
- }
-
- if (mState != STATE_PREPARE) {
- throw new IllegalStateException("Already started!");
- }
-
- mState = STATE_DELAYED;
- if (mHandler == null) {
- mHandler = new Handler(true);
- }
- applyInterpolator();
-
- if (mNativePtr == null) {
- // It's dead, immediately cancel
- cancel();
- } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
- nSetStartDelay(mNativePtr.get(), mStartDelay);
- doStart();
- } else {
- getHelper().addDelayedAnimation(this);
- }
- }
-
- private void doStart() {
+ public void onAlphaAnimationStart(float finalAlpha) {
// Alpha is a special snowflake that has the canonical value stored
// in mTransformationInfo instead of in RenderNode, so we need to update
// it with the final value here.
- if (mRenderProperty == RenderNodeAnimator.ALPHA) {
- mViewTarget.ensureTransformationInfo();
- mViewTarget.setAlphaInternal(mFinalValue);
- }
-
- moveToRunningState();
-
- if (mViewTarget != null) {
- // Kick off a frame to start the process
- mViewTarget.invalidateViewProperty(true, false);
- }
- }
-
- private void moveToRunningState() {
- mState = STATE_RUNNING;
- if (mNativePtr != null) {
- nStart(mNativePtr.get());
- }
- notifyStartListeners();
- }
-
- private void notifyStartListeners() {
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationStart(this);
- }
+ mViewTarget.ensureTransformationInfo();
+ mViewTarget.setAlphaInternal(finalAlpha);
}
@Override
- public void cancel() {
- if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
- if (mState == STATE_DELAYED) {
- getHelper().removeDelayedAnimation(this);
- moveToRunningState();
- }
-
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationCancel(this);
- }
-
- end();
- }
- }
-
- @Override
- public void end() {
- if (mState != STATE_FINISHED) {
- if (mState < STATE_RUNNING) {
- getHelper().removeDelayedAnimation(this);
- doStart();
- }
- if (mNativePtr != null) {
- nEnd(mNativePtr.get());
- if (mViewTarget != null) {
- // Kick off a frame to flush the state change
- mViewTarget.invalidateViewProperty(true, false);
- }
- } else {
- // It's already dead, jump to onFinish
- onFinished();
- }
- }
- }
-
- @Override
- public void pause() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void resume() {
- throw new UnsupportedOperationException();
+ public void invalidateParent(boolean forceRedraw) {
+ mViewTarget.invalidateViewProperty(true, false);
}
/** @hide */
- public void setTarget(View view) {
+ public void setTarget(@NonNull View view) {
mViewTarget = view;
+ setViewListener(this);
setTarget(mViewTarget.mRenderNode);
}
-
- /** Sets the animation target to the owning view of the RecordingCanvas */
- public void setTarget(RecordingCanvas canvas) {
- setTarget(canvas.mNode);
- }
-
- /** @hide */
- public void setTarget(DisplayListCanvas canvas) {
- setTarget((RecordingCanvas) canvas);
- }
-
- private void setTarget(RenderNode node) {
- checkMutable();
- if (mTarget != null) {
- throw new IllegalStateException("Target already set!");
- }
- nSetListener(mNativePtr.get(), this);
- mTarget = node;
- mTarget.addAnimator(this);
- }
-
- public void setStartValue(float startValue) {
- checkMutable();
- nSetStartValue(mNativePtr.get(), startValue);
- }
-
- @Override
- public void setStartDelay(long startDelay) {
- checkMutable();
- if (startDelay < 0) {
- throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
- }
- mUnscaledStartDelay = startDelay;
- mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
- }
-
- @Override
- public long getStartDelay() {
- return mUnscaledStartDelay;
- }
-
- @Override
- public RenderNodeAnimator setDuration(long duration) {
- checkMutable();
- if (duration < 0) {
- throw new IllegalArgumentException("duration must be positive; " + duration);
- }
- mUnscaledDuration = duration;
- nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
- return this;
- }
-
- @Override
- public long getDuration() {
- return mUnscaledDuration;
- }
-
- @Override
- public long getTotalDuration() {
- return mUnscaledDuration + mUnscaledStartDelay;
- }
-
- @Override
- public boolean isRunning() {
- return mState == STATE_DELAYED || mState == STATE_RUNNING;
- }
-
- @Override
- public boolean isStarted() {
- return mState != STATE_PREPARE;
- }
-
- @Override
- public void setInterpolator(TimeInterpolator interpolator) {
- checkMutable();
- mInterpolator = interpolator;
- }
-
- @Override
- public TimeInterpolator getInterpolator() {
- return mInterpolator;
- }
-
- protected void onFinished() {
- if (mState == STATE_PREPARE) {
- // Unlikely but possible, the native side has been destroyed
- // before we have started.
- releaseNativePtr();
- return;
- }
- if (mState == STATE_DELAYED) {
- getHelper().removeDelayedAnimation(this);
- notifyStartListeners();
- }
- mState = STATE_FINISHED;
-
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationEnd(this);
- }
-
- // Release the native object, as it has a global reference to us. This
- // breaks the cyclic reference chain, and allows this object to be
- // GC'd
- releaseNativePtr();
- }
-
- private void releaseNativePtr() {
- if (mNativePtr != null) {
- mNativePtr.release();
- mNativePtr = null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private ArrayList<AnimatorListener> cloneListeners() {
- ArrayList<AnimatorListener> listeners = getListeners();
- if (listeners != null) {
- listeners = (ArrayList<AnimatorListener>) listeners.clone();
- }
- return listeners;
- }
-
- public long getNativeAnimator() {
- return mNativePtr.get();
- }
-
- /**
- * @return true if the animator was started, false if still delayed
- */
- private boolean processDelayed(long frameTimeMs) {
- if (mStartTime == 0) {
- mStartTime = frameTimeMs;
- } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
- doStart();
- return true;
- }
- return false;
- }
-
- private static DelayedAnimationHelper getHelper() {
- DelayedAnimationHelper helper = sAnimationHelper.get();
- if (helper == null) {
- helper = new DelayedAnimationHelper();
- sAnimationHelper.set(helper);
- }
- return helper;
- }
-
- private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
- new ThreadLocal<DelayedAnimationHelper>();
-
- private static class DelayedAnimationHelper implements Runnable {
-
- private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
- private final Choreographer mChoreographer;
- private boolean mCallbackScheduled;
-
- public DelayedAnimationHelper() {
- mChoreographer = Choreographer.getInstance();
- }
-
- public void addDelayedAnimation(RenderNodeAnimator animator) {
- mDelayedAnims.add(animator);
- scheduleCallback();
- }
-
- public void removeDelayedAnimation(RenderNodeAnimator animator) {
- mDelayedAnims.remove(animator);
- }
-
- private void scheduleCallback() {
- if (!mCallbackScheduled) {
- mCallbackScheduled = true;
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
- }
- }
-
- @Override
- public void run() {
- long frameTimeMs = mChoreographer.getFrameTime();
- mCallbackScheduled = false;
-
- int end = 0;
- for (int i = 0; i < mDelayedAnims.size(); i++) {
- RenderNodeAnimator animator = mDelayedAnims.get(i);
- if (!animator.processDelayed(frameTimeMs)) {
- if (end != i) {
- mDelayedAnims.set(end, animator);
- }
- end++;
- }
- }
- while (mDelayedAnims.size() > end) {
- mDelayedAnims.remove(mDelayedAnims.size() - 1);
- }
-
- if (mDelayedAnims.size() > 0) {
- scheduleCallback();
- }
- }
- }
-
- // Called by native
- private static void callOnFinished(RenderNodeAnimator animator) {
- if (animator.mHandler != null) {
- animator.mHandler.post(animator::onFinished);
- } else {
- new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
- }
- }
-
- @Override
- public Animator clone() {
- throw new IllegalStateException("Cannot clone this animator");
- }
-
- @Override
- public void setAllowRunningAsynchronously(boolean mayRunAsync) {
- checkMutable();
- nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
- }
-
- private static native long nCreateAnimator(int property, float finalValue);
- private static native long nCreateCanvasPropertyFloatAnimator(
- long canvasProperty, float finalValue);
- private static native long nCreateCanvasPropertyPaintAnimator(
- long canvasProperty, int paintField, float finalValue);
- private static native long nCreateRevealAnimator(
- int x, int y, float startRadius, float endRadius);
-
- private static native void nSetStartValue(long nativePtr, float startValue);
- private static native void nSetDuration(long nativePtr, long duration);
- private static native long nGetDuration(long nativePtr);
- private static native void nSetStartDelay(long nativePtr, long startDelay);
- private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
- private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
- private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
-
- private static native void nStart(long animPtr);
- private static native void nEnd(long animPtr);
}
diff --git a/core/java/android/view/RenderNodeAnimatorSetHelper.java b/core/java/android/view/RenderNodeAnimatorSetHelper.java
deleted file mode 100644
index d222e07..0000000
--- a/core/java/android/view/RenderNodeAnimatorSetHelper.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view;
-
-import android.animation.TimeInterpolator;
-import android.graphics.RecordingCanvas;
-import android.graphics.RenderNode;
-
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
-/**
- * This is a helper class to get access to methods and fields needed for RenderNodeAnimatorSet
- * that are internal or package private to android.view package.
- *
- * @hide
- */
-public class RenderNodeAnimatorSetHelper {
-
- /** checkstyle @hide */
- public static RenderNode getTarget(RecordingCanvas recordingCanvas) {
- return recordingCanvas.mNode;
- }
-
- /** checkstyle @hide */
- public static long createNativeInterpolator(TimeInterpolator interpolator, long
- duration) {
- if (interpolator == null) {
- // create LinearInterpolator
- return NativeInterpolatorFactoryHelper.createLinearInterpolator();
- } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
- return ((NativeInterpolatorFactory)interpolator).createNativeInterpolator();
- } else {
- return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
- }
- }
-
-}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index bf84819..680a878 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -24,6 +24,7 @@
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 @@
private WindowlessWindowManager mWm;
private SurfaceControl mSurfaceControl;
+ private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
/**
* Package encapsulating a Surface hierarchy which contains interactive view
@@ -49,15 +51,18 @@
*/
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 @@
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 @@
@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 @@
@NonNull WindowlessWindowManager wwm) {
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm);
+ mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
/**
@@ -118,6 +135,7 @@
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
+ mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
/**
@@ -128,8 +146,8 @@
* 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 d5ed36b..47ffd3e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -28,6 +28,7 @@
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.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 @@
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 @@
}
mTmpTransaction.apply();
+ updateScreenMatrixForEmbeddedHierarchy();
if (sizeChanged || creating) {
redrawNeeded = true;
@@ -1510,6 +1518,7 @@
@Override
public void surfaceDestroyed() {
setWindowStopped(true);
+ setRemoteAccessibilityEmbeddedConnection(null, null);
}
/**
@@ -1568,31 +1577,133 @@
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 857bc50..b971a20 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -655,7 +655,7 @@
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
- private IAccessibilityEmbeddedConnection mEmbeddedConnection;
+ private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
static final class SystemUiVisibilityInfo {
int seq;
@@ -9370,11 +9370,12 @@
* 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/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
index 21d5a5b..a2bbc5c 100644
--- a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
@@ -17,19 +17,18 @@
package android.view.animation;
import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
- implements NativeInterpolatorFactory {
+ implements NativeInterpolator {
public AccelerateDecelerateInterpolator() {
}
@@ -44,6 +43,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
+ return NativeInterpolatorFactory.createAccelerateDecelerateInterpolator();
}
}
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
index 6c8d7b1..9d4cd32 100644
--- a/core/java/android/view/animation/AccelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -20,12 +20,12 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change starts out slowly and
@@ -33,7 +33,7 @@
*
*/
@HasNativeInterpolator
-public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
private final float mFactor;
private final double mDoubleFactor;
@@ -85,6 +85,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
+ return NativeInterpolatorFactory.createAccelerateInterpolator(mFactor);
}
}
diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java
index 7a837c3..d146394 100644
--- a/core/java/android/view/animation/AnticipateInterpolator.java
+++ b/core/java/android/view/animation/AnticipateInterpolator.java
@@ -20,18 +20,18 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the change starts backward then flings forward.
*/
@HasNativeInterpolator
-public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolator {
private final float mTension;
public AnticipateInterpolator() {
@@ -73,6 +73,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAnticipateInterpolator(mTension);
+ return NativeInterpolatorFactory.createAnticipateInterpolator(mTension);
}
}
diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
index 9a75134..4d6a390 100644
--- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java
+++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
@@ -24,11 +24,11 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the change starts backward then flings forward and overshoots
@@ -36,7 +36,7 @@
*/
@HasNativeInterpolator
public class AnticipateOvershootInterpolator extends BaseInterpolator
- implements NativeInterpolatorFactory {
+ implements NativeInterpolator {
private final float mTension;
public AnticipateOvershootInterpolator() {
@@ -103,6 +103,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAnticipateOvershootInterpolator(mTension);
+ return NativeInterpolatorFactory.createAnticipateOvershootInterpolator(mTension);
}
}
diff --git a/core/java/android/view/animation/BounceInterpolator.java b/core/java/android/view/animation/BounceInterpolator.java
index 909eaa4..d3f6a3f 100644
--- a/core/java/android/view/animation/BounceInterpolator.java
+++ b/core/java/android/view/animation/BounceInterpolator.java
@@ -17,17 +17,16 @@
package android.view.animation;
import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
/**
* An interpolator where the change bounces at the end.
*/
@HasNativeInterpolator
-public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class BounceInterpolator extends BaseInterpolator implements NativeInterpolator {
public BounceInterpolator() {
}
@@ -56,6 +55,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createBounceInterpolator();
+ return NativeInterpolatorFactory.createBounceInterpolator();
}
}
\ No newline at end of file
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
index 72d64a1..6b1a80a 100644
--- a/core/java/android/view/animation/CycleInterpolator.java
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -20,12 +20,12 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* Repeats the animation for a specified number of cycles. The
@@ -33,7 +33,7 @@
*
*/
@HasNativeInterpolator
-public class CycleInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class CycleInterpolator extends BaseInterpolator implements NativeInterpolator {
public CycleInterpolator(float cycles) {
mCycles = cycles;
}
@@ -65,6 +65,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createCycleInterpolator(mCycles);
+ return NativeInterpolatorFactory.createCycleInterpolator(mCycles);
}
}
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 2d1249d..2d2f770 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -20,12 +20,12 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change starts out quickly and
@@ -33,7 +33,7 @@
*
*/
@HasNativeInterpolator
-public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
public DecelerateInterpolator() {
}
@@ -81,6 +81,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createDecelerateInterpolator(mFactor);
+ return NativeInterpolatorFactory.createDecelerateInterpolator(mFactor);
}
}
diff --git a/core/java/android/view/animation/LinearInterpolator.java b/core/java/android/view/animation/LinearInterpolator.java
index 2a047b4..f6a820c 100644
--- a/core/java/android/view/animation/LinearInterpolator.java
+++ b/core/java/android/view/animation/LinearInterpolator.java
@@ -17,17 +17,16 @@
package android.view.animation;
import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
-public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {
public LinearInterpolator() {
}
@@ -42,6 +41,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+ return NativeInterpolatorFactory.createLinearInterpolator();
}
}
diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java
index 306688a..e6445d7 100644
--- a/core/java/android/view/animation/OvershootInterpolator.java
+++ b/core/java/android/view/animation/OvershootInterpolator.java
@@ -20,19 +20,19 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the change flings forward and overshoots the last value
* then comes back.
*/
@HasNativeInterpolator
-public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolator {
private final float mTension;
public OvershootInterpolator() {
@@ -76,6 +76,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createOvershootInterpolator(mTension);
+ return NativeInterpolatorFactory.createOvershootInterpolator(mTension);
}
}
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index 924437a..99d6b9c 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -20,14 +20,14 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Path;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import android.util.PathParser;
import android.view.InflateException;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator that can traverse a Path that extends from <code>Point</code>
@@ -46,7 +46,7 @@
* </pre></blockquote></p>
*/
@HasNativeInterpolator
-public class PathInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class PathInterpolator extends BaseInterpolator implements NativeInterpolator {
// This governs how accurate the approximation of the Path is.
private static final float PRECISION = 0.002f;
@@ -237,7 +237,7 @@
/** @hide **/
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createPathInterpolator(mX, mY);
+ return NativeInterpolatorFactory.createPathInterpolator(mX, mY);
}
}
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index a85b5f1..406e599 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcelable;
import android.util.Size;
@@ -55,6 +56,7 @@
/**
* @hide
*/
+ @SystemApi
public @Nullable String getStyle() {
return mStyle;
}
@@ -281,10 +283,10 @@
}
@DataClass.Generated(
- time = 1577145109444L,
+ time = 1581117017522L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
- inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static java.lang.String defaultStyle()\npublic @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static java.lang.String defaultStyle()\npublic @android.annotation.SystemApi @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index a32ea4b..57269c4 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -286,7 +286,7 @@
};
@DataClass.Generated(
- time = 1578972138081L,
+ time = 1581377984320L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 860ce90..be9370a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.os.IBinder;
import android.os.Parcelable;
import android.view.inline.InlinePresentationSpec;
@@ -59,6 +60,14 @@
private @NonNull String mHostPackageName;
/**
+ * The host input token of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
+ */
+ private @Nullable IBinder mHostInputToken;
+
+ /**
* @hide
* @see {@link #mHostPackageName}.
*/
@@ -66,6 +75,14 @@
mHostPackageName = hostPackageName;
}
+ /**
+ * @hide
+ * @see {@link #mHostInputToken}.
+ */
+ public void setHostInputToken(IBinder hostInputToken) {
+ mHostInputToken = hostInputToken;
+ }
+
private void onConstructed() {
Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
}
@@ -78,11 +95,17 @@
return ActivityThread.currentPackageName();
}
+ private static IBinder defaultHostInputToken() {
+ return null;
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
abstract Builder setHostPackageName(@Nullable String value);
+
+ abstract Builder setHostInputToken(IBinder hostInputToken);
}
@@ -104,7 +127,8 @@
/* package-private */ InlineSuggestionsRequest(
int maxSuggestionCount,
@NonNull List<InlinePresentationSpec> presentationSpecs,
- @NonNull String hostPackageName) {
+ @NonNull String hostPackageName,
+ @Nullable IBinder hostInputToken) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -112,6 +136,7 @@
this.mHostPackageName = hostPackageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHostPackageName);
+ this.mHostInputToken = hostInputToken;
onConstructed();
}
@@ -145,6 +170,17 @@
return mHostPackageName;
}
+ /**
+ * The host input token of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable IBinder getHostInputToken() {
+ return mHostInputToken;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -154,7 +190,8 @@
return "InlineSuggestionsRequest { " +
"maxSuggestionCount = " + mMaxSuggestionCount + ", " +
"presentationSpecs = " + mPresentationSpecs + ", " +
- "hostPackageName = " + mHostPackageName +
+ "hostPackageName = " + mHostPackageName + ", " +
+ "hostInputToken = " + mHostInputToken +
" }";
}
@@ -173,7 +210,8 @@
return true
&& mMaxSuggestionCount == that.mMaxSuggestionCount
&& java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
- && java.util.Objects.equals(mHostPackageName, that.mHostPackageName);
+ && java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
+ && java.util.Objects.equals(mHostInputToken, that.mHostInputToken);
}
@Override
@@ -186,6 +224,7 @@
_hash = 31 * _hash + mMaxSuggestionCount;
_hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
_hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
return _hash;
}
@@ -195,9 +234,13 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (mHostInputToken != null) flg |= 0x8;
+ dest.writeByte(flg);
dest.writeInt(mMaxSuggestionCount);
dest.writeParcelableList(mPresentationSpecs, flags);
dest.writeString(mHostPackageName);
+ if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken);
}
@Override
@@ -211,10 +254,12 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
String hostPackageName = in.readString();
+ IBinder hostInputToken = (flg & 0x8) == 0 ? null : in.readStrongBinder();
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
@@ -223,6 +268,7 @@
this.mHostPackageName = hostPackageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHostPackageName);
+ this.mHostInputToken = hostInputToken;
onConstructed();
}
@@ -251,6 +297,7 @@
private int mMaxSuggestionCount;
private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
private @NonNull String mHostPackageName;
+ private @Nullable IBinder mHostInputToken;
private long mBuilderFieldsSet = 0L;
@@ -320,10 +367,25 @@
return this;
}
+ /**
+ * The host input token of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ @Override
+ @NonNull Builder setHostInputToken(@Nullable IBinder value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mHostInputToken = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
+ mBuilderFieldsSet |= 0x10; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -331,15 +393,19 @@
if ((mBuilderFieldsSet & 0x4) == 0) {
mHostPackageName = defaultHostPackageName();
}
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mHostInputToken = defaultHostInputToken();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mPresentationSpecs,
- mHostPackageName);
+ mHostPackageName,
+ mHostInputToken);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -347,10 +413,10 @@
}
@DataClass.Generated(
- time = 1578948035951L,
+ time = 1581555687721L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\npublic void setHostPackageName(java.lang.String)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\npublic void setHostPackageName(java.lang.String)\npublic void setHostInputToken(android.os.IBinder)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.IBinder defaultHostInputToken()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 46b8b77..12f245e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5271,6 +5271,8 @@
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 78e8518..efcd54f 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -61,6 +61,7 @@
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 @@
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 @@
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 @@
*/
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/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 5565864..94924a5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,7 +24,6 @@
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.os.Build;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
@@ -291,7 +290,7 @@
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- int targetSdkVersion, boolean isTopApp, String[] pkgDataInfoList) {
+ boolean isTopApp, String[] pkgDataInfoList) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
@@ -299,8 +298,6 @@
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
pkgDataInfoList);
if (pid == 0) {
- Zygote.disableExecuteOnly(targetSdkVersion);
-
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
@@ -696,8 +693,6 @@
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
args.mPkgDataInfoList);
- disableExecuteOnly(args.mTargetSdkVersion);
-
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
@@ -777,17 +772,6 @@
}
/**
- * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q.
- */
- private static void disableExecuteOnly(int targetSdkVersion) {
- if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) {
- Log.e("Zygote", "Failed to set libraries to read+execute.");
- }
- }
-
- private static native boolean nativeDisableExecuteOnly();
-
- /**
* @return Raw file descriptors for the read-end of USAP reporting pipes.
*/
static int[] getUsapPipeFDs() {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index c91c661..4949811 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -257,8 +257,8 @@
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
- parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion,
- parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList);
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
+ parsedArgs.mPkgDataInfoList);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
deleted file mode 100644
index ebeec40..0000000
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view.animation;
-
-/**
- * Static utility class for constructing native interpolators to keep the
- * JNI simpler
- */
-public final class NativeInterpolatorFactoryHelper {
- private NativeInterpolatorFactoryHelper() {}
-
- public static native long createAccelerateDecelerateInterpolator();
- public static native long createAccelerateInterpolator(float factor);
- public static native long createAnticipateInterpolator(float tension);
- public static native long createAnticipateOvershootInterpolator(float tension);
- public static native long createBounceInterpolator();
- public static native long createCycleInterpolator(float cycles);
- public static native long createDecelerateInterpolator(float factor);
- public static native long createLinearInterpolator();
- public static native long createOvershootInterpolator(float tension);
- public static native long createPathInterpolator(float[] x, float[] y);
- public static native long createLutInterpolator(float[] values);
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 30d0d1d..d1a7d24 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,9 +42,7 @@
"android_util_StringBlock.cpp",
"android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
- "android_view_RenderNodeAnimator.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
- "com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
],
include_dirs: [
@@ -351,6 +349,8 @@
"android/graphics/apex/android_paint.cpp",
"android/graphics/apex/android_region.cpp",
+ "android_graphics_animation_NativeInterpolatorFactory.cpp",
+ "android_graphics_animation_RenderNodeAnimator.cpp",
"android_graphics_Canvas.cpp",
"android_graphics_ColorSpace.cpp",
"android_graphics_drawable_AnimatedVectorDrawable.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 19f6e27..bbc23fe 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -119,13 +119,11 @@
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_InputApplicationHandle(JNIEnv* env);
extern int register_android_view_InputWindowHandle(JNIEnv* env);
-extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_SurfaceControl(JNIEnv* env);
extern int register_android_view_SurfaceSession(JNIEnv* env);
extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
extern int register_android_view_TextureView(JNIEnv* env);
-extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
extern int register_android_database_CursorWindow(JNIEnv* env);
extern int register_android_database_SQLiteConnection(JNIEnv* env);
extern int register_android_database_SQLiteGlobal(JNIEnv* env);
@@ -1461,7 +1459,6 @@
REG_JNI(register_android_os_VintfRuntimeInfo),
REG_JNI(register_android_service_DataLoaderService),
REG_JNI(register_android_view_DisplayEventReceiver),
- REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_InputApplicationHandle),
REG_JNI(register_android_view_InputWindowHandle),
REG_JNI(register_android_view_Surface),
@@ -1469,7 +1466,6 @@
REG_JNI(register_android_view_SurfaceSession),
REG_JNI(register_android_view_CompositionSamplingListener),
REG_JNI(register_android_view_TextureView),
- REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_EGL14),
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 7ceef98..7ee509b 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -65,6 +65,8 @@
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv* env);
extern int register_android_graphics_Region(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
@@ -82,10 +84,8 @@
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
-extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
-extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
#define REG_JNI(name) { name }
struct RegJNIRec {
@@ -130,6 +130,10 @@
{"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+ {"android.graphics.animation.NativeInterpolatorFactory",
+ REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
+ {"android.graphics.animation.RenderNodeAnimator",
+ REG_JNI(register_android_graphics_animation_RenderNodeAnimator)},
{"android.graphics.drawable.AnimatedVectorDrawable",
REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
{"android.graphics.drawable.VectorDrawable",
@@ -151,11 +155,8 @@
{"android.util.Log", REG_JNI(register_android_util_Log)},
{"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
{"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)},
- {"android.view.RenderNodeAnimator", REG_JNI(register_android_view_RenderNodeAnimator)},
{"com.android.internal.util.VirtualRefBasePtr",
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
- {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
- REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
};
// Vector to store the names of classes that need delegates of their native methods
static vector<string> classesToDelegate;
diff --git a/core/jni/android/graphics/apex/jni_runtime.cpp b/core/jni/android/graphics/apex/jni_runtime.cpp
index 1f66153..35c997d 100644
--- a/core/jni/android/graphics/apex/jni_runtime.cpp
+++ b/core/jni/android/graphics/apex/jni_runtime.cpp
@@ -60,6 +60,8 @@
extern int register_android_graphics_Picture(JNIEnv*);
extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
@@ -123,6 +125,8 @@
REG_JNI(register_android_graphics_SurfaceTexture),
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
+ REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_fonts_Font),
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
similarity index 95%
rename from core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
rename to core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
index f4d2e7b..2073ac2 100644
--- a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+++ b/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
@@ -90,7 +90,7 @@
// JNI Glue
// ----------------------------------------------------------------------------
-const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
+const char* const kClassPathName = "android/graphics/animation/NativeInterpolatorFactory";
static const JNINativeMethod gMethods[] = {
{ "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
@@ -106,7 +106,7 @@
{ "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
};
-int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv* env) {
+int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env) {
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_graphics_animation_RenderNodeAnimator.cpp
similarity index 96%
rename from core/jni/android_view_RenderNodeAnimator.cpp
rename to core/jni/android_graphics_animation_RenderNodeAnimator.cpp
index ca32b00..878d4fc 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_graphics_animation_RenderNodeAnimator.cpp
@@ -190,7 +190,7 @@
// JNI Glue
// ----------------------------------------------------------------------------
-const char* const kClassPathName = "android/view/RenderNodeAnimator";
+const char* const kClassPathName = "android/graphics/animation/RenderNodeAnimator";
static const JNINativeMethod gMethods[] = {
{ "nCreateAnimator", "(IF)J", (void*) createAnimator },
@@ -203,12 +203,12 @@
{ "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
{ "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
{ "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
- { "nSetListener", "(JLandroid/view/RenderNodeAnimator;)V", (void*) setListener},
+ { "nSetListener", "(JLandroid/graphics/animation/RenderNodeAnimator;)V", (void*) setListener},
{ "nStart", "(J)V", (void*) start},
{ "nEnd", "(J)V", (void*) end },
};
-int register_android_view_RenderNodeAnimator(JNIEnv* env) {
+int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env) {
sLifecycleChecker.incStrong(0);
gRenderNodeAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
gRenderNodeAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
@@ -216,7 +216,7 @@
gRenderNodeAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
env, gRenderNodeAnimatorClassInfo.clazz, "callOnFinished",
- "(Landroid/view/RenderNodeAnimator;)V");
+ "(Landroid/graphics/animation/RenderNodeAnimator;)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 87be5f6..d8f30e9 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -566,10 +566,10 @@
return (jint) status;
}
-static jint
-android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
-{
- return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
+static jint android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state,
+ jint uid) {
+ return (jint)check_AudioSystem_Command(
+ AudioSystem::setPhoneState((audio_mode_t)state, (uid_t)uid));
}
static jint
@@ -2434,7 +2434,7 @@
(void *)android_media_AudioSystem_getDeviceConnectionState},
{"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I",
(void *)android_media_AudioSystem_handleDeviceConfigChange},
- {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
+ {"setPhoneState", "(II)I", (void *)android_media_AudioSystem_setPhoneState},
{"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
{"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
{"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9fbb8df..d91911c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -49,7 +49,6 @@
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
-#include <link.h>
#include <malloc.h>
#include <mntent.h>
#include <paths.h>
@@ -59,7 +58,6 @@
#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/eventfd.h>
-#include <sys/mman.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -72,25 +70,23 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bionic/malloc.h>
-#include <bionic/page.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-#include <selinux/android.h>
-#include <seccomp_policy.h>
-#include <stats_event_list.h>
#include <processgroup/processgroup.h>
#include <processgroup/sched_policy.h>
+#include <seccomp_policy.h>
+#include <selinux/android.h>
+#include <stats_socket.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
#include "core_jni_helpers.h"
#include <nativehelper/JNIHelp.h>
@@ -1080,7 +1076,7 @@
// Close any logging related FDs before we start evaluating the list of
// file descriptors.
__android_log_close();
- stats_log_close();
+ AStatsSocket_close();
// If this is the first fork for this zygote, create the open FD table. If
// it isn't, we just need to check whether the list of open files has changed
@@ -1651,7 +1647,7 @@
SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
__android_log_close();
- stats_log_close();
+ AStatsSocket_close();
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
@@ -1905,25 +1901,6 @@
UnmountTree("/storage");
}
-static int DisableExecuteOnly(struct dl_phdr_info* info,
- size_t size [[maybe_unused]],
- void* data [[maybe_unused]]) {
- // Search for any execute-only segments and mark them read+execute.
- for (int i = 0; i < info->dlpi_phnum; i++) {
- const auto& phdr = info->dlpi_phdr[i];
- if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) {
- auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr));
- size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz;
- if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) {
- ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len);
- return -1;
- }
- }
- }
- // Return non-zero to exit dl_iterate_phdr.
- return 0;
-}
-
} // anonymous namespace
namespace android {
@@ -2286,14 +2263,6 @@
}
}
-/**
- * @param env Managed runtime environment
- * @return True if disable was successful.
- */
-static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) {
- return dl_iterate_phdr(DisableExecuteOnly, nullptr) == 0;
-}
-
static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) {
auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
BlockSignal(SIGTERM, fail_fn);
@@ -2375,8 +2344,6 @@
{"nativeGetUsapPoolCount", "()I",
(void*)com_android_internal_os_Zygote_nativeGetUsapPoolCount},
{"nativeEmptyUsapPool", "()V", (void*)com_android_internal_os_Zygote_nativeEmptyUsapPool},
- {"nativeDisableExecuteOnly", "()Z",
- (void*)com_android_internal_os_Zygote_nativeDisableExecuteOnly},
{"nativeBlockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeBlockSigTerm},
{"nativeUnblockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeUnblockSigTerm},
{"nativeBoostUsapPriority", "()V",
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index a648831..a4e2193 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -162,4 +162,18 @@
SET_PERSONAL_APPS_SUSPENDED = 135;
SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF = 136;
COMP_TO_ORG_OWNED_PO_MIGRATED = 137;
+ SET_CROSS_PROFILE_PACKAGES = 138;
+ SET_INTERACT_ACROSS_PROFILES_APP_OP = 139;
+ GET_CROSS_PROFILE_PACKAGES = 140;
+ CAN_REQUEST_INTERACT_ACROSS_PROFILES_TRUE = 141;
+ CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_NO_PROFILES = 142;
+ CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_WHITELIST = 143;
+ CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_PERMISSION = 144;
+ CAN_INTERACT_ACROSS_PROFILES_TRUE = 145;
+ CAN_INTERACT_ACROSS_PROFILES_FALSE_PERMISSION = 146;
+ CAN_INTERACT_ACROSS_PROFILES_FALSE_NO_PROFILES = 147;
+ CREATE_CROSS_PROFILE_INTENT = 148;
+ IS_MANAGED_PROFILE = 149;
+ START_ACTIVITY_BY_INTENT = 150;
+ BIND_CROSS_PROFILE_SERVICE = 151;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60b3a19..9dc40c8 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 6959e9c..5ed4c53 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 7d8b8db..7fc041c 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/config.xml b/core/res/res/values/config.xml
index 9282925..bbe9a6d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -59,6 +59,7 @@
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_rotate">rotate</string>
@@ -94,6 +95,7 @@
<string translatable="false" name="status_bar_camera">camera</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
<string translatable="false" name="status_bar_sensors_off">sensors_off</string>
+ <string translatable="false" name="status_bar_screen_record">screen_record</string>
<!-- Flag indicating whether the surface flinger has limited
alpha compositing functionality in hardware. If set, the window
@@ -2830,13 +2832,13 @@
"logout" = Logout the current user
-->
<string-array translatable="false" name="config_globalActionsList">
+ <item>emergency</item>
<item>power</item>
<item>restart</item>
<item>lockdown</item>
<item>logout</item>
<item>bugreport</item>
<item>screenshot</item>
- <item>emergency</item>
</string-array>
<!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5aefe11..500bfe9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2908,6 +2908,7 @@
<java-symbol type="string" name="status_bar_microphone" />
<java-symbol type="string" name="status_bar_camera" />
<java-symbol type="string" name="status_bar_sensors_off" />
+ <java-symbol type="string" name="status_bar_screen_record" />
<!-- Locale picker -->
<java-symbol type="id" name="locale_search_menu" />
@@ -3902,4 +3903,6 @@
<!-- Whether to expand the lock screen user switcher by default -->
<java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
+
+ <java-symbol type="string" name="loading" />
</resources>
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 ce71beb..6d0e58b 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 @@
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 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();
+
+ ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
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);
-
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
@@ -382,9 +379,11 @@
// 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 @@
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 @@
}
@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 @@
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 @@
// 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 @@
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 @@
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 a68b5908..363551b 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.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.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 @@
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 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 @@
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 5f4194a..a7bf488 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 @@
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.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 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();
+
+ ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
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);
-
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource());
waitForIdle();
@@ -249,8 +243,9 @@
};
// 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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 36c8724..2087104 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 @@
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 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 @@
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/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 3835b2d..752695f 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -22,8 +22,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.animation.RenderNodeAnimator;
import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimator;
import android.view.Surface;
import android.view.View;
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
similarity index 89%
rename from core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
rename to graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
index d28ab07..36062c1 100644
--- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
+++ b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.view.animation;
+package android.graphics.animation;
import android.animation.TimeInterpolator;
import android.util.TimeUtils;
@@ -26,14 +26,15 @@
* with {@link HasNativeInterpolator}
*
* This implements TimeInterpolator to allow for easier interop with Animators
+ * @hide
*/
@HasNativeInterpolator
-public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
+public class FallbackLUTInterpolator implements NativeInterpolator, TimeInterpolator {
// If the duration of an animation is more than 300 frames, we cap the sample size to 300.
private static final int MAX_SAMPLE_POINTS = 300;
private TimeInterpolator mSourceInterpolator;
- private final float mLut[];
+ private final float[] mLut;
/**
* Used to cache the float[] LUT for use across multiple native
@@ -50,7 +51,7 @@
// We need 2 frame values as the minimal.
int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
- float values[] = new float[numAnimFrames];
+ float[] values = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
float inValue = i / lastFrame;
@@ -61,7 +62,7 @@
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createLutInterpolator(mLut);
+ return NativeInterpolatorFactory.createLutInterpolator(mLut);
}
/**
@@ -69,7 +70,7 @@
*/
public static long createNativeInterpolator(TimeInterpolator interpolator, long duration) {
float[] lut = createLUT(interpolator, duration);
- return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
+ return NativeInterpolatorFactory.createLutInterpolator(lut);
}
@Override
diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
similarity index 89%
rename from core/java/com/android/internal/view/animation/HasNativeInterpolator.java
rename to graphics/java/android/graphics/animation/HasNativeInterpolator.java
index 48ea4da..c53d5a4 100644
--- a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
+++ b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.view.animation;
+package android.graphics.animation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -23,10 +23,10 @@
/**
* This is a class annotation that signals that it is safe to create
- * a native interpolator counterpart via {@link NativeInterpolatorFactory}
+ * a native interpolator counterpart via {@link NativeInterpolator}
*
* The idea here is to prevent subclasses of interpolators from being treated as a
- * NativeInterpolatorFactory, and instead have them fall back to the LUT & LERP
+ * NativeInterpolator, and instead have them fall back to the LUT & LERP
* method like a custom interpolator.
*
* @hide
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolator.java
similarity index 71%
rename from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
rename to graphics/java/android/graphics/animation/NativeInterpolator.java
index fcacd52..1e6fea8 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/graphics/java/android/graphics/animation/NativeInterpolator.java
@@ -14,8 +14,16 @@
* limitations under the License.
*/
-package com.android.internal.view.animation;
+package android.graphics.animation;
-public interface NativeInterpolatorFactory {
+/**
+ * @hide
+ */
+public interface NativeInterpolator {
+ /**
+ * Generates a native interpolator object that can be used by HardwareRenderer to draw
+ * RenderNodes.
+ * @return ptr to native object
+ */
long createNativeInterpolator();
}
diff --git a/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
new file mode 100644
index 0000000..3886614
--- /dev/null
+++ b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.animation;
+
+import android.animation.TimeInterpolator;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ *
+ * @hide
+ */
+public final class NativeInterpolatorFactory {
+ private NativeInterpolatorFactory() {}
+
+ /**
+ * Create a native interpolator from the provided param generating a LUT variant if a native
+ * implementation does not exist.
+ */
+ public static long createNativeInterpolator(TimeInterpolator interpolator, long
+ duration) {
+ if (interpolator == null) {
+ return createLinearInterpolator();
+ } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+ return ((NativeInterpolator) interpolator).createNativeInterpolator();
+ } else {
+ return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
+ }
+ }
+
+ /** Creates a specialized native interpolator for Accelerate/Decelerate */
+ public static native long createAccelerateDecelerateInterpolator();
+ /** Creates a specialized native interpolator for Accelerate */
+ public static native long createAccelerateInterpolator(float factor);
+ /** Creates a specialized native interpolator for Anticipate */
+ public static native long createAnticipateInterpolator(float tension);
+ /** Creates a specialized native interpolator for Anticipate with Overshoot */
+ public static native long createAnticipateOvershootInterpolator(float tension);
+ /** Creates a specialized native interpolator for Bounce */
+ public static native long createBounceInterpolator();
+ /** Creates a specialized native interpolator for Cycle */
+ public static native long createCycleInterpolator(float cycles);
+ /** Creates a specialized native interpolator for Decelerate */
+ public static native long createDecelerateInterpolator(float factor);
+ /** Creates a specialized native interpolator for Linear interpolation */
+ public static native long createLinearInterpolator();
+ /** Creates a specialized native interpolator for Overshoot */
+ public static native long createOvershootInterpolator(float tension);
+ /** Creates a specialized native interpolator for along traveling along a Path */
+ public static native long createPathInterpolator(float[] x, float[] y);
+ /** Creates a specialized native interpolator for LUT */
+ public static native long createLutInterpolator(float[] values);
+}
diff --git a/graphics/java/android/graphics/animation/RenderNodeAnimator.java b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
new file mode 100644
index 0000000..282b2f9
--- /dev/null
+++ b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.animation;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class RenderNodeAnimator extends Animator {
+ // Keep in sync with enum RenderProperty in Animator.h
+ public static final int TRANSLATION_X = 0;
+ public static final int TRANSLATION_Y = 1;
+ public static final int TRANSLATION_Z = 2;
+ public static final int SCALE_X = 3;
+ public static final int SCALE_Y = 4;
+ public static final int ROTATION = 5;
+ public static final int ROTATION_X = 6;
+ public static final int ROTATION_Y = 7;
+ public static final int X = 8;
+ public static final int Y = 9;
+ public static final int Z = 10;
+ public static final int ALPHA = 11;
+ // The last value in the enum, used for array size initialization
+ public static final int LAST_VALUE = ALPHA;
+
+ // Keep in sync with enum PaintFields in Animator.h
+ public static final int PAINT_STROKE_WIDTH = 0;
+
+ /**
+ * Field for the Paint alpha channel, which should be specified as a value
+ * between 0 and 255.
+ */
+ public static final int PAINT_ALPHA = 1;
+
+ private VirtualRefBasePtr mNativePtr;
+
+ private Handler mHandler;
+ private RenderNode mTarget;
+ private ViewListener mViewListener;
+ private int mRenderProperty = -1;
+ private float mFinalValue;
+ private TimeInterpolator mInterpolator;
+
+ private static final int STATE_PREPARE = 0;
+ private static final int STATE_DELAYED = 1;
+ private static final int STATE_RUNNING = 2;
+ private static final int STATE_FINISHED = 3;
+ private int mState = STATE_PREPARE;
+
+ private long mUnscaledDuration = 300;
+ private long mUnscaledStartDelay = 0;
+ // If this is true, we will run any start delays on the UI thread. This is
+ // the safe default, and is necessary to ensure start listeners fire at
+ // the correct time. Animators created by RippleDrawable (the
+ // CanvasProperty<> ones) do not have this expectation, and as such will
+ // set this to false so that the renderthread handles the startdelay instead
+ private final boolean mUiThreadHandlesDelay;
+ private long mStartDelay = 0;
+ private long mStartTime;
+
+ /**
+ * Interface used by the view system to update the view hierarchy in conjunction
+ * with this animator.
+ */
+ public interface ViewListener {
+ /** notify the listener that an alpha animation has begun. */
+ void onAlphaAnimationStart(float finalAlpha);
+ /** notify the listener that the animator has mutated a value that requires invalidation */
+ void invalidateParent(boolean forceRedraw);
+ }
+
+ public RenderNodeAnimator(int property, float finalValue) {
+ mRenderProperty = property;
+ mFinalValue = finalValue;
+ mUiThreadHandlesDelay = true;
+ init(nCreateAnimator(property, finalValue));
+ }
+
+ public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
+ init(nCreateCanvasPropertyFloatAnimator(
+ property.getNativeContainer(), finalValue));
+ mUiThreadHandlesDelay = false;
+ }
+
+ /**
+ * Creates a new render node animator for a field on a Paint property.
+ *
+ * @param property The paint property to target
+ * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
+ * {@link #PAINT_STROKE_WIDTH}
+ * @param finalValue The target value for the property
+ */
+ public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
+ init(nCreateCanvasPropertyPaintAnimator(
+ property.getNativeContainer(), paintField, finalValue));
+ mUiThreadHandlesDelay = false;
+ }
+
+ public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
+ init(nCreateRevealAnimator(x, y, startRadius, endRadius));
+ mUiThreadHandlesDelay = true;
+ }
+
+ private void init(long ptr) {
+ mNativePtr = new VirtualRefBasePtr(ptr);
+ }
+
+ private void checkMutable() {
+ if (mState != STATE_PREPARE) {
+ throw new IllegalStateException("Animator has already started, cannot change it now!");
+ }
+ if (mNativePtr == null) {
+ throw new IllegalStateException("Animator's target has been destroyed "
+ + "(trying to modify an animation after activity destroy?)");
+ }
+ }
+
+ static boolean isNativeInterpolator(TimeInterpolator interpolator) {
+ return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
+ }
+
+ private void applyInterpolator() {
+ if (mInterpolator == null || mNativePtr == null) return;
+
+ long ni;
+ if (isNativeInterpolator(mInterpolator)) {
+ ni = ((NativeInterpolator) mInterpolator).createNativeInterpolator();
+ } else {
+ long duration = nGetDuration(mNativePtr.get());
+ ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
+ }
+ nSetInterpolator(mNativePtr.get(), ni);
+ }
+
+ @Override
+ public void start() {
+ if (mTarget == null) {
+ throw new IllegalStateException("Missing target!");
+ }
+
+ if (mState != STATE_PREPARE) {
+ throw new IllegalStateException("Already started!");
+ }
+
+ mState = STATE_DELAYED;
+ if (mHandler == null) {
+ mHandler = new Handler(true);
+ }
+ applyInterpolator();
+
+ if (mNativePtr == null) {
+ // It's dead, immediately cancel
+ cancel();
+ } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
+ nSetStartDelay(mNativePtr.get(), mStartDelay);
+ doStart();
+ } else {
+ getHelper().addDelayedAnimation(this);
+ }
+ }
+
+ private void doStart() {
+ // Alpha is a special snowflake that has the canonical value stored
+ // in mTransformationInfo instead of in RenderNode, so we need to update
+ // it with the final value here.
+ if (mRenderProperty == RenderNodeAnimator.ALPHA && mViewListener != null) {
+ mViewListener.onAlphaAnimationStart(mFinalValue);
+ }
+
+ moveToRunningState();
+
+ if (mViewListener != null) {
+ // Kick off a frame to start the process
+ mViewListener.invalidateParent(false);
+ }
+ }
+
+ private void moveToRunningState() {
+ mState = STATE_RUNNING;
+ if (mNativePtr != null) {
+ nStart(mNativePtr.get());
+ }
+ notifyStartListeners();
+ }
+
+ private void notifyStartListeners() {
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationStart(this);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ moveToRunningState();
+ }
+
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationCancel(this);
+ }
+
+ end();
+ }
+ }
+
+ @Override
+ public void end() {
+ if (mState != STATE_FINISHED) {
+ if (mState < STATE_RUNNING) {
+ getHelper().removeDelayedAnimation(this);
+ doStart();
+ }
+ if (mNativePtr != null) {
+ nEnd(mNativePtr.get());
+ if (mViewListener != null) {
+ // Kick off a frame to flush the state change
+ mViewListener.invalidateParent(false);
+ }
+ } else {
+ // It's already dead, jump to onFinish
+ onFinished();
+ }
+ }
+ }
+
+ @Override
+ public void pause() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void resume() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ public void setViewListener(ViewListener listener) {
+ mViewListener = listener;
+ }
+
+ /** Sets the animation target to the owning view of the RecordingCanvas */
+ public final void setTarget(RecordingCanvas canvas) {
+ setTarget(canvas.mNode);
+ }
+
+ /** Sets the node that is to be the target of this animation */
+ protected void setTarget(RenderNode node) {
+ checkMutable();
+ if (mTarget != null) {
+ throw new IllegalStateException("Target already set!");
+ }
+ nSetListener(mNativePtr.get(), this);
+ mTarget = node;
+ mTarget.addAnimator(this);
+ }
+
+ /** Set the start value for the animation */
+ public void setStartValue(float startValue) {
+ checkMutable();
+ nSetStartValue(mNativePtr.get(), startValue);
+ }
+
+ @Override
+ public void setStartDelay(long startDelay) {
+ checkMutable();
+ if (startDelay < 0) {
+ throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
+ }
+ mUnscaledStartDelay = startDelay;
+ mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+ }
+
+ @Override
+ public long getStartDelay() {
+ return mUnscaledStartDelay;
+ }
+
+ @Override
+ public RenderNodeAnimator setDuration(long duration) {
+ checkMutable();
+ if (duration < 0) {
+ throw new IllegalArgumentException("duration must be positive; " + duration);
+ }
+ mUnscaledDuration = duration;
+ nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
+ return this;
+ }
+
+ @Override
+ public long getDuration() {
+ return mUnscaledDuration;
+ }
+
+ @Override
+ public long getTotalDuration() {
+ return mUnscaledDuration + mUnscaledStartDelay;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mState == STATE_DELAYED || mState == STATE_RUNNING;
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mState != STATE_PREPARE;
+ }
+
+ @Override
+ public void setInterpolator(TimeInterpolator interpolator) {
+ checkMutable();
+ mInterpolator = interpolator;
+ }
+
+ @Override
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ protected void onFinished() {
+ if (mState == STATE_PREPARE) {
+ // Unlikely but possible, the native side has been destroyed
+ // before we have started.
+ releaseNativePtr();
+ return;
+ }
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
+ mState = STATE_FINISHED;
+
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationEnd(this);
+ }
+
+ // Release the native object, as it has a global reference to us. This
+ // breaks the cyclic reference chain, and allows this object to be
+ // GC'd
+ releaseNativePtr();
+ }
+
+ private void releaseNativePtr() {
+ if (mNativePtr != null) {
+ mNativePtr.release();
+ mNativePtr = null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private ArrayList<AnimatorListener> cloneListeners() {
+ ArrayList<AnimatorListener> listeners = getListeners();
+ if (listeners != null) {
+ listeners = (ArrayList<AnimatorListener>) listeners.clone();
+ }
+ return listeners;
+ }
+
+ public long getNativeAnimator() {
+ return mNativePtr.get();
+ }
+
+ /**
+ * @return true if the animator was started, false if still delayed
+ */
+ private boolean processDelayed(long frameTimeMs) {
+ if (mStartTime == 0) {
+ mStartTime = frameTimeMs;
+ } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+ doStart();
+ return true;
+ }
+ return false;
+ }
+
+ private static DelayedAnimationHelper getHelper() {
+ DelayedAnimationHelper helper = sAnimationHelper.get();
+ if (helper == null) {
+ helper = new DelayedAnimationHelper();
+ sAnimationHelper.set(helper);
+ }
+ return helper;
+ }
+
+ private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
+ new ThreadLocal<DelayedAnimationHelper>();
+
+ private static class DelayedAnimationHelper implements Runnable {
+
+ private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
+ private final Choreographer mChoreographer;
+ private boolean mCallbackScheduled;
+
+ DelayedAnimationHelper() {
+ mChoreographer = Choreographer.getInstance();
+ }
+
+ public void addDelayedAnimation(RenderNodeAnimator animator) {
+ mDelayedAnims.add(animator);
+ scheduleCallback();
+ }
+
+ public void removeDelayedAnimation(RenderNodeAnimator animator) {
+ mDelayedAnims.remove(animator);
+ }
+
+ private void scheduleCallback() {
+ if (!mCallbackScheduled) {
+ mCallbackScheduled = true;
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+ }
+ }
+
+ @Override
+ public void run() {
+ long frameTimeMs = mChoreographer.getFrameTime();
+ mCallbackScheduled = false;
+
+ int end = 0;
+ for (int i = 0; i < mDelayedAnims.size(); i++) {
+ RenderNodeAnimator animator = mDelayedAnims.get(i);
+ if (!animator.processDelayed(frameTimeMs)) {
+ if (end != i) {
+ mDelayedAnims.set(end, animator);
+ }
+ end++;
+ }
+ }
+ while (mDelayedAnims.size() > end) {
+ mDelayedAnims.remove(mDelayedAnims.size() - 1);
+ }
+
+ if (mDelayedAnims.size() > 0) {
+ scheduleCallback();
+ }
+ }
+ }
+
+ // Called by native
+ private static void callOnFinished(RenderNodeAnimator animator) {
+ if (animator.mHandler != null) {
+ animator.mHandler.post(animator::onFinished);
+ } else {
+ new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
+ }
+ }
+
+ @Override
+ public Animator clone() {
+ throw new IllegalStateException("Cannot clone this animator");
+ }
+
+ @Override
+ public void setAllowRunningAsynchronously(boolean mayRunAsync) {
+ checkMutable();
+ nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
+ }
+
+ private static native long nCreateAnimator(int property, float finalValue);
+ private static native long nCreateCanvasPropertyFloatAnimator(
+ long canvasProperty, float finalValue);
+ private static native long nCreateCanvasPropertyPaintAnimator(
+ long canvasProperty, int paintField, float finalValue);
+ private static native long nCreateRevealAnimator(
+ int x, int y, float startRadius, float endRadius);
+
+ private static native void nSetStartValue(long nativePtr, float startValue);
+ private static native void nSetDuration(long nativePtr, long duration);
+ private static native long nGetDuration(long nativePtr);
+ private static native void nSetStartDelay(long nativePtr, long startDelay);
+ private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+ private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
+ private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
+
+ private static native void nStart(long animPtr);
+ private static native void nEnd(long animPtr);
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1acf6c5..9fb72cf 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -42,6 +42,7 @@
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.os.Build;
import android.os.Handler;
import android.util.ArrayMap;
@@ -54,7 +55,6 @@
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimatorSetHelper;
import android.view.View;
import com.android.internal.R;
@@ -1532,7 +1532,7 @@
long startDelay = extraDelay + animator.getStartDelay();
TimeInterpolator interpolator = animator.getInterpolator();
long nativeInterpolator =
- RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
+ NativeInterpolatorFactory.createNativeInterpolator(interpolator, duration);
startDelay *= ValueAnimator.getDurationScale();
duration *= ValueAnimator.getDurationScale();
@@ -1548,7 +1548,7 @@
* to the last seen RenderNode target and start right away.
*/
protected void recordLastSeenTarget(RecordingCanvas canvas) {
- final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+ final RenderNode node = canvas.mNode;
mLastSeenTarget = new WeakReference<RenderNode>(node);
// Add the animator to the list of animators on every draw
if (mInitialized || mPendingAnimationActions.size() > 0) {
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index cce9ba3..0f37695 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -25,9 +25,9 @@
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.animation.RenderNodeAnimator;
import android.util.FloatProperty;
import android.util.MathUtils;
-import android.view.RenderNodeAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index c520331..7c0af6d 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -152,8 +152,8 @@
derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
mReaderSecretKey = new SecretKeySpec(derivedKey, "AES");
- mEphemeralCounter = 0;
- mReadersExpectedEphemeralCounter = 0;
+ mEphemeralCounter = 1;
+ mReadersExpectedEphemeralCounter = 1;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Error performing key agreement", e);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 91f9447..1df3336 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -204,8 +204,7 @@
void CanvasContext::allocateBuffers() {
if (mNativeSurface) {
- ANativeWindow* anw = mNativeSurface->getNativeWindow();
- ANativeWindow_allocateBuffers(anw);
+ ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
}
}
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index f075a53..3dc7cfc 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -16,6 +16,8 @@
package android.location;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
@@ -62,20 +64,24 @@
}
private void execute(Consumer<TListener> operation) {
- mExecutor.execute(() -> {
- TListener listener = mListener;
- if (listener == null) {
- return;
- }
+ mExecutor.execute(
+ obtainRunnable(Registration<TRequest, TListener>::accept, this, operation)
+ .recycleOnUse());
+ }
- // we may be under the binder identity if a direct executor is used
- long identity = Binder.clearCallingIdentity();
- try {
- operation.accept(listener);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- });
+ private void accept(Consumer<TListener> operation) {
+ TListener listener = mListener;
+ if (listener == null) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ operation.accept(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3d0765b..03e1c75 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,6 +22,8 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.AlarmManager.ELAPSED_REALTIME;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -57,6 +59,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ProviderProperties;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledRunnable;
import java.util.Collections;
import java.util.List;
@@ -81,8 +84,6 @@
@RequiresFeature(PackageManager.FEATURE_LOCATION)
public class LocationManager {
- private static final String TAG = "LocationManager";
-
/**
* For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
* specific package.
@@ -91,7 +92,7 @@
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
- public static final long TARGETED_PENDING_INTENT = 148963590L;
+ private static final long TARGETED_PENDING_INTENT = 148963590L;
/**
* For apps targeting Android K and above, incomplete locations may not be passed to
@@ -2604,18 +2605,28 @@
return;
}
- mExecutor.execute(() -> {
- Consumer<Location> consumer;
- synchronized (GetCurrentLocationTransport.this) {
- if (mConsumer == null) {
- return;
- }
- consumer = mConsumer;
- cancel();
- }
+ PooledRunnable runnable =
+ obtainRunnable(GetCurrentLocationTransport::acceptResult, this, location)
+ .recycleOnUse();
+ try {
+ mExecutor.execute(runnable);
+ } catch (RejectedExecutionException e) {
+ runnable.recycle();
+ throw e;
+ }
+ }
- consumer.accept(location);
- });
+ private void acceptResult(Location location) {
+ Consumer<Location> consumer;
+ synchronized (this) {
+ if (mConsumer == null) {
+ return;
+ }
+ consumer = mConsumer;
+ cancel();
+ }
+
+ consumer.accept(location);
}
}
@@ -2649,30 +2660,36 @@
return;
}
+ PooledRunnable runnable =
+ obtainRunnable(LocationListenerTransport::acceptLocation, this, currentExecutor,
+ location).recycleOnUse();
try {
- currentExecutor.execute(() -> {
- try {
- if (currentExecutor != mExecutor) {
- return;
- }
-
- // we may be under the binder identity if a direct executor is used
- long identity = Binder.clearCallingIdentity();
- try {
- mListener.onLocationChanged(location);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } finally {
- locationCallbackFinished();
- }
- });
+ currentExecutor.execute(runnable);
} catch (RejectedExecutionException e) {
+ runnable.recycle();
locationCallbackFinished();
throw e;
}
}
+ private void acceptLocation(Executor currentExecutor, Location location) {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mListener.onLocationChanged(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ }
+
@Override
public void onProviderEnabled(String provider) {
Executor currentExecutor = mExecutor;
@@ -2680,25 +2697,13 @@
return;
}
+ PooledRunnable runnable =
+ obtainRunnable(LocationListenerTransport::acceptProviderChange, this,
+ currentExecutor, provider, true).recycleOnUse();
try {
- currentExecutor.execute(() -> {
- try {
- if (currentExecutor != mExecutor) {
- return;
- }
-
- // we may be under the binder identity if a direct executor is used
- long identity = Binder.clearCallingIdentity();
- try {
- mListener.onProviderEnabled(provider);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } finally {
- locationCallbackFinished();
- }
- });
+ currentExecutor.execute(runnable);
} catch (RejectedExecutionException e) {
+ runnable.recycle();
locationCallbackFinished();
throw e;
}
@@ -2711,30 +2716,41 @@
return;
}
+ PooledRunnable runnable =
+ obtainRunnable(LocationListenerTransport::acceptProviderChange, this,
+ currentExecutor, provider, false).recycleOnUse();
try {
- currentExecutor.execute(() -> {
- try {
- if (currentExecutor != mExecutor) {
- return;
- }
-
- // we may be under the binder identity if a direct executor is used
- long identity = Binder.clearCallingIdentity();
- try {
- mListener.onProviderDisabled(provider);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } finally {
- locationCallbackFinished();
- }
- });
+ currentExecutor.execute(runnable);
} catch (RejectedExecutionException e) {
+ runnable.recycle();
locationCallbackFinished();
throw e;
}
}
+ private void acceptProviderChange(Executor currentExecutor, String provider,
+ boolean enabled) {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (enabled) {
+ mListener.onProviderEnabled(provider);
+ } else {
+ mListener.onProviderDisabled(provider);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ }
+
@Override
public void onRemoved() {
// TODO: onRemoved is necessary to GC hanging listeners, but introduces some interesting
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 0a0f7f6..53379b87 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1024,7 +1024,18 @@
String device_name,
int codecFormat);
@UnsupportedAppUsage
- public static native int setPhoneState(int state);
+ public static int setPhoneState(int state) {
+ Log.w(TAG, "Do not use this method! Use AudioManager.setMode() instead.");
+ return 0;
+ }
+ /**
+ * @hide
+ * Send the current audio mode to audio policy manager and audio HAL.
+ * @param state the audio mode
+ * @param uid the UID of the app owning the audio mode
+ * @return command completion status.
+ */
+ public static native int setPhoneState(int state, int uid);
@UnsupportedAppUsage
public static native int setForceUse(int usage, int config);
@UnsupportedAppUsage
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 0e6ade5..2178393 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -875,12 +875,12 @@
}
/**
- * @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 @@
* 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 @@
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 @@
.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 @@
.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 5ce291c..4801d44 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -259,7 +259,7 @@
/**
* 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 @@
//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 @@
}
/**
- * @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 @@
* 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 @@
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/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index ebcb9ed..2e038e6 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -31,8 +31,16 @@
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 @@
@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 @@
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 @@
@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 @@
.append(String.join(", ", mPreferredFeatures))
.append("}")
.append(", activeScan=")
- .append(mActiveScan)
+ .append(mShouldPerformActiveScan)
.append(" }");
return result.toString();
@@ -128,12 +152,12 @@
}
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 @@
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 @@
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 @@
/**
* 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 0d4e666..19a46ce 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -55,7 +55,7 @@
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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
&& 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 @@
@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 @@
.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 @@
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 @@
mSelectedRoutes = new ArrayList<>();
mSelectableRoutes = new ArrayList<>();
mDeselectableRoutes = new ArrayList<>();
- mTransferrableRoutes = new ArrayList<>();
+ mTransferableRoutes = new ArrayList<>();
}
/**
@@ -400,7 +400,7 @@
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 @@
}
/**
- * 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/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
similarity index 99%
rename from media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 3ffb951..615dc48 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 @@
@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 267927f..f1dcf3d 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 @@
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 @@
.clearSelectedRoutes()
.addSelectedRoute(routeId)
.removeDeselectableRoute(routeId)
- .removeTransferrableRoute(routeId)
+ .removeTransferableRoute(routeId)
.build();
notifySessionUpdated(newSessionInfo);
publishRoutes();
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 6212493..3f42ad4 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -27,6 +27,7 @@
"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 e6f8c01..dc6dddb 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 6a2b729..4a99e84 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 0000000..ef59252
--- /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 0000000..1b0e4bf
--- /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 0000000..7d2b8e2
--- /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 0000000..a423e47
--- /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 e910967..8e4a982 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 @@
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 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 984ab11..f8db70a 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 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 @@
@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 @@
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 @@
}
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 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 @@
*/
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/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 954eb9b..b47aa98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1366,7 +1366,7 @@
mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
} else {
mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
- mConfig.requirePMF = true;
+ mConfig.requirePmf = true;
}
}
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 05f5fa0..8b815bf 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 @@
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 3d67ba0..3611dfe 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.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.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 @@
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 @@
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
mInfoMediaManager, "com.test.packagename");
+ mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@Test
@@ -419,4 +431,50 @@
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 906dba4..015ce149 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 @@
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 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 3787727..76bea31 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 8037266..c5d4fa9 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 @@
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 1ee5f90..ca841a5 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 @@
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 @@
.setLocalOnly(true)
.setColor(color)
.setStyle(new Notification.BigTextStyle()
- .bigText(content)
.setBigContentTitle(title)
.setSummaryText(contentSummary))
.setAutoCancel(true)
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1814593..530823a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -606,26 +606,21 @@
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 @@
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 @@
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 @@
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/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml
index 646fe5d..977310c 100644
--- a/packages/SystemUI/res/color/control_background.xml
+++ b/packages/SystemUI/res/color/control_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_blue_50" />
+ <item android:color="@color/GM2_blue_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/control_foreground.xml b/packages/SystemUI/res/color/control_foreground.xml
index bf028f1..339f1e2 100644
--- a/packages/SystemUI/res/color/control_foreground.xml
+++ b/packages/SystemUI/res/color/control_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_blue_700" />
+ <item android:color="@color/GM2_blue_200" />
</selector>
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
index 2effd99..1299464 100644
--- a/packages/SystemUI/res/color/light_background.xml
+++ b/packages/SystemUI/res/color/light_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_yellow_50" />
+ <item android:color="@color/GM2_yellow_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/light_foreground.xml b/packages/SystemUI/res/color/light_foreground.xml
index 8143028..7c02075 100644
--- a/packages/SystemUI/res/color/light_foreground.xml
+++ b/packages/SystemUI/res/color/light_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_orange_900" />
+ <item android:color="@color/GM2_yellow_200" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml
index 646fe5d..977310c 100644
--- a/packages/SystemUI/res/color/thermo_cool_background.xml
+++ b/packages/SystemUI/res/color/thermo_cool_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_blue_50" />
+ <item android:color="@color/GM2_blue_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_foreground.xml b/packages/SystemUI/res/color/thermo_cool_foreground.xml
index bf028f1..339f1e2 100644
--- a/packages/SystemUI/res/color/thermo_cool_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_cool_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_blue_700" />
+ <item android:color="@color/GM2_blue_200" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml
index 6f29ed5..2709ebe 100644
--- a/packages/SystemUI/res/color/thermo_heat_background.xml
+++ b/packages/SystemUI/res/color/thermo_heat_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_red_50" />
+ <item android:color="@color/GM2_red_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_foreground.xml b/packages/SystemUI/res/color/thermo_heat_foreground.xml
index 72f4b8d..ffcf550 100644
--- a/packages/SystemUI/res/color/thermo_heat_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_heat_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_red_700" />
+ <item android:color="@color/GM2_red_200" />
</selector>
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
index b246ea0..29b4efa 100644
--- a/packages/SystemUI/res/drawable/control_background.xml
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -19,7 +19,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
- <solid android:color="?android:attr/colorBackgroundFloating"/>
+ <solid android:color="@color/control_default_background" />
<corners android:radius="@dimen/control_corner_radius" />
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/ic_screen_record_background.xml b/packages/SystemUI/res/drawable/ic_screen_record_background.xml
new file mode 100644
index 0000000..9195305
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screen_record_background.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorError"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M10,0L14,0A10,10 0,0 1,24 10L24,10A10,10 0,0 1,14 20L10,20A10,10 0,0 1,0 10L0,10A10,10 0,0 1,10 0z"
+ android:fillColor="@android:color/white"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record.xml
new file mode 100644
index 0000000..486af9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record.xml
@@ -0,0 +1,16 @@
+<!--
+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.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable />
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml
new file mode 100644
index 0000000..ab2314e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml
@@ -0,0 +1,18 @@
+<!--
+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.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable
+ level="1"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml
new file mode 100644
index 0000000..8764ff9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml
@@ -0,0 +1,18 @@
+<!--
+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.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable
+ level="2"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml
new file mode 100644
index 0000000..0ff4d9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml
@@ -0,0 +1,18 @@
+<!--
+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.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable
+ level="3"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index f90012d..4fe21e5 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -2,15 +2,17 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/global_actions_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/global_actions_grid_root"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
+ android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
<com.android.systemui.globalactions.GlobalActionsFlatLayout
@@ -25,65 +27,20 @@
android:gravity="top | center_horizontal"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
- android:layout_marginTop="@dimen/global_actions_top_margin"
- android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
+ android:layout_marginTop="@dimen/global_actions_top_margin">
<LinearLayout
- android:layout_height="wrap_content"
+ android:id="@android:id/list"
android:layout_width="wrap_content"
- android:layoutDirection="ltr"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin">
- <!-- For separated items-->
- <LinearLayout
- android:id="@+id/separated_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
- android:layout_marginRight="@dimen/global_actions_grid_side_margin"
- android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
- android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
- android:paddingTop="@dimen/global_actions_grid_vertical_padding"
- android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
- android:orientation="vertical"
- android:gravity="center"
- android:translationZ="@dimen/global_actions_translate"
- />
- <!-- Grid of action items -->
- <com.android.systemui.globalactions.ListGridLayout
- android:id="@android:id/list"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="right"
- android:layout_marginRight="@dimen/global_actions_grid_side_margin"
- android:translationZ="@dimen/global_actions_translate"
- android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
- android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
- android:paddingTop="@dimen/global_actions_grid_vertical_padding"
- android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
- >
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layoutDirection="locale"
- />
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layoutDirection="locale"
- />
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layoutDirection="locale"
- />
- </com.android.systemui.globalactions.ListGridLayout>
- </LinearLayout>
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+ android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:orientation="horizontal"
+ android:gravity="left"
+ android:translationZ="@dimen/global_actions_translate" />
</com.android.systemui.globalactions.GlobalActionsFlatLayout>
<LinearLayout
@@ -110,8 +67,7 @@
android:layout_marginLeft="@dimen/global_actions_grid_horizontal_padding"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@id/global_actions_panel">
-
- </LinearLayout>
+ app:layout_constraintTop_toBottomOf="@id/global_actions_panel"
+ app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index d0151ff..0b74a11a 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -76,6 +76,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@android:color/white"
+ android:elevation="8dp"
android:visibility="gone"/>
<com.android.systemui.screenshot.ScreenshotSelectorView
android:id="@+id/global_screenshot_selector"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 09058f2..43ceb4e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -201,6 +201,7 @@
<color name="GM2_grey_900">#202124</color>
<color name="GM2_red_50">#FCE8E6</color>
+ <color name="GM2_red_200">#F6AEA9</color>
<color name="GM2_red_300">#F28B82</color>
<color name="GM2_red_500">#B71C1C</color>
<color name="GM2_red_700">#C5221F</color>
@@ -213,6 +214,7 @@
<color name="GM2_blue_700">#1967D2</color>
<color name="GM2_yellow_50">#FEF7E0</color>
+ <color name="GM2_yellow_200">#FDE293</color>
<color name="GM2_yellow_500">#FFFBBC04</color>
<color name="GM2_green_500">#FF34A853</color>
@@ -222,6 +224,8 @@
<color name="magnification_border_color">#FF9900</color>
<!-- controls -->
- <color name="control_default_foreground">?android:attr/textColorPrimary</color>
- <color name="control_default_background">?android:attr/colorBackgroundFloating</color>
+ <color name="control_primary_text">@*android:color/foreground_material_dark</color>
+ <color name="control_secondary_text">@*android:color/dim_foreground_dark</color>
+ <color name="control_default_foreground">@*android:color/foreground_material_dark</color>
+ <color name="control_default_background">@*android:color/background_floating_material_dark</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ec56c1f..15575a4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1228,6 +1228,8 @@
<!-- Screen Record -->
<dimen name="screenrecord_dialog_padding">18dp</dimen>
<dimen name="screenrecord_logo_size">24dp</dimen>
+ <dimen name="screenrecord_status_text_size">14sp</dimen>
+ <dimen name="screenrecord_status_icon_radius">5dp</dimen>
<dimen name="kg_user_switcher_text_size">16sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6fedac0..fecb75c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -635,16 +635,16 @@
<style name="TextAppearance.Control.Status">
<item name="android:textSize">12sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">@color/control_primary_text</item>
</style>
<style name="TextAppearance.Control.Title">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">@color/control_primary_text</item>
</style>
<style name="TextAppearance.Control.Subtitle">
<item name="android:textSize">12sp</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">@color/control_secondary_text</item>
</style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index d153fb0..611da0d 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -57,7 +57,10 @@
}
protected void setSeparatedViewVisibility(boolean visible) {
- getSeparatedView().setVisibility(visible ? View.VISIBLE : View.GONE);
+ ViewGroup separatedView = getSeparatedView();
+ if (separatedView != null) {
+ separatedView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 55c1b6a..aec20751 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,14 +17,15 @@
package com.android.systemui.controls.ui
import android.content.Context
+import android.graphics.BlendMode
import android.graphics.drawable.ClipDrawable
-import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.service.controls.templates.ControlTemplate
import android.service.controls.templates.TemperatureControlTemplate
+import android.service.controls.templates.ThumbnailTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
import android.view.View
@@ -41,7 +42,8 @@
class ControlViewHolder(
val layout: ViewGroup,
val controlsController: ControlsController,
- val uiExecutor: DelayableExecutor
+ val uiExecutor: DelayableExecutor,
+ val bgExecutor: DelayableExecutor
) {
val icon: ImageView = layout.requireViewById(R.id.icon)
val status: TextView = layout.requireViewById(R.id.status)
@@ -50,7 +52,6 @@
val subtitle: TextView = layout.requireViewById(R.id.subtitle)
val context: Context = layout.getContext()
val clipLayer: ClipDrawable
- val gd: GradientDrawable
lateinit var cws: ControlWithState
var cancelUpdate: Runnable? = null
@@ -58,7 +59,6 @@
val ld = layout.getBackground() as LayerDrawable
ld.mutate()
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
- gd = clipLayer.getDrawable() as GradientDrawable
}
fun bindData(cws: ControlWithState) {
@@ -121,6 +121,7 @@
template is ToggleTemplate -> ToggleBehavior()
template is ToggleRangeTemplate -> ToggleRangeBehavior()
template is TemperatureControlTemplate -> TemperatureControlBehavior()
+ template is ThumbnailTemplate -> StaticBehavior(uiExecutor, bgExecutor)
else -> {
object : Behavior {
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
@@ -141,9 +142,9 @@
icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
icon.setImageTintList(fg)
- gd.setColor(bg)
+ clipLayer.getDrawable().setTintBlendMode(BlendMode.HUE)
+ clipLayer.getDrawable().setTintList(bg)
}
-
fun setEnabled(enabled: Boolean) {
status.setEnabled(enabled)
icon.setEnabled(enabled)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 9e6636f..98cdf28 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -223,7 +223,7 @@
val item = inflater.inflate(
R.layout.controls_base_item, lastRow, false) as ViewGroup
lastRow.addView(item)
- val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor)
+ val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor, bgExecutor)
val key = ControlKey(it.component, it.controlId)
cvh.bindData(controlsById.getValue(key))
controlViewsById.put(key, cvh)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
new file mode 100644
index 0000000..af581e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.controls.ui
+
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+
+/**
+ * Use a path to add mask for corners around the drawable, to match the radius
+ * of the underlying shape.
+ */
+class CornerDrawable(val wrapped: Drawable, val cornerRadius: Float) : DrawableWrapper(wrapped) {
+ val path: Path = Path()
+
+ init {
+ val b = getBounds()
+ updatePath(RectF(b))
+ }
+
+ override fun draw(canvas: Canvas) {
+ canvas.clipPath(path)
+ super.draw(canvas)
+ }
+
+ override fun setBounds(l: Int, t: Int, r: Int, b: Int) {
+ updatePath(RectF(l.toFloat(), t.toFloat(), r.toFloat(), b.toFloat()))
+ super.setBounds(l, t, r, b)
+ }
+
+ override fun setBounds(r: Rect) {
+ updatePath(RectF(r))
+ super.setBounds(r)
+ }
+
+ private fun updatePath(r: RectF) {
+ path.reset()
+ path.addRoundRect(r, cornerRadius, cornerRadius, Path.Direction.CW)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
new file mode 100644
index 0000000..340cdc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.templates.ThumbnailTemplate
+
+import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
+
+import java.util.concurrent.Executor
+
+/**
+ * Used for controls that cannot be interacted with. Information is presented to the user
+ * but no actions can be taken. If using a ThumbnailTemplate, the background image will
+ * be changed.
+ */
+class StaticBehavior(
+ val uiExecutor: Executor,
+ val bgExecutor: Executor
+) : Behavior {
+ lateinit var control: Control
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+
+ cvh.status.setText(control.getStatusText())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ val clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+
+ clipLayer.setLevel(MAX_LEVEL)
+ cvh.setEnabled(true)
+ cvh.applyRenderInfo(RenderInfo.lookup(control.getDeviceType(), true))
+
+ val template = control.getControlTemplate()
+ if (template is ThumbnailTemplate) {
+ bgExecutor.execute {
+ // clear the default tinting in favor of only using alpha
+ val drawable = template.getThumbnail().loadDrawable(cvh.context)
+ drawable.setTintList(null)
+ drawable.setAlpha((0.45 * 255).toInt())
+ uiExecutor.execute {
+ val radius = cvh.context.getResources()
+ .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
+ clipLayer.setDrawable(CornerDrawable(drawable, radius))
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 5a6e5b4..27f1b33 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -18,7 +18,7 @@
class UnknownBehavior : Behavior {
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- cvh.status.setText("Loading...")
+ cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
cvh.setEnabled(false)
cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d298b99..9203e6d 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.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.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 java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -155,6 +156,7 @@
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 @@
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 @@
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 @@
mBroadcastDispatcher = broadcastDispatcher;
mContentResolver = contentResolver;
mResources = resources;
+ mConfigurationController = configurationController;
mUserManager = userManager;
mTrustManager = trustManager;
mIActivityManager = iActivityManager;
@@ -225,6 +231,8 @@
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
+ mIWindowManager = iWindowManager;
+ mBackgroundExecutor = backgroundExecutor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -249,7 +257,7 @@
mScreenshotHelper = new ScreenshotHelper(context);
mScreenRecordHelper = new ScreenRecordHelper(context);
- configurationController.addCallback(this);
+ mConfigurationController.addCallback(this);
mActivityStarter = activityStarter;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -335,45 +343,7 @@
} else {
mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
}
- mAirplaneModeOn = new ToggleAction(
- R.drawable.ic_lock_airplane_mode,
- R.drawable.ic_lock_airplane_mode_off,
- R.string.global_actions_toggle_airplane_mode,
- R.string.global_actions_airplane_mode_on_status,
- R.string.global_actions_airplane_mode_off_status) {
-
- void onToggle(boolean on) {
- if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
- mIsWaitingForEcmExit = true;
- // Launch ECM exit dialog
- Intent ecmDialogIntent =
- new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
- ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(ecmDialogIntent);
- } else {
- changeAirplaneModeSystemSetting(on);
- }
- }
-
- @Override
- protected void changeStateFromPress(boolean buttonOn) {
- if (!mHasTelephony) return;
-
- // In ECM mode airplane state cannot be changed
- if (!TelephonyProperties.in_ecm_mode().orElse(false)) {
- mState = buttonOn ? State.TurningOn : State.TurningOff;
- mAirplaneState = mState;
- }
- }
-
- public boolean showDuringKeyguard() {
- return true;
- }
-
- public boolean showBeforeProvisioning() {
- return false;
- }
- };
+ mAirplaneModeOn = new AirplaneModeAction();
onAirplaneModeChanged();
mItems = new ArrayList<Action>();
@@ -495,7 +465,7 @@
}
public void destroy() {
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ mConfigurationController.removeCallback(this);
}
private final class PowerAction extends SinglePressAction implements LongPressAction {
@@ -537,7 +507,7 @@
@Override
public boolean shouldBeSeparated() {
- return !shouldShowControls();
+ return true;
}
@Override
@@ -836,14 +806,12 @@
@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);
}
@@ -1333,6 +1301,48 @@
}
}
+ private class AirplaneModeAction extends ToggleAction {
+ AirplaneModeAction() {
+ super(
+ R.drawable.ic_lock_airplane_mode,
+ R.drawable.ic_lock_airplane_mode_off,
+ R.string.global_actions_toggle_airplane_mode,
+ R.string.global_actions_airplane_mode_on_status,
+ R.string.global_actions_airplane_mode_off_status);
+ }
+ void onToggle(boolean on) {
+ if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
+ mIsWaitingForEcmExit = true;
+ // Launch ECM exit dialog
+ Intent ecmDialogIntent =
+ new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
+ ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(ecmDialogIntent);
+ } else {
+ changeAirplaneModeSystemSetting(on);
+ }
+ }
+
+ @Override
+ protected void changeStateFromPress(boolean buttonOn) {
+ if (!mHasTelephony) return;
+
+ // In ECM mode airplane state cannot be changed
+ if (!TelephonyProperties.in_ecm_mode().orElse(false)) {
+ mState = buttonOn ? State.TurningOn : State.TurningOff;
+ mAirplaneState = mState;
+ }
+ }
+
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+ }
+
private class SilentModeToggleAction extends ToggleAction {
public SilentModeToggleAction() {
super(R.drawable.ic_audio_vol_mute,
@@ -1555,6 +1565,7 @@
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
+ private ViewGroup mContainerView;
ActionsDialog(Context context, MyAdapter adapter,
GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
@@ -1672,6 +1683,14 @@
mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
}
getWindow().setBackgroundDrawable(mBackgroundDrawable);
+
+ if (mControlsView != null) {
+ mContainerView = findViewById(com.android.systemui.R.id.global_actions_container);
+ mContainerView.setOnTouchListener((v, e) -> {
+ dismiss();
+ return true;
+ });
+ }
}
private void fixNavBarClipping() {
@@ -1894,14 +1913,6 @@
return isPanelDebugModeEnabled(context);
}
-
- /**
- * Determines whether the Global Actions menu should use a separated view for emergency actions.
- */
- private static boolean shouldUseSeparatedView() {
- return true;
- }
-
private boolean shouldShowControls() {
return !mKeyguardManager.isDeviceLocked()
&& mControlsUiController.getAvailable();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
index 6749f1d..f102561 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -23,164 +23,62 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.HardwareBgDrawable;
+import com.android.systemui.R;
/**
- * Single row implementation of the button layout created by the global actions dialog.
+ * Flat, single-row implementation of the button layout created by the global actions dialog.
*/
public class GlobalActionsFlatLayout extends GlobalActionsLayout {
+ private static final int MAX_ITEMS = 4;
public GlobalActionsFlatLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mBackgroundsSet = true;
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // backgrounds set only once, the first time onMeasure is called after inflation
- // if (getListView() != null && !mBackgroundsSet) {
- // setBackgrounds();
- // mBackgroundsSet = true;
- // }
- }
-
@VisibleForTesting
- protected void setupListView() {
- ListGridLayout listView = getListView();
- listView.setExpectedCount(Math.min(2, mAdapter.countListItems()));
- listView.setReverseSublists(shouldReverseSublists());
- listView.setReverseItems(shouldReverseListItems());
- listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
+ protected boolean shouldReverseListItems() {
+ int rotation = getCurrentRotation();
+ if (rotation == ROTATION_NONE) {
+ return false;
+ }
+ if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ return rotation == ROTATION_LANDSCAPE;
+ }
+ return rotation == ROTATION_SEASCAPE;
}
@Override
- public void onUpdateList() {
- setupListView();
- super.onUpdateList();
- updateSeparatedItemSize();
- }
-
- /**
- * If the separated view contains only one item, expand the bounds of that item to take up the
- * entire view, so that the whole thing is touch-able.
- */
- @VisibleForTesting
- protected void updateSeparatedItemSize() {
- ViewGroup separated = getSeparatedView();
- if (separated.getChildCount() == 0) {
- return;
- }
- View firstChild = separated.getChildAt(0);
- ViewGroup.LayoutParams childParams = firstChild.getLayoutParams();
-
- if (separated.getChildCount() == 1) {
- childParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
- childParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
- } else {
- childParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
- childParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- }
- }
-
- @Override
- protected ListGridLayout getListView() {
- return (ListGridLayout) super.getListView();
- }
-
- @Override
- protected void removeAllListViews() {
- ListGridLayout list = getListView();
- if (list != null) {
- list.removeAllItems();
- }
+ protected HardwareBgDrawable getBackgroundDrawable(int backgroundColor) {
+ return null;
}
@Override
protected void addToListView(View v, boolean reverse) {
- ListGridLayout list = getListView();
- if (list != null) {
- list.addItem(v);
+ // only add items to the list view if we haven't hit our max yet
+ if (getListView().getChildCount() < MAX_ITEMS) {
+ super.addToListView(v, reverse);
}
}
- @Override
- public void removeAllItems() {
- ViewGroup separatedList = getSeparatedView();
- ListGridLayout list = getListView();
- if (separatedList != null) {
- separatedList.removeAllViews();
- }
- if (list != null) {
- list.removeAllItems();
- }
- }
-
- /**
- * Determines whether the ListGridLayout should fill sublists in the reverse order.
- * Used to account for sublist ordering changing between landscape and seascape views.
- */
@VisibleForTesting
- protected boolean shouldReverseSublists() {
- if (getCurrentRotation() == ROTATION_SEASCAPE) {
- return true;
- }
- return false;
- }
-
- /**
- * Determines whether the ListGridLayout should fill rows first instead of columns.
- * Used to account for vertical/horizontal changes due to landscape or seascape rotations.
- */
- @VisibleForTesting
- protected boolean shouldSwapRowsAndColumns() {
- if (getCurrentRotation() == ROTATION_NONE) {
- return false;
- }
- return true;
- }
-
- @Override
- protected boolean shouldReverseListItems() {
- int rotation = getCurrentRotation();
- boolean reverse = false; // should we add items to parents in the reverse order?
- if (rotation == ROTATION_NONE
- || rotation == ROTATION_SEASCAPE) {
- reverse = !reverse; // if we're in portrait or seascape, reverse items
- }
- if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- reverse = !reverse; // if we're in an RTL language, reverse items (again)
- }
- return reverse;
+ protected float getGridItemSize() {
+ return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
}
@VisibleForTesting
protected float getAnimationDistance() {
- int rows = getListView().getRowCount();
- float gridItemSize = getContext().getResources().getDimension(
- com.android.systemui.R.dimen.global_actions_grid_item_height);
- return rows * gridItemSize / 2;
+ return getGridItemSize() / 2;
}
@Override
public float getAnimationOffsetX() {
- switch (getCurrentRotation()) {
- case ROTATION_LANDSCAPE:
- return getAnimationDistance();
- case ROTATION_SEASCAPE:
- return -getAnimationDistance();
- default: // Portrait
- return 0;
- }
+ return 0;
}
@Override
public float getAnimationOffsetY() {
- if (getCurrentRotation() == ROTATION_NONE) {
- return getAnimationDistance();
- }
- return 0;
+ return -getAnimationDistance();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
index f755a93..183ee45 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
@@ -42,16 +42,30 @@
}
private void setBackgrounds() {
- int gridBackgroundColor = getResources().getColor(
+ ViewGroup listView = getListView();
+ int listBgColor = getResources().getColor(
R.color.global_actions_grid_background, null);
- int separatedBackgroundColor = getResources().getColor(
- R.color.global_actions_separated_background, null);
- HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext());
- HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
- listBackground.setTint(gridBackgroundColor);
- separatedBackground.setTint(separatedBackgroundColor);
- getListView().setBackground(listBackground);
- getSeparatedView().setBackground(separatedBackground);
+ HardwareBgDrawable listBackground = getBackgroundDrawable(listBgColor);
+ if (listBackground != null) {
+ listView.setBackground(listBackground);
+ }
+
+ ViewGroup separatedView = getSeparatedView();
+
+ if (separatedView != null) {
+ int separatedBgColor = getResources().getColor(
+ R.color.global_actions_separated_background, null);
+ HardwareBgDrawable separatedBackground = getBackgroundDrawable(separatedBgColor);
+ if (separatedBackground != null) {
+ getSeparatedView().setBackground(separatedBackground);
+ }
+ }
+ }
+
+ protected HardwareBgDrawable getBackgroundDrawable(int backgroundColor) {
+ HardwareBgDrawable background = new HardwareBgDrawable(true, true, getContext());
+ background.setTint(backgroundColor);
+ return background;
}
@Override
@@ -74,10 +88,16 @@
}
protected void addToSeparatedView(View v, boolean reverse) {
- if (reverse) {
- getSeparatedView().addView(v, 0);
+ ViewGroup separated = getSeparatedView();
+ if (separated != null) {
+ if (reverse) {
+ separated.addView(v, 0);
+ } else {
+ separated.addView(v);
+ }
} else {
- getSeparatedView().addView(v);
+ // if no separated view exists, just use the list view
+ addToListView(v, reverse);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 88a30a1..84891ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -31,20 +31,26 @@
/**
* Quick settings tile for screen recording
*/
-public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> {
+public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
+ implements RecordingController.RecordingStateChangeCallback {
private static final String TAG = "ScreenRecordTile";
private RecordingController mController;
private long mMillisUntilFinished = 0;
+ private Callback mCallback = new Callback();
@Inject
public ScreenRecordTile(QSHost host, RecordingController controller) {
super(host);
mController = controller;
+ mController.observe(this, mCallback);
}
@Override
public BooleanState newTileState() {
- return new BooleanState();
+ BooleanState state = new BooleanState();
+ state.label = mContext.getString(R.string.quick_settings_screen_record_label);
+ state.handlesLongClick = false;
+ return state;
}
@Override
@@ -59,24 +65,13 @@
refreshState();
}
- /**
- * Refresh tile state
- * @param millisUntilFinished Time until countdown completes, or 0 if not counting down
- */
- public void refreshState(long millisUntilFinished) {
- mMillisUntilFinished = millisUntilFinished;
- refreshState();
- }
-
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
boolean isStarting = mController.isStarting();
boolean isRecording = mController.isRecording();
- state.label = mContext.getString(R.string.quick_settings_screen_record_label);
state.value = isRecording || isStarting;
state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- state.handlesLongClick = false;
if (isRecording) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
@@ -125,4 +120,22 @@
Log.d(TAG, "Stopping recording from tile");
mController.stopRecording();
}
+
+ private final class Callback implements RecordingController.RecordingStateChangeCallback {
+ @Override
+ public void onCountdown(long millisUntilFinished) {
+ mMillisUntilFinished = millisUntilFinished;
+ refreshState();
+ }
+
+ @Override
+ public void onRecordingStart() {
+ refreshState();
+ }
+
+ @Override
+ public void onRecordingEnd() {
+ refreshState();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 188501e..6ad9c40 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -24,6 +24,9 @@
import android.util.Log;
import com.android.systemui.qs.tiles.ScreenRecordTile;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.ArrayList;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -32,7 +35,8 @@
* Helper class to initiate a screen recording
*/
@Singleton
-public class RecordingController {
+public class RecordingController
+ implements CallbackController<RecordingController.RecordingStateChangeCallback> {
private static final String TAG = "RecordingController";
private static final String SYSUI_PACKAGE = "com.android.systemui";
private static final String SYSUI_SCREENRECORD_LAUNCHER =
@@ -41,10 +45,11 @@
private final Context mContext;
private boolean mIsStarting;
private boolean mIsRecording;
- private ScreenRecordTile mTileToUpdate;
private PendingIntent mStopIntent;
private CountDownTimer mCountDownTimer = null;
+ private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>();
+
/**
* Create a new RecordingController
* @param context Context for the controller
@@ -63,10 +68,7 @@
final Intent intent = new Intent();
intent.setComponent(launcherComponent);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra("com.android.systemui.screenrecord.EXTRA_SETTINGS_ONLY", true);
mContext.startActivity(intent);
-
- mTileToUpdate = tileToUpdate;
}
/**
@@ -82,16 +84,21 @@
mCountDownTimer = new CountDownTimer(ms, 1000) {
@Override
public void onTick(long millisUntilFinished) {
- refreshTile(millisUntilFinished);
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onCountdown(millisUntilFinished);
+ }
}
@Override
public void onFinish() {
mIsStarting = false;
mIsRecording = true;
- refreshTile();
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onRecordingEnd();
+ }
try {
startIntent.send();
+ Log.d(TAG, "sent start intent");
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
}
@@ -101,18 +108,6 @@
mCountDownTimer.start();
}
- private void refreshTile() {
- refreshTile(0);
- }
-
- private void refreshTile(long millisUntilFinished) {
- if (mTileToUpdate != null) {
- mTileToUpdate.refreshState(millisUntilFinished);
- } else {
- Log.e(TAG, "No tile to refresh");
- }
- }
-
/**
* Cancel a countdown in progress. This will not stop the recording if it already started.
*/
@@ -123,7 +118,10 @@
Log.e(TAG, "Timer was null");
}
mIsStarting = false;
- refreshTile();
+
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onRecordingEnd();
+ }
}
/**
@@ -152,7 +150,10 @@
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error stopping: " + e.getMessage());
}
- refreshTile();
+
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onRecordingEnd();
+ }
}
/**
@@ -161,6 +162,44 @@
*/
public void updateState(boolean isRecording) {
mIsRecording = isRecording;
- refreshTile();
+ for (RecordingStateChangeCallback cb : mListeners) {
+ if (isRecording) {
+ cb.onRecordingStart();
+ } else {
+ cb.onRecordingEnd();
+ }
+ }
+ }
+
+ @Override
+ public void addCallback(RecordingStateChangeCallback listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeCallback(RecordingStateChangeCallback listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * A callback for changes in the screen recording state
+ */
+ public interface RecordingStateChangeCallback {
+ /**
+ * Called when a countdown to recording has updated
+ *
+ * @param millisUntilFinished Time in ms remaining in the countdown
+ */
+ default void onCountdown(long millisUntilFinished) {}
+
+ /**
+ * Called when a screen recording has started
+ */
+ default void onRecordingStart() {}
+
+ /**
+ * Called when a screen recording has ended
+ */
+ default void onRecordingEnd() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 197fe21..9e1e347 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -27,7 +27,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -58,6 +57,7 @@
import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
@@ -68,6 +68,7 @@
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
@@ -136,13 +137,11 @@
private static final String TAG = "GlobalScreenshot";
- private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
- private static final int SCREENSHOT_DROP_IN_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
- private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
- private static final float BACKGROUND_ALPHA = 0.5f;
- private static final float SCREENSHOT_DROP_IN_MIN_SCALE = 0.725f;
+ private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
+ private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+ private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
+ private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
+ private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
private static final float ROUNDED_CORNER_RADIUS = .05f;
private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 6000;
private static final int MESSAGE_CORNER_TIMEOUT = 2;
@@ -166,18 +165,21 @@
private final FrameLayout mDismissButton;
private Bitmap mScreenBitmap;
- private AnimatorSet mScreenshotAnimation;
+ private Animator mScreenshotAnimation;
private float mScreenshotOffsetXPx;
private float mScreenshotOffsetYPx;
private float mScreenshotHeightPx;
- private float mCornerScale;
private float mDismissButtonSize;
+ private float mCornerSizeX;
private AsyncTask<Void, Void, Void> mSaveInBgTask;
private MediaActionSound mCameraSound;
+ // standard material ease
+ private final Interpolator mFastOutSlowIn;
+
private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -227,6 +229,8 @@
mScreenshotLayout.setFocusable(true);
mScreenshotSelectorView.setFocusable(true);
mScreenshotSelectorView.setFocusableInTouchMode(true);
+ mScreenshotView.setPivotX(0);
+ mScreenshotView.setPivotY(0);
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -250,10 +254,12 @@
mScreenshotOffsetYPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_y);
mScreenshotHeightPx =
resources.getDimensionPixelSize(R.dimen.screenshot_action_container_offset_y);
- mCornerScale = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale)
- / (float) mDisplayMetrics.widthPixels;
mDismissButtonSize = resources.getDimensionPixelSize(
R.dimen.screenshot_dismiss_button_tappable_size);
+ mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
+
+ mFastOutSlowIn =
+ AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
// Setup the Camera shutter sound
mCameraSound = new MediaActionSound();
@@ -305,7 +311,9 @@
int width = crop.width();
int height = crop.height();
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, null);
+ Rect screenRect = new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
+
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect);
}
private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
@@ -325,7 +333,7 @@
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
// Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ startAnimation(finisher, screenRect.width(), screenRect.height(),
screenRect);
}
@@ -406,7 +414,6 @@
mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
// Clear any references to the bitmap
- mScreenBitmap = null;
mScreenshotView.setImageBitmap(null);
mActionsContainer.setVisibility(View.GONE);
mBackgroundView.setVisibility(View.GONE);
@@ -430,21 +437,8 @@
// Add the view for the animation
mScreenshotView.setImageBitmap(mScreenBitmap);
- mScreenshotLayout.requestFocus();
- // Setup the animation with the screenshot just taken
- if (mScreenshotAnimation != null) {
- if (mScreenshotAnimation.isStarted()) {
- mScreenshotAnimation.end();
- }
- mScreenshotAnimation.removeAllListeners();
- }
-
- ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
- : createScreenshotDropInAnimation();
- ValueAnimator screenshotToCornerAnimation = createScreenshotToCornerAnimation(w, h);
- mScreenshotAnimation = new AnimatorSet();
- mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotToCornerAnimation);
+ mScreenshotAnimation = createScreenshotDropInAnimation(w, h, screenRect);
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
@@ -487,147 +481,64 @@
});
}
- private ValueAnimator createRectAnimation(Rect rect) {
- mScreenshotView.setAdjustViewBounds(true);
- mScreenshotView.setMaxHeight(rect.height());
- mScreenshotView.setMaxWidth(rect.width());
+ private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) {
+ float cornerScale = mCornerSizeX / (float) width;
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator scaleInterpolator = x -> {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
+ AnimatorSet dropInAnimation = new AnimatorSet();
+ ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
+ flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
+ flashInAnimator.setInterpolator(mFastOutSlowIn);
+ flashInAnimator.addUpdateListener(animation ->
+ mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+ ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
+ flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
+ flashOutAnimator.setInterpolator(mFastOutSlowIn);
+ flashOutAnimator.addUpdateListener(animation ->
+ mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+ final PointF startPos = new PointF((float) bounds.left, (float) bounds.top);
+ final PointF finalPos = new PointF(mScreenshotOffsetXPx,
+ mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale);
+
+ ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
+ toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+ float xPositionPct =
+ SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ float scalePct =
+ SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ toCorner.addUpdateListener(animation -> {
+ float t = animation.getAnimatedFraction();
+ if (t < scalePct) {
+ float scale = MathUtils.lerp(
+ 1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
+ mScreenshotView.setScaleX(scale);
+ mScreenshotView.setScaleY(scale);
}
- return (x - flashDurationPct) / (1f - flashDurationPct);
- };
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
+ if (t < xPositionPct) {
+ mScreenshotView.setX(MathUtils.lerp(
+ startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)));
+ }
+ mScreenshotView.setY(MathUtils.lerp(
+ startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)));
+ });
+
+ toCorner.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setElevation(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(1f);
- mScreenshotView.setScaleY(1f);
+ super.onAnimationStart(animation);
mScreenshotView.setVisibility(View.VISIBLE);
}
});
- anim.addUpdateListener(animation -> {
- float t = (Float) animation.getAnimatedValue();
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- });
- return anim;
- }
- private ValueAnimator createScreenshotDropInAnimation() {
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator flashAlphaInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // Flash the flash view in and out quickly
- if (x <= flashDurationPct) {
- return (float) Math.sin(Math.PI * (x / flashDurationPct));
- }
- return 0;
- }
- };
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- }
- };
+ mScreenshotFlash.setAlpha(0f);
+ mScreenshotFlash.setVisibility(View.VISIBLE);
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(1);
- mScreenshotView.setScaleY(1);
- mScreenshotView.setVisibility(View.VISIBLE);
- mScreenshotFlash.setAlpha(0f);
- mScreenshotFlash.setVisibility(View.VISIBLE);
- }
+ dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
+ dropInAnimation.play(flashOutAnimator).with(toCorner);
- @Override
- public void onAnimationEnd(Animator animation) {
- mScreenshotFlash.setVisibility(View.GONE);
- }
- });
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = 1 - (scaleInterpolator.getInterpolation(t)
- * (1 - SCREENSHOT_DROP_IN_MIN_SCALE));
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
- }
- });
- return anim;
- }
-
- private ValueAnimator createScreenshotToCornerAnimation(int w, int h) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
-
- final float scaleDurationPct =
- (float) SCREENSHOT_DROP_OUT_SCALE_DURATION / SCREENSHOT_DROP_OUT_DURATION;
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- if (x < scaleDurationPct) {
- // Decelerate, and scale the input accordingly
- return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
- }
- return 1f;
- }
- };
-
- // Determine the bounds of how to scale
- float halfScreenWidth = w / 2f;
- float halfScreenHeight = h / 2f;
- final PointF finalPos = new PointF(
- -halfScreenWidth + mCornerScale * halfScreenWidth + mScreenshotOffsetXPx,
- halfScreenHeight - mCornerScale * halfScreenHeight - mScreenshotOffsetYPx);
-
- // Animate the screenshot to the bottom left corner
- anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
- anim.addUpdateListener(animation -> {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_DROP_IN_MIN_SCALE - mCornerScale);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setTranslationX(t * finalPos.x);
- mScreenshotView.setTranslationY(t * finalPos.y);
- });
- anim.addListener(new AnimatorListenerAdapter() {
+ dropInAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
@@ -638,7 +549,8 @@
mDismissButton.setVisibility(View.VISIBLE);
}
});
- return anim;
+
+ return dropInAnimation;
}
private ValueAnimator createScreenshotActionsShadeAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 92236ae..7de70f5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -219,11 +219,6 @@
mParams.mActionsReadyListener.onActionsReady(null, null, null);
}
- // Recycle the bitmap data
- if (image != null) {
- image.recycle();
- }
-
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
new file mode 100644
index 0000000..44ef6b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
@@ -0,0 +1,140 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * The screen record drawable draws a colored background and either a countdown or circle to
+ * indicate that the screen is being recorded.
+ */
+public class ScreenRecordDrawable extends DrawableWrapper {
+ private Drawable mFillDrawable;
+ private int mHorizontalPadding;
+ private int mLevel;
+ private float mTextSize;
+ private float mIconRadius;
+ private Paint mPaint;
+
+ /** No-arg constructor used by drawable inflation. */
+ public ScreenRecordDrawable() {
+ super(null);
+ }
+
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+ setDrawable(r.getDrawable(R.drawable.ic_screen_record_background, theme).mutate());
+ mFillDrawable = r.getDrawable(R.drawable.ic_screen_record_background, theme).mutate();
+ mHorizontalPadding = r.getDimensionPixelSize(R.dimen.status_bar_horizontal_padding);
+
+ mTextSize = r.getDimensionPixelSize(R.dimen.screenrecord_status_text_size);
+ mIconRadius = r.getDimensionPixelSize(R.dimen.screenrecord_status_icon_radius);
+ mLevel = attrs.getAttributeIntValue(null, "level", 0);
+
+ mPaint = new Paint();
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ mPaint.setColor(Color.WHITE);
+ mPaint.setTextSize(mTextSize);
+ mPaint.setFakeBoldText(true);
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mFillDrawable.canApplyTheme() || super.canApplyTheme();
+ }
+
+ @Override
+ public void applyTheme(Resources.Theme t) {
+ super.applyTheme(t);
+ mFillDrawable.applyTheme(t);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mFillDrawable.setBounds(bounds);
+ }
+
+ @Override
+ public boolean onLayoutDirectionChanged(int layoutDirection) {
+ mFillDrawable.setLayoutDirection(layoutDirection);
+ return super.onLayoutDirectionChanged(layoutDirection);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ mFillDrawable.draw(canvas);
+
+ Rect b = mFillDrawable.getBounds();
+ if (mLevel > 0) {
+ String val = String.valueOf(mLevel);
+ Rect textBounds = new Rect();
+ mPaint.getTextBounds(val, 0, val.length(), textBounds);
+ float yOffset = textBounds.height() / 4; // half, and half again since it's centered
+ canvas.drawText(val, b.centerX(), b.centerY() + yOffset, mPaint);
+ } else {
+ canvas.drawCircle(b.centerX(), b.centerY() - mIconRadius / 2, mIconRadius, mPaint);
+ }
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ padding.left += mHorizontalPadding;
+ padding.right += mHorizontalPadding;
+ padding.top = 0;
+ padding.bottom = 0;
+ android.util.Log.d("ScreenRecordDrawable", "set zero top/bottom pad");
+ return true;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ super.setAlpha(alpha);
+ mFillDrawable.setAlpha(alpha);
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ mFillDrawable.setVisible(visible, restart);
+ return super.setVisible(visible, restart);
+ }
+
+ @Override
+ public Drawable mutate() {
+ mFillDrawable.mutate();
+ return super.mutate();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 41d8968..260f94c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -43,6 +43,7 @@
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
@@ -76,7 +77,8 @@
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardStateController.Callback,
- LocationController.LocationChangeCallback {
+ LocationController.LocationChangeCallback,
+ RecordingController.RecordingStateChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -98,6 +100,7 @@
private final String mSlotMicrophone;
private final String mSlotCamera;
private final String mSlotSensorsOff;
+ private final String mSlotScreenRecord;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -116,6 +119,7 @@
private final LocationController mLocationController;
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
+ private final RecordingController mRecordingController;
// Assume it's all good unless we hear otherwise. We don't always seem
// to get broadcasts that it *is* there.
@@ -149,6 +153,7 @@
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mLocationController = Dependency.get(LocationController.class);
mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
+ mRecordingController = Dependency.get(RecordingController.class);
mUiBgExecutor = uiBgExecutor;
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
@@ -167,6 +172,8 @@
mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
+ mSlotScreenRecord = context.getString(
+ com.android.internal.R.string.status_bar_screen_record);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -235,6 +242,10 @@
mIconController.setIconVisibility(mSlotSensorsOff,
mSensorPrivacyController.isSensorPrivacyEnabled());
+ // screen record
+ mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null);
+ mIconController.setIconVisibility(mSlotScreenRecord, false);
+
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
mProvisionedController.addCallback(this);
@@ -246,6 +257,7 @@
mKeyguardStateController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
+ mRecordingController.addCallback(this);
commandQueue.addCallback(this);
}
@@ -438,7 +450,7 @@
}
if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
mHandler.removeCallbacks(mRemoveCastIconRunnable);
- if (isCasting) {
+ if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
mContext.getString(R.string.accessibility_casting));
mIconController.setIconVisibility(mSlotCast, true);
@@ -643,4 +655,40 @@
mIconController.setIconVisibility(mSlotCast, false);
}
};
+
+ // Screen Recording
+ @Override
+ public void onCountdown(long millisUntilFinished) {
+ if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished);
+ int countdown = (int) Math.floorDiv(millisUntilFinished + 500, 1000);
+ int resourceId = R.drawable.stat_sys_screen_record;
+ switch (countdown) {
+ case 1:
+ resourceId = R.drawable.stat_sys_screen_record_1;
+ break;
+ case 2:
+ resourceId = R.drawable.stat_sys_screen_record_2;
+ break;
+ case 3:
+ resourceId = R.drawable.stat_sys_screen_record_3;
+ break;
+ }
+ mIconController.setIcon(mSlotScreenRecord, resourceId, null);
+ mIconController.setIconVisibility(mSlotScreenRecord, true);
+ }
+
+ @Override
+ public void onRecordingStart() {
+ if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
+ mIconController.setIcon(mSlotScreenRecord,
+ R.drawable.stat_sys_screen_record, null);
+ mIconController.setIconVisibility(mSlotScreenRecord, true);
+ }
+
+ @Override
+ public void onRecordingEnd() {
+ // Ensure this is on the main thread, since it could be called during countdown
+ if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
+ mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index 70bcc21..0487ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -98,6 +98,14 @@
private val allContentBounds: MutableMap<FloatingContent, Rect> = HashMap()
/**
+ * Whether we are currently resolving conflicts by asking content to move. If we are, we'll
+ * temporarily ignore calls to [onContentMoved] - those calls are from the content that is
+ * moving to new, conflict-free bounds, so we don't need to perform conflict detection
+ * calculations in response.
+ */
+ private var currentlyResolvingConflicts = false
+
+ /**
* Makes the coordinator aware of a new piece of floating content, and moves any existing
* content out of the way, if necessary.
*
@@ -126,6 +134,13 @@
*/
@JvmOverloads
fun onContentMoved(content: FloatingContent) {
+
+ // Ignore calls when we are currently resolving conflicts, since those calls are from
+ // content that is moving to new, conflict-free bounds.
+ if (currentlyResolvingConflicts) {
+ return
+ }
+
if (!allContentBounds.containsKey(content)) {
Log.wtf(TAG, "Received onContentMoved call before onContentAdded! " +
"This should never happen.")
@@ -162,6 +177,8 @@
* them to move out of the way.
*/
private fun maybeMoveConflictingContent(fromContent: FloatingContent) {
+ currentlyResolvingConflicts = true
+
val conflictingNewBounds = allContentBounds[fromContent]!!
allContentBounds
// Filter to content that intersects with the new bounds. That's content that needs
@@ -182,6 +199,8 @@
.minus(conflictingNewBounds)))
allContentBounds[content] = content.getFloatingBoundsOnScreen()
}
+
+ currentlyResolvingConflicts = false
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 7d47f6bd..fc79fcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -44,7 +44,6 @@
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.res.Resources;
import android.hardware.face.FaceManager;
import android.service.notification.ZenModeConfig;
@@ -859,42 +858,6 @@
verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
}
- static class TestableBubbleController extends BubbleController {
- // Let's assume surfaces can be synchronized immediately.
- TestableBubbleController(Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController,
- ShadeController shadeController,
- BubbleData data,
- ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
- ZenModeController zenModeController,
- NotificationLockscreenUserManager lockscreenUserManager,
- NotificationGroupManager groupManager,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- FeatureFlags featureFlags,
- DumpController dumpController) {
- super(context,
- notificationShadeWindowController, statusBarStateController, shadeController,
- data, Runnable::run, configurationController, interruptionStateProvider,
- zenModeController, lockscreenUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpController);
- setInflateSynchronously(true);
- }
- }
-
- static class TestableNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
-
- TestableNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter, StatusBarStateController controller,
- BatteryController batteryController) {
- super(context, filter, controller, batteryController);
- mUseHeadsUp = true;
- }
- }
-
/**
* Sets the bubble metadata flags for this entry. These ]flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 5a1bef9..24f8a7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -40,7 +40,6 @@
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.res.Resources;
import android.hardware.face.FaceManager;
import android.service.notification.ZenModeConfig;
@@ -788,42 +787,6 @@
groupSummary.getEntry()));
}
- static class TestableBubbleController extends BubbleController {
- // Let's assume surfaces can be synchronized immediately.
- TestableBubbleController(Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController,
- ShadeController shadeController,
- BubbleData data,
- ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
- ZenModeController zenModeController,
- NotificationLockscreenUserManager lockscreenUserManager,
- NotificationGroupManager groupManager,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- FeatureFlags featureFlags,
- DumpController dumpController) {
- super(context,
- notificationShadeWindowController, statusBarStateController, shadeController,
- data, Runnable::run, configurationController, interruptionStateProvider,
- zenModeController, lockscreenUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpController);
- setInflateSynchronously(true);
- }
- }
-
- static class TestableNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
-
- TestableNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter, StatusBarStateController controller,
- BatteryController batteryController) {
- super(context, filter, controller, batteryController);
- mUseHeadsUp = true;
- }
- }
-
/**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
new file mode 100644
index 0000000..338abf5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -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.bubbles;
+
+import android.content.Context;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+/**
+ * Testable BubbleController subclass that immediately synchronizes surfaces.
+ */
+public class TestableBubbleController extends BubbleController {
+
+ // Let's assume surfaces can be synchronized immediately.
+ TestableBubbleController(Context context,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController,
+ ShadeController shadeController,
+ BubbleData data,
+ ConfigurationController configurationController,
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ NotificationGroupManager groupManager,
+ NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
+ FeatureFlags featureFlags,
+ DumpController dumpController) {
+ super(context,
+ notificationShadeWindowController, statusBarStateController, shadeController,
+ data, Runnable::run, configurationController, interruptionStateProvider,
+ zenModeController, lockscreenUserManager, groupManager, entryManager,
+ notifPipeline, featureFlags, dumpController);
+ setInflateSynchronously(true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..5d192b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.bubbles;
+
+import android.content.Context;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+public class TestableNotificationInterruptionStateProvider
+ extends NotificationInterruptionStateProvider {
+
+ TestableNotificationInterruptionStateProvider(Context context,
+ NotificationFilter filter, StatusBarStateController controller,
+ BatteryController batteryController) {
+ super(context, filter, controller, batteryController);
+ mUseHeadsUp = 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 5a0a495..f1fba79 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 @@
.inflationCallback(any()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
when(mExpandableNotificationRowComponentBuilder
+ .rowContentBindStage(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
.onExpandClickListener(any()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp
index 351e92c..0e7e101 100644
--- a/packages/services/PacProcessor/jni/Android.bp
+++ b/packages/services/PacProcessor/jni/Android.bp
@@ -37,8 +37,7 @@
"-Wunused",
"-Wunreachable-code",
],
- // Re-enable when b/145990493 is fixed
- // sanitize: {
- // cfi: true,
- // },
+ sanitize: {
+ cfi: true,
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 75ec4b0..6c8aaf4 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.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 @@
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/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4474f60..872f0eb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -119,6 +119,9 @@
private final LocalLog mUiLatencyHistory;
private final LocalLog mWtfHistory;
private final FieldClassificationStrategy mFieldClassificationStrategy;
+
+ @GuardedBy("mLock")
+ @Nullable
private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
/**
@@ -236,17 +239,8 @@
sendStateToClients(/* resetClient= */ false);
}
updateRemoteAugmentedAutofillService();
+ updateRemoteInlineSuggestionRenderServiceLocked();
- final ComponentName componentName = RemoteInlineSuggestionRenderService
- .getServiceComponentName(getContext(), mUserId);
- if (componentName != null) {
- mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
- getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
- mUserId, new InlineSuggestionRenderCallbacksImpl(),
- mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
- } else {
- mRemoteInlineSuggestionRenderService = null;
- }
return enabledChanged;
}
@@ -1644,7 +1638,29 @@
return mFieldClassificationStrategy.getDefaultAlgorithm();
}
- RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderService() {
+ private void updateRemoteInlineSuggestionRenderServiceLocked() {
+ if (mRemoteInlineSuggestionRenderService != null) {
+ if (sVerbose) {
+ Slog.v(TAG, "updateRemoteInlineSuggestionRenderService(): "
+ + "destroying old remote service");
+ }
+ mRemoteInlineSuggestionRenderService = null;
+ }
+
+ mRemoteInlineSuggestionRenderService = getRemoteInlineSuggestionRenderServiceLocked();
+ }
+
+ RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderServiceLocked() {
+ final ComponentName componentName = RemoteInlineSuggestionRenderService
+ .getServiceComponentName(getContext(), mUserId);
+
+ if (mRemoteInlineSuggestionRenderService == null) {
+ mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
+ getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
+ mUserId, new InlineSuggestionRenderCallbacksImpl(),
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
+
return mRemoteInlineSuggestionRenderService;
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index d93ac68..1eb7692 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -146,7 +146,8 @@
@Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull Runnable onErrorCallback) {
+ @NonNull Runnable onErrorCallback,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
long requestTime = SystemClock.elapsedRealtime();
AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
@@ -168,7 +169,7 @@
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsRequest, inlineSuggestionsData,
focusedId, inlineSuggestionsCallback, client,
- onErrorCallback);
+ onErrorCallback, remoteRenderService);
requestAutofill.complete(null);
}
@@ -234,7 +235,8 @@
@Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
@NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
+ @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
|| request == null) {
return;
@@ -254,7 +256,7 @@
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
- }, onErrorCallback));
+ }, onErrorCallback, remoteRenderService));
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 31dc23f..5d5af53 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -36,7 +36,10 @@
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
-final class RemoteInlineSuggestionRenderService extends
+/**
+ * Remote service to help connect to InlineSuggestionRenderService in ExtServices.
+ */
+public final class RemoteInlineSuggestionRenderService extends
AbstractMultiplePendingRequestsRemoteService<RemoteInlineSuggestionRenderService,
IInlineSuggestionRenderService> {
@@ -81,9 +84,11 @@
* Called by {@link Session} to generate a call to the
* {@link RemoteInlineSuggestionRenderService} to request rendering a slice .
*/
- void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
- @NonNull InlinePresentation presentation, int width, int height) {
- scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height));
+ public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
+ @NonNull InlinePresentation presentation, int width, int height,
+ @Nullable IBinder hostInputToken) {
+ scheduleAsyncRequest(
+ (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken));
}
@Nullable
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 72029d1..b3ef134 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2677,7 +2677,7 @@
synchronized (mLock) {
requestHideFillUi(mCurrentViewId);
}
- });
+ }, mService.getRemoteInlineSuggestionRenderServiceLocked());
try {
imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
@@ -2981,7 +2981,7 @@
synchronized (mLock) {
cancelAugmentedAutofillLocked();
}
- });
+ }, mService.getRemoteInlineSuggestionRenderServiceLocked());
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 5f6e47b..54d0433 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -22,13 +22,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.IBinder;
import android.os.RemoteException;
import android.service.autofill.Dataset;
+import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
import android.text.TextUtils;
import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.View;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inline.InlinePresentationSpec;
@@ -40,11 +41,14 @@
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.LocalServices;
import com.android.server.UiThread;
+import com.android.server.autofill.RemoteInlineSuggestionRenderService;
+import com.android.server.wm.WindowManagerInternal;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.BiFunction;
+import java.util.function.BiConsumer;
import java.util.regex.Pattern;
public final class InlineSuggestionFactory {
@@ -61,24 +65,6 @@
}
/**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
- * augmented autofill service.
- */
- public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
- @NonNull InlineSuggestionsRequest request,
- @NonNull Dataset[] datasets,
- @NonNull AutofillId autofillId,
- @NonNull Context context,
- @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
- @NonNull Runnable onErrorCallback) {
- if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
- return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
- datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context,
- onErrorCallback,
- (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
- }
-
- /**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
* autofill service, potentially filtering the datasets.
*/
@@ -90,11 +76,33 @@
@NonNull AutofillId autofillId,
@NonNull Context context,
@NonNull AutoFillUI.AutoFillUiCallback client,
- @NonNull Runnable onErrorCallback) {
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
filterText, inlineActions, autofillId, context, onErrorCallback,
- (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
+ (dataset, datasetIndex) -> client.fill(requestId, datasetIndex, dataset),
+ remoteRenderService);
+ }
+
+ /**
+ * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
+ * autofill service.
+ */
+ public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
+ @NonNull InlineSuggestionsRequest request,
+ @NonNull Dataset[] datasets,
+ @NonNull AutofillId autofillId,
+ @NonNull Context context,
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+ if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
+ return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
+ datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context,
+ onErrorCallback,
+ (dataset, fieldIndex) -> inlineSuggestionUiCallback.autofill(dataset),
+ remoteRenderService);
}
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
@@ -102,10 +110,9 @@
@NonNull Dataset[] datasets, @Nullable String filterText,
@Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
@NonNull Context context, @NonNull Runnable onErrorCallback,
- @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+ @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
- onErrorCallback);
for (int i = 0; i < datasets.length; i++) {
final Dataset dataset = datasets[i];
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
@@ -124,14 +131,15 @@
}
InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
- inlineSuggestionUi, onClickListenerFactory);
+ onClickFactory, remoteRenderService, onErrorCallback,
+ request.getHostInputToken());
inlineSuggestions.add(inlineSuggestion);
}
if (inlineActions != null) {
for (InlinePresentation inlinePresentation : inlineActions) {
final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
mergedInlinePresentation(request, 0, inlinePresentation),
- inlineSuggestionUi);
+ remoteRenderService, onErrorCallback, request.getHostInputToken());
inlineSuggestions.add(inlineAction);
}
}
@@ -173,40 +181,40 @@
private static InlineSuggestion createInlineAction(boolean isAugmented,
@NonNull Context context,
@NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi) {
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
: InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
InlineSuggestionInfo.TYPE_ACTION);
- final View.OnClickListener onClickListener = v -> {
- // TODO(b/148567875): Launch the intent provided through the slice. This
- // should be part of the UI renderer therefore will be moved to the support
- // library.
+ final Runnable onClickAction = () -> {
Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
};
return new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
- onClickListener));
+ createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
+ remoteRenderService, hostInputToken));
}
private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
- @NonNull Dataset dataset,
- int fieldIndex, @NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi,
- @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+ @NonNull Dataset dataset, int fieldIndex,
+ @NonNull InlinePresentation inlinePresentation,
+ @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
: InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
- final View.OnClickListener onClickListener = onClickListenerFactory.apply(dataset,
- fieldIndex);
+
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
- onClickListener));
+ createInlineContentProvider(inlinePresentation,
+ () -> onClickFactory.accept(dataset, fieldIndex), onErrorCallback,
+ remoteRenderService, hostInputToken));
+
return inlineSuggestion;
}
@@ -231,27 +239,64 @@
}
private static IInlineContentProvider.Stub createInlineContentProvider(
- @NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi,
- @Nullable View.OnClickListener onClickListener) {
+ @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+ @Nullable IBinder hostInputToken) {
return new IInlineContentProvider.Stub() {
@Override
- public void provideContent(int width, int height,
- IInlineContentCallback callback) {
+ public void provideContent(int width, int height, IInlineContentCallback callback) {
UiThread.getHandler().post(() -> {
- SurfaceControl sc = inlineSuggestionUi.inflate(inlinePresentation, width,
- height,
- onClickListener);
- try {
- callback.onContent(sc);
- } catch (RemoteException e) {
- Slog.w(TAG, "Encounter exception calling back with inline content.");
+ final IInlineSuggestionUiCallback uiCallback = createInlineSuggestionUiCallback(
+ callback, onClickAction, onErrorCallback);
+
+ if (remoteRenderService == null) {
+ Slog.e(TAG, "RemoteInlineSuggestionRenderService is null");
+ return;
}
+
+ remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
+ width, height, hostInputToken);
});
}
};
}
+ private static IInlineSuggestionUiCallback.Stub createInlineSuggestionUiCallback(
+ @NonNull IInlineContentCallback callback, @NonNull Runnable onAutofillCallback,
+ @NonNull Runnable onErrorCallback) {
+ return new IInlineSuggestionUiCallback.Stub() {
+ @Override
+ public void onAutofill() throws RemoteException {
+ onAutofillCallback.run();
+ }
+
+ @Override
+ public void onContent(SurfaceControl surface)
+ throws RemoteException {
+ callback.onContent(surface);
+ }
+
+ @Override
+ public void onError() throws RemoteException {
+ onErrorCallback.run();
+ }
+
+ @Override
+ public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId)
+ throws RemoteException {
+ //TODO(b/149574510): Move logic to IMMS
+ final WindowManagerInternal windowManagerInternal = LocalServices.getService(
+ WindowManagerInternal.class);
+ if (!windowManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
+ displayId)) {
+ Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
+ onErrorCallback.run();
+ }
+ }
+ };
+ }
+
private InlineSuggestionFactory() {
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
deleted file mode 100644
index bf148a6..0000000
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill.ui;
-
-import static android.app.slice.SliceItem.FORMAT_IMAGE;
-import static android.app.slice.SliceItem.FORMAT_TEXT;
-
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Icon;
-import android.graphics.fonts.SystemFonts;
-import android.os.IBinder;
-import android.service.autofill.InlinePresentation;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This is a temporary inline suggestion UI inflater which will be replaced by the ExtServices
- * implementation.
- *
- * TODO(b/146453086): remove this class once autofill ext service is implemented.
- *
- * @hide
- */
-public class InlineSuggestionUi {
-
- private static final String TAG = "InlineSuggestionUi";
-
- // The pattern to match the value can be obtained by calling {@code Resources#getResourceName
- // (int)}. This name is a single string of the form "package:type/entry".
- private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
-
- private final @NonNull Context mContext;
- private final @NonNull Runnable mOnErrorCallback;
-
- InlineSuggestionUi(@NonNull Context context, @NonNull Runnable onErrorCallback) {
- this.mContext = context;
- mOnErrorCallback = onErrorCallback;
- }
-
- /**
- * Returns a {@link SurfaceControl} with the inflated content embedded in it.
- */
- @MainThread
- @Nullable
- public SurfaceControl inflate(@NonNull InlinePresentation inlinePresentation, int width,
- int height, @Nullable View.OnClickListener onClickListener) {
- Log.d(TAG, "Inflating the inline suggestion UI");
-
- //TODO(b/137800469): Pass in inputToken from IME.
- final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext,
- mContext.getDisplay(), (IBinder) null);
- final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
-
- Context contextThemeWrapper = getContextThemeWrapper(mContext,
- inlinePresentation.getInlinePresentationSpec().getStyle());
- if (contextThemeWrapper == null) {
- contextThemeWrapper = getDefaultContextThemeWrapper(mContext);
- }
- final View suggestionView = renderSlice(inlinePresentation.getSlice(),
- contextThemeWrapper);
-
- final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(
- mContext, mOnErrorCallback);
- suggestionRoot.addView(suggestionView);
- suggestionRoot.setOnClickListener(onClickListener);
-
- WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0,
- PixelFormat.TRANSPARENT);
- wvr.addView(suggestionRoot, lp);
- return sc;
- }
-
- private static View renderSlice(Slice slice, Context context) {
- final LayoutInflater inflater = LayoutInflater.from(context);
- final ViewGroup suggestionView =
- (ViewGroup) inflater.inflate(R.layout.autofill_inline_suggestion, null);
-
- final ImageView startIconView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_start_icon);
- final TextView titleView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_title);
- final TextView subtitleView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_subtitle);
- final ImageView endIconView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_end_icon);
-
- boolean hasStartIcon = false;
- boolean hasEndIcon = false;
- boolean hasSubtitle = false;
- final List<SliceItem> sliceItems = slice.getItems();
- for (int i = 0; i < sliceItems.size(); i++) {
- final SliceItem sliceItem = sliceItems.get(i);
- if (sliceItem.getFormat().equals(FORMAT_IMAGE)) {
- final Icon sliceIcon = sliceItem.getIcon();
- if (i == 0) { // start icon
- startIconView.setImageIcon(sliceIcon);
- hasStartIcon = true;
- } else { // end icon
- endIconView.setImageIcon(sliceIcon);
- hasEndIcon = true;
- }
- } else if (sliceItem.getFormat().equals(FORMAT_TEXT)) {
- final List<String> sliceHints = sliceItem.getHints();
- final String sliceText = sliceItem.getText().toString();
- if (sliceHints.contains("inline_title")) { // title
- titleView.setText(sliceText);
- } else { // subtitle
- subtitleView.setText(sliceText);
- hasSubtitle = true;
- }
- }
- }
- if (!hasStartIcon) {
- startIconView.setVisibility(View.GONE);
- }
- if (!hasEndIcon) {
- endIconView.setVisibility(View.GONE);
- }
- if (!hasSubtitle) {
- subtitleView.setVisibility(View.GONE);
- }
-
- return suggestionView;
- }
-
- private Context getDefaultContextThemeWrapper(@NonNull Context context) {
- Resources.Theme theme = context.getResources().newTheme();
- theme.applyStyle(android.R.style.Theme_AutofillInlineSuggestion, true);
- return new ContextThemeWrapper(context, theme);
- }
-
- /**
- * Returns a context wrapping the theme in the provided {@code style}, or null if {@code
- * style} doesn't pass validation.
- */
- @Nullable
- private static Context getContextThemeWrapper(@NonNull Context context,
- @Nullable String style) {
- if (style == null) {
- return null;
- }
- Matcher matcher = RESOURCE_NAME_PATTERN.matcher(style);
- if (!matcher.matches()) {
- Log.d(TAG, "Can not parse the style=" + style);
- return null;
- }
- String packageName = matcher.group(1);
- String type = matcher.group(2);
- String entry = matcher.group(3);
- if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(type) || TextUtils.isEmpty(entry)) {
- Log.d(TAG, "Can not proceed with empty field values in the style=" + style);
- return null;
- }
- Resources resources = null;
- try {
- resources = context.getPackageManager().getResourcesForApplication(
- packageName);
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- int resId = resources.getIdentifier(entry, type, packageName);
- if (resId == Resources.ID_NULL) {
- return null;
- }
- Resources.Theme theme = resources.newTheme();
- theme.applyStyle(resId, true);
- if (!validateBaseTheme(theme, resId)) {
- Log.d(TAG, "Provided theme is not a child of Theme.InlineSuggestion, ignoring it.");
- return null;
- }
- if (!validateFontFamilyForTextViewStyles(theme)) {
- Log.d(TAG,
- "Provided theme specifies a font family that is not system font, ignoring it.");
- return null;
- }
- return new ContextThemeWrapper(context, theme);
- }
-
- private static boolean validateFontFamilyForTextViewStyles(Resources.Theme theme) {
- return validateFontFamily(theme, android.R.attr.autofillInlineSuggestionTitle)
- && validateFontFamily(theme, android.R.attr.autofillInlineSuggestionSubtitle);
- }
-
- private static boolean validateFontFamily(Resources.Theme theme, int styleAttr) {
- TypedArray ta = null;
- try {
- ta = theme.obtainStyledAttributes(null, new int[]{android.R.attr.fontFamily},
- styleAttr,
- 0);
- if (ta.getIndexCount() == 0) {
- return true;
- }
- String fontFamily = ta.getString(ta.getIndex(0));
- return SystemFonts.getRawSystemFallbackMap().containsKey(fontFamily);
- } finally {
- if (ta != null) {
- ta.recycle();
- }
- }
- }
-
- private static boolean validateBaseTheme(Resources.Theme theme, int styleAttr) {
- TypedArray ta = null;
- try {
- ta = theme.obtainStyledAttributes(null,
- new int[]{android.R.attr.isAutofillInlineSuggestionTheme}, styleAttr, 0);
- if (ta.getIndexCount() == 0) {
- return false;
- }
- return ta.getBoolean(ta.getIndex(0), false);
- } finally {
- if (ta != null) {
- ta.recycle();
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index e3c7325..aabe98b 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -168,6 +168,11 @@
public int getUserIdentifier() {
return mUserInfo.id;
}
+
+ @Override
+ public String toString() {
+ return Integer.toString(getUserIdentifier());
+ }
}
/**
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3ffe1be..0edc160 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.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 @@
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d7530..f41eeeb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1001,11 +1001,14 @@
mDeviceBroker.onAudioServerDied();
// Restore call state
- if (AudioSystem.setPhoneState(mMode) == AudioSystem.AUDIO_STATUS_OK) {
- mModeLogger.log(new AudioEventLogger.StringEvent(
- "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode) + ")"));
+ synchronized (mDeviceBroker.mSetModeLock) {
+ if (AudioSystem.setPhoneState(mMode, getModeOwnerUid())
+ == AudioSystem.AUDIO_STATUS_OK) {
+ mModeLogger.log(new AudioEventLogger.StringEvent(
+ "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
+ + ", uid=" + getModeOwnerUid() + ")"));
+ }
}
-
final int forSys;
synchronized (mSettingsLock) {
forSys = mCameraSoundForced ?
@@ -3425,14 +3428,30 @@
return modeOwnerPid;
}
+ /**
+ * Return the uid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ /*package*/ int getModeOwnerUid() {
+ int modeOwnerUid = 0;
+ try {
+ modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
+ } catch (Exception e) {
+ // nothing to do, modeOwnerUid is not modified
+ }
+ return modeOwnerUid;
+ }
+
private class SetModeDeathHandler implements IBinder.DeathRecipient {
- private IBinder mCb; // To be notified of client's death
- private int mPid;
+ private final IBinder mCb; // To be notified of client's death
+ private final int mPid;
+ private final int mUid;
private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
- SetModeDeathHandler(IBinder cb, int pid) {
+ SetModeDeathHandler(IBinder cb, int pid, int uid) {
mCb = cb;
mPid = pid;
+ mUid = uid;
}
public void binderDied() {
@@ -3445,7 +3464,7 @@
if (index < 0) {
Log.w(TAG, "unregistered setMode() client died");
} else {
- newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
+ newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG);
}
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
@@ -3470,6 +3489,10 @@
public IBinder getBinder() {
return mCb;
}
+
+ public int getUid() {
+ return mUid;
+ }
}
/** @see AudioManager#setMode(int) */
@@ -3518,7 +3541,8 @@
+ " without permission or being mode owner");
return;
}
- newModeOwnerPid = setModeInt(mode, cb, callingPid, callingPackage);
+ newModeOwnerPid = setModeInt(
+ mode, cb, callingPid, Binder.getCallingUid(), callingPackage);
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
@@ -3530,9 +3554,11 @@
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
@GuardedBy("mDeviceBroker.mSetModeLock")
- private int setModeInt(int mode, IBinder cb, int pid, String caller) {
- if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
- + caller + ")"); }
+ private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) {
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
+ + ", uid=" + uid + ", caller=" + caller + ")");
+ }
int newModeOwnerPid = 0;
if (cb == null) {
Log.e(TAG, "setModeInt() called with null binder");
@@ -3569,7 +3595,7 @@
}
} else {
if (hdlr == null) {
- hdlr = new SetModeDeathHandler(cb, pid);
+ hdlr = new SetModeDeathHandler(cb, pid, uid);
}
// Register for client death notification
try {
@@ -3587,7 +3613,7 @@
if (actualMode != mMode) {
final long identity = Binder.clearCallingIdentity();
- status = AudioSystem.setPhoneState(actualMode);
+ status = AudioSystem.setPhoneState(actualMode, getModeOwnerUid());
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
@@ -6634,7 +6660,7 @@
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
- "phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
+ "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))");
// logs for wired + A2DP device connections:
// - wired: logged before onSetWiredDeviceConnectionState() is executed
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 648e07a..ac3a653 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -395,7 +395,7 @@
static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
// TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
- // True by default.
+ // False by default.
static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 0944324..5541b11 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -45,7 +45,7 @@
private static final String TAG = "HdmiCecLocalDevicePlayback";
private static final boolean WAKE_ON_HOTPLUG =
- SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
+ SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, false);
private static final boolean SET_MENU_LANGUAGE =
HdmiProperties.set_menu_language().orElse(false);
diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING
index b45b4ea..ca7f396 100644
--- a/services/core/java/com/android/server/integrity/TEST_MAPPING
+++ b/services/core/java/com/android/server/integrity/TEST_MAPPING
@@ -5,12 +5,6 @@
"options": [
{
"include-filter": "com.android.server.integrity."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 997f21c..433ec43 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -16,6 +16,8 @@
package com.android.server.location;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
import android.annotation.Nullable;
import android.content.Context;
import android.location.Location;
@@ -335,7 +337,8 @@
*/
public final void setRequest(ProviderRequest request) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
- mExecutor.execute(() -> onSetRequest(request));
+ mExecutor.execute(obtainRunnable(AbstractLocationProvider::onSetRequest, this, request)
+ .recycleOnUse());
}
/**
@@ -348,7 +351,13 @@
*/
public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
- mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
+
+ // the integer boxing done here likely cancels out any gains from removing lambda
+ // allocation, but since this an infrequently used api with no real performance needs, we
+ // we use pooled lambdas anyways for consistency.
+ mExecutor.execute(
+ obtainRunnable(AbstractLocationProvider::onExtraCommand, this, uid, pid, command,
+ extras).recycleOnUse());
}
/**
@@ -361,7 +370,9 @@
*/
public final void requestSetAllowed(boolean allowed) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
- mExecutor.execute(() -> onRequestSetAllowed(allowed));
+ mExecutor.execute(
+ obtainRunnable(AbstractLocationProvider::onRequestSetAllowed, this, allowed)
+ .recycleOnUse());
}
/**
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index 370a3f7..5fe21bd 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -27,6 +27,9 @@
import static android.provider.Settings.Secure.LOCATION_MODE;
import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
+import static com.android.server.LocationManagerService.D;
+import static com.android.server.LocationManagerService.TAG;
+
import android.app.ActivityManager;
import android.content.Context;
import android.database.ContentObserver;
@@ -37,6 +40,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -423,6 +427,10 @@
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (D) {
+ Log.d(TAG, "location setting changed [u" + userId + "]: " + uri);
+ }
+
for (UserSettingChangedListener listener : mListeners) {
listener.onSettingChanged(userId);
}
diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java
index a33e2da..28f3f47 100644
--- a/services/core/java/com/android/server/location/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/UserInfoHelper.java
@@ -18,6 +18,9 @@
import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
+import static com.android.server.LocationManagerService.D;
+import static com.android.server.LocationManagerService.TAG;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -30,6 +33,7 @@
import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -122,13 +126,13 @@
case Intent.ACTION_USER_STARTED:
userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId != UserHandle.USER_NULL) {
- onUserChanged(userId, UserListener.USER_STARTED);
+ onUserStarted(userId);
}
break;
case Intent.ACTION_USER_STOPPED:
userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId != UserHandle.USER_NULL) {
- onUserChanged(userId, UserListener.USER_STOPPED);
+ onUserStopped(userId);
}
break;
case Intent.ACTION_MANAGED_PROFILE_ADDED:
@@ -161,6 +165,10 @@
return;
}
+ if (D) {
+ Log.d(TAG, "current user switched from u" + mCurrentUserId + " to u" + newUserId);
+ }
+
int oldUserId = mCurrentUserId;
mCurrentUserId = newUserId;
@@ -168,6 +176,22 @@
onUserChanged(newUserId, UserListener.USER_SWITCHED);
}
+ private void onUserStarted(@UserIdInt int userId) {
+ if (D) {
+ Log.d(TAG, "u" + userId + " started");
+ }
+
+ onUserChanged(userId, UserListener.USER_STARTED);
+ }
+
+ private void onUserStopped(@UserIdInt int userId) {
+ if (D) {
+ Log.d(TAG, "u" + userId + " stopped");
+ }
+
+ onUserChanged(userId, UserListener.USER_STOPPED);
+ }
+
private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
for (UserListener listener : mListeners) {
listener.onUserChanged(userId, change);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 18383c4..ae11c70 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 @@
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 d8cab82..d0d0f5a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3979,6 +3979,29 @@
}
/**
+ * 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}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 661297a..bae1dd3 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 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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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;
}
- 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 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());
- }
- }
- }
- }
+ // 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 snoozedForUser;
+ return snoozed;
}
/**
@@ -261,120 +249,88 @@
}
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> keyToValue = targets.get(getPkgKey(userId, pkg));
+ if (keyToValue == null) {
+ keyToValue = new ArrayMap<>();
}
- ArrayMap<String, T> pkgRecords = records.get(pkg);
- if (pkgRecords == null) {
- pkgRecords = 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};
- if (includeCurrentProfiles) {
- userIds = mUserProfiles.getCurrentProfileIds().toArray();
+ protected void cancel(int userId, boolean includeCurrentProfiles) {
+ if (mSnoozedNotifications.size() == 0) {
+ return;
}
- 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;
- }
- }
+ IntArray userIds = new IntArray();
+ userIds.add(userId);
+ if (includeCurrentProfiles) {
+ userIds = mUserProfiles.getCurrentProfileIds();
+ }
+ 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 @@
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 @@
}
protected void repostGroupSummary(String pkg, int userId, String groupKey) {
- if (mSnoozedNotifications.containsKey(userId)) {
- ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage
- = mSnoozedNotifications.get(userId);
+ ArrayMap<String, NotificationRecord> recordsByKey
+ = mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (recordsByKey == null) {
+ return;
+ }
- if (keysByPackage != null && keysByPackage.containsKey(pkg)) {
- ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg);
+ 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 (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;
- }
- }
+ if (groupSummaryKey != null) {
+ NotificationRecord record = recordsByKey.remove(groupSummaryKey);
+ mPackages.remove(groupSummaryKey);
+ mUsers.remove(groupSummaryKey);
- 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);
- }
- }
- }
+ 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 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 @@
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 @@
}
scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
}
- continue;
}
if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
final String creationId = parser.getAttributeValue(
@@ -615,18 +566,9 @@
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 c5b868f..31fa4d1 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 @@
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 064fd3f..8c6e6916 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 @@
void handleVerificationFinished() {
if (!mVerificationCompleted) {
mVerificationCompleted = true;
- if (mIntegrityVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
- mIntegrityVerificationCompleted = true;
+ if (mIntegrityVerificationCompleted) {
handleReturnCode();
}
// integrity verification still pending.
@@ -14718,8 +14717,7 @@
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/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 372c1f5..cd5e360 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -169,7 +169,7 @@
}
@Override
- public synchronized void serviceDied(long cookie) {
+ public synchronized void serviceDied(long cookie) {
Log.w(TAG, String.format("Underlying HAL driver died."));
for (Session session : mActiveSessions) {
session.moduleDied();
@@ -248,44 +248,77 @@
@Override
public int loadModel(@NonNull SoundModel model) {
Log.d(TAG, String.format("loadModel(model=%s)", model));
- synchronized (SoundTriggerModule.this) {
- checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
+
+ // We must do this outside the lock, to avoid possible deadlocks with the remote process
+ // that provides the audio sessions, which may also be calling into us.
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+
+ try {
+ synchronized (SoundTriggerModule.this) {
+ checkValid();
+ if (mNumLoadedModels == mProperties.maxSoundModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION,
+ "Maximum number of models loaded.");
+ }
+ Model loadedModel = new Model();
+ int result = loadedModel.load(model, audioSession);
+ ++mNumLoadedModels;
+ return result;
}
- Model loadedModel = new Model();
- int result = loadedModel.load(model);
- ++mNumLoadedModels;
- return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ throw e;
}
}
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
- synchronized (SoundTriggerModule.this) {
- checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
+
+ // We must do this outside the lock, to avoid possible deadlocks with the remote process
+ // that provides the audio sessions, which may also be calling into us.
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+
+ try {
+ synchronized (SoundTriggerModule.this) {
+ checkValid();
+ if (mNumLoadedModels == mProperties.maxSoundModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION,
+ "Maximum number of models loaded.");
+ }
+ Model loadedModel = new Model();
+ int result = loadedModel.load(model, audioSession);
+ ++mNumLoadedModels;
+ Log.d(TAG, String.format("loadPhraseModel()->%d", result));
+ return result;
}
- Model loadedModel = new Model();
- int result = loadedModel.load(model);
- ++mNumLoadedModels;
- Log.d(TAG, String.format("loadPhraseModel()->%d", result));
- return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ throw e;
}
}
@Override
public void unloadModel(int modelHandle) {
Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
+
+ int sessionId;
+
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).unload();
+ sessionId = mLoadedModels.get(modelHandle).unload();
--mNumLoadedModels;
}
+
+ // We must do this outside the lock, to avoid possible deadlocks with the remote process
+ // that provides the audio sessions, which may also be calling into us.
+ mAudioSessionProvider.releaseSession(sessionId);
}
@Override
@@ -413,45 +446,40 @@
SoundTriggerModule.this.notifyAll();
}
- private int load(@NonNull SoundModel model) {
+ private int load(@NonNull SoundModel model,
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
mModelType = model.type;
+ mSession = audioSession;
ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
- mSession = mAudioSessionProvider.acquireSession();
- try {
- mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
- } catch (Exception e) {
- mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
- throw e;
- }
-
+ mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
}
- private int load(@NonNull PhraseSoundModel model) {
+ private int load(@NonNull PhraseSoundModel model,
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
mModelType = model.common.type;
+ mSession = audioSession;
ISoundTriggerHw.PhraseSoundModel hidlModel =
ConversionUtil.aidl2hidlPhraseSoundModel(model);
- mSession = mAudioSessionProvider.acquireSession();
- try {
- mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
- } catch (Exception e) {
- mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
- throw e;
- }
+ mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
}
- private void unload() {
- mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
+ /**
+ * Unloads the model.
+ * @return The audio session handle.
+ */
+ private int unload() {
mHalService.unloadSoundModel(mHandle);
mLoadedModels.remove(mHandle);
+ return mSession.mSessionHandle;
}
private void startRecognition(@NonNull RecognitionConfig config) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e26dac6..084800c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2679,7 +2679,8 @@
// DisplayContent#topRunningActivity().
final ActivityRecord next = display.topRunningActivity();
final boolean isLastStackOverEmptyHome =
- next == null && stack.isFocusedStackOnDisplay() && display.getRootHomeTask() != null;
+ next == null && stack.isFocusedStackOnDisplay()
+ && display.getOrCreateRootHomeTask() != null;
if (isLastStackOverEmptyHome) {
// Don't destroy activity immediately if this is the last activity on the display and
// the display contains home stack. Although there is no next activity at the moment,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8ed798c..e77d8ae 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1374,7 +1374,7 @@
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
final ActivityStack homeStack =
- startedActivityStack.getDisplay().getRootHomeTask();
+ startedActivityStack.getDisplay().getOrCreateRootHomeTask();
if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
mService.mWindowManager.showRecentApps();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 314e866..f777e90 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5552,11 +5552,20 @@
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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index cafe29e..55a41ab 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1934,6 +1934,20 @@
return mTaskContainers.getRootHomeTask();
}
+ /**
+ * Returns the existing home stack or creates and returns a new one if it should exist for the
+ * display.
+ */
+ @Nullable
+ ActivityStack getOrCreateRootHomeTask() {
+ ActivityStack homeTask = getRootHomeTask();
+ if (homeTask == null && supportsSystemDecorations() && !isUntrustedVirtualDisplay()) {
+ homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
+ false /* onTop */);
+ }
+ return homeTask;
+ }
+
/** @return The primary split-screen task, and {@code null} otherwise. */
ActivityStack getRootSplitScreenPrimaryTask() {
return mTaskContainers.getRootSplitScreenPrimaryTask();
@@ -3206,9 +3220,9 @@
mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
mInputMethodWindow.getDisplayId());
}
- computeImeTarget(true /* updateImeTarget */);
mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
null /* frameProvider */, null /* imeFrameProvider */);
+ computeImeTarget(true /* updateImeTarget */);
}
/**
@@ -3766,6 +3780,10 @@
}
prepareSurfaces();
+ // This should be called after the insets have been dispatched to clients and we have
+ // committed finish drawing windows.
+ mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
+
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
@@ -4204,9 +4222,6 @@
}
ActivityStack getRootHomeTask() {
- if (mRootHomeTask == null && mDisplayId == DEFAULT_DISPLAY) {
- Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
- }
return mRootHomeTask;
}
@@ -5983,7 +5998,7 @@
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityStack homeStack = getRootHomeTask();
+ final ActivityStack homeStack = getOrCreateRootHomeTask();
if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
// Whenever split-screen is dismissed we want the home stack directly behind the
// current top fullscreen stack so it shows up when the top stack is finished.
@@ -6528,7 +6543,7 @@
}
void moveHomeStackToFront(String reason) {
- final ActivityStack homeStack = getRootHomeTask();
+ final ActivityStack homeStack = getOrCreateRootHomeTask();
if (homeStack != null) {
homeStack.moveToFront(reason);
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 3ff1b95..370d125 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -39,13 +39,6 @@
}
/**
- * Called when Insets have been dispatched to client. This gets called just after onPostLayout.
- */
- void onPostInsetsDispatched() {
- checkShowImePostLayout();
- }
-
- /**
* Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
* requests to show IME on {@param imeTarget}.
*
@@ -80,7 +73,7 @@
};
}
- private void checkShowImePostLayout() {
+ void checkShowImePostLayout() {
// check if IME is drawn
if (mIsImeLayoutDrawn
|| (mImeTargetFromIme != null
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 2d7d3f1..a8fe349 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -168,7 +168,6 @@
mLastState.set(mState, true /* copySources */);
notifyInsetsChanged();
}
- getImeSourceProvider().onPostInsetsDispatched();
}
void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2196d89..2596452 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1998,8 +1998,6 @@
removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId =
- mUserStackInFront.get(userId, getDefaultDisplay().getRootHomeTask().getRootTaskId());
mCurrentUser = userId;
mStackSupervisor.mStartingUsers.add(uss);
@@ -2008,16 +2006,13 @@
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);
- }
}
}
+ final int restoreStackId = mUserStackInFront.get(userId);
ActivityStack stack = getStack(restoreStackId);
if (stack == null) {
- stack = getDefaultDisplay().getRootHomeTask();
+ stack = getDefaultDisplay().getOrCreateRootHomeTask();
}
final boolean homeInFront = stack.isActivityTypeHome();
if (stack.isOnHomeDisplay()) {
@@ -2039,8 +2034,11 @@
*/
void updateUserStack(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getRootTaskId()
- : getDefaultDisplay().getRootHomeTask().getRootTaskId());
+ if (stack == null) {
+ stack = getDefaultDisplay().getOrCreateRootHomeTask();
+ }
+
+ mUserStackInFront.put(userId, stack.getRootTaskId());
}
}
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 2dde0ba..f715d8f 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 @@
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 @@
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 @@
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 @@
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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f68f8b6..ed4e684 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1493,7 +1493,8 @@
// Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
// associate them with some stack to enable dimming.
final DisplayContent dc = getDisplayContent();
- return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getRootHomeTask() : null;
+ return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null
+ ? dc.getOrCreateRootHomeTask() : null;
}
/**
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index 5545656..636d110 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -31,4 +31,12 @@
"-Wall",
"-Werror",
],
+ data: [
+ ":default-permissions",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core"
+ ],
+ test_config: "vts_defaultPermissions_validate_test.xml",
}
diff --git a/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
new file mode 100644
index 0000000..1ccacae
--- /dev/null
+++ b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<configuration description="Runs vts_defaultPermissions_validate_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="default-permissions.xsd->/data/local/tmp/default-permissions.xsd" />
+ <option name="push" value="vts_defaultPermissions_validate_test->/data/local/tmp/vts_defaultPermissions_validate_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_defaultPermissions_validate_test" />
+ </test>
+</configuration>
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 93662c9..1936f13 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1741,7 +1741,7 @@
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 2d18a29..663bf4f 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.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 @@
});
}
+ @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 @@
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 ea36d38..3afb209 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 @@
}
@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 @@
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 0000000..58f0654
--- /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 a904b42..2d7c937 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.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.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.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 @@
private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
private final SparseArray<NotificationListenerService> mNotificationListeners =
new SparseArray<>();
- private final ContentObserver mCallLogContentObserver;
- private final ContentObserver mMmsSmsContentObserver;
+ private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
+ private ContentObserver mCallLogContentObserver;
+ private ContentObserver mMmsSmsContentObserver;
private ShortcutServiceInternal mShortcutServiceInternal;
+ private PackageManagerInternal mPackageManagerInternal;
private ShortcutManager mShortcutManager;
private UserManager mUserManager;
@@ -110,16 +118,13 @@
mContext = context;
mInjector = injector;
mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
- mCallLogContentObserver = new CallLogContentObserver(
- BackgroundThread.getHandler());
- mMmsSmsContentObserver = new MmsSmsContentObserver(
- BackgroundThread.getHandler());
mDiskReadWriterExecutor = mInjector.createScheduledExecutor();
}
/** 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,17 +177,26 @@
// 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.
// TODO: Register observers after the conversations and events being loaded from disk.
+ mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
mCallLogContentObserver, UserHandle.USER_SYSTEM);
+
+ mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
}
+
+ DataMaintenanceService.scheduleJob(mContext, userId);
}
/** This method is called when a user is stopped. */
@@ -207,10 +221,21 @@
// Should never occur for local calls.
}
}
- if (userId == UserHandle.USER_SYSTEM) {
- mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
- mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+ if (mPackageMonitors.indexOfKey(userId) >= 0) {
+ mPackageMonitors.get(userId).unregister();
}
+ if (userId == UserHandle.USER_SYSTEM) {
+ if (mCallLogContentObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
+ mCallLogContentObserver = null;
+ }
+ if (mMmsSmsContentObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+ mCallLogContentObserver = null;
+ }
+ }
+
+ DataMaintenanceService.cancelJob(mContext, userId);
}
/**
@@ -274,9 +299,8 @@
|| 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 +315,45 @@
}
}
+ /** 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 +410,8 @@
|| packageData.getConversationStore().getConversation(shortcutId) == null) {
return null;
}
- return packageData.getEventStore().getOrCreateShortcutEventHistory(shortcutId);
+ return packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
}
@VisibleForTesting
@@ -413,6 +477,11 @@
}
@VisibleForTesting
+ PackageMonitor getPackageMonitorForTesting(@UserIdInt int userId) {
+ return mPackageMonitors.get(userId);
+ }
+
+ @VisibleForTesting
UserData getUserDataForTesting(@UserIdInt int userId) {
return mUserDataArray.get(userId);
}
@@ -499,7 +568,8 @@
return;
}
EventStore eventStore = defaultDialer.getEventStore();
- eventStore.getOrCreateCallEventHistory(phoneNumber).addEvent(event);
+ eventStore.getOrCreateEventHistory(
+ EventStore.CATEGORY_CALL, phoneNumber).addEvent(event);
});
}
}
@@ -544,7 +614,8 @@
return;
}
EventStore eventStore = defaultSmsApp.getEventStore();
- eventStore.getOrCreateSmsEventHistory(phoneNumber).addEvent(event);
+ eventStore.getOrCreateEventHistory(
+ EventStore.CATEGORY_SMS, phoneNumber).addEvent(event);
});
}
}
@@ -670,6 +741,20 @@
}
}
+ 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 6b6bd7e..6bef1db 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 @@
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 b267d66..d770f91 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 @@
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 d6b7a86..c8d44ac 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);
- }
-
- /** 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. */
- @Nullable
- EventHistory getSmsEventHistory(String phoneNumber) {
- return mSmsEventHistoryMap.get(phoneNumber);
+ 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 {@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 EventHistory} for the specified key if exists.
+ *
+ * @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());
+ @Nullable
+ EventHistory getEventHistory(@EventCategory int category, String key) {
+ return mEventHistoryMaps.get(category).get(key);
}
/**
- * 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.
+ * 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 getOrCreateLocusEventHistory(LocusId locusId) {
- return mLocusEventHistoryMap.computeIfAbsent(locusId, 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 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.
+ * 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 getOrCreateCallEventHistory(String phoneNumber) {
- return mCallEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ void deleteEventHistory(@EventCategory int category, String key) {
+ EventHistoryImpl eventHistory = mEventHistoryMaps.get(category).remove(key);
+ if (eventHistory != null) {
+ eventHistory.onDestroy();
+ }
+ }
+
+ /** 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 f67699c..c55f972 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 @@
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 @@
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 @@
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 @@
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 @@
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 fba4f4e..6dcfaa0 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -129,8 +129,8 @@
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 @@
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 @@
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 aaa5db8..7ca4b6c 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 @@
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 64da6f6..d7a3cfd 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 @@
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 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.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 @@
@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 @@
.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 @@
}
}
+ 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/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index bbc6bdb..d34f783 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -26,11 +26,6 @@
<option name="test-file-name" value="SimpleServiceTestApp.apk" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="AppIntegrityManagerServiceTestApp.apk->/data/local/tmp/AppIntegrityManagerServiceTestApp.apk" />
- </target_preparer>
-
<option name="test-tag" value="FrameworksServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk
new file mode 100644
index 0000000..cc1f68c
--- /dev/null
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk
Binary files differ
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 8f70cca..7b2b30b 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 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(new ArrayList<>())
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2105,7 +2105,7 @@
setupProfileOwner();
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
assertExpectException(SecurityException.class, null,
@@ -2157,7 +2157,7 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(new ArrayList<>())
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2177,8 +2177,8 @@
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 bc853c6..e8818a3 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 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(accounts)
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
testParcelAndUnparcel(policy);
@@ -77,7 +77,7 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(accounts)
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
testParcelAndUnparcel(policy);
@@ -133,8 +133,8 @@
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/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 0d4c6e8..be873bd 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -90,7 +90,7 @@
@RunWith(JUnit4.class)
public class AppIntegrityManagerServiceImplTest {
private static final String TEST_APP_PATH =
- "/data/local/tmp/AppIntegrityManagerServiceTestApp.apk";
+ "AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk";
private static final String TEST_APP_TWO_CERT_PATH =
"AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
@@ -106,9 +106,7 @@
// These are obtained by running the test and checking logcat.
private static final String APP_CERT =
- "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
- private static final String INSTALLER_CERT =
- "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
+ "C8A2E9BCCF597C2FB6DC66BEE293FC13F2FC47EC77BC6B2B0D52C11F51192AB8";
// We use SHA256 for package names longer than 32 characters.
private static final String INSTALLER_SHA256 =
"30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
@@ -149,8 +147,12 @@
@Before
public void setup() throws Exception {
- mTestApk = new File(TEST_APP_PATH);
- mTestApkTwoCerts = File.createTempFile("AppIntegrity", ".apk");
+ mTestApk = File.createTempFile("AppIntegrity", ".apk");
+ try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_PATH)) {
+ Files.copy(inputStream, mTestApk.toPath(), REPLACE_EXISTING);
+ }
+
+ mTestApkTwoCerts = File.createTempFile("AppIntegrityTwoCerts", ".apk");
try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
}
@@ -174,6 +176,7 @@
@After
public void tearDown() throws Exception {
+ mTestApk.delete();
mTestApkTwoCerts.delete();
}
@@ -304,7 +307,7 @@
assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
- assertThat(appInstallMetadata.getInstallerCertificates()).containsExactly(INSTALLER_CERT);
+ // we cannot check installer cert because it seems to be device specific.
assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
assertFalse(appInstallMetadata.isPreInstalled());
// These are hardcoded in the test apk android manifest
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 6769faa..0bb984e 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 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.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.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.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.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 @@
@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 @@
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
private int mCallingUserId;
+ private CancellationSignal mCancellationSignal;
private TestInjector mInjector;
@Before
@@ -124,6 +135,15 @@
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 @@
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 @@
mCallingUserId = USER_ID_PRIMARY;
+ mCancellationSignal = new CancellationSignal();
+
mInjector = new TestInjector();
mDataManager = new DataManager(mContext, mInjector);
mDataManager.initialize();
@@ -177,6 +203,7 @@
public void tearDown() {
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@Test
@@ -454,6 +481,101 @@
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 1ddc21e..e52cdf5 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 @@
@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 @@
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 @@
@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 @@
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 c8543c4..dc4876b 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
@@ -288,14 +288,13 @@
@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/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp
deleted file mode 100644
index 9aaa37d..0000000
--- a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-android_test_helper_app {
- name: "AppIntegrityManagerServiceTestApp",
-
- test_suites: ["device-tests"],
-
- certificate: "platform",
-}
diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
deleted file mode 100644
index 98572d4..0000000
--- a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 Google Inc.
- *
- * 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.google.android.appintegritymanager.test.app"
- android:versionCode="5000">
-
- <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="28" />
-
- <application android:hasCode="false">
- <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb"/>
- </application>
-</manifest>
-
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 92d47c3..bc33f08 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2622,6 +2622,33 @@
}
@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");
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 3186d53..1dd0b1a 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 @@
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 @@
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 @@
}
@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 9647178..e8c0362 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 @@
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/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2ea00ce..3b6816a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1033,6 +1033,54 @@
assertTrue(continued[0]);
}
+ @Test
+ public void testGetOrCreateRootHomeTask_defaultDisplay() {
+ DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
+
+ // Remove the current home stack if it exists so a new one can be created below.
+ ActivityStack homeTask = defaultDisplay.getRootHomeTask();
+ if (homeTask != null) {
+ defaultDisplay.removeStack(homeTask);
+ }
+ assertNull(defaultDisplay.getRootHomeTask());
+
+ assertNotNull(defaultDisplay.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
+ DisplayContent display = createNewDisplay();
+ doReturn(true).when(display).supportsSystemDecorations();
+ doReturn(false).when(display).isUntrustedVirtualDisplay();
+
+ // Remove the current home stack if it exists so a new one can be created below.
+ ActivityStack homeTask = display.getRootHomeTask();
+ if (homeTask != null) {
+ display.removeStack(homeTask);
+ }
+ assertNull(display.getRootHomeTask());
+
+ assertNotNull(display.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
+ DisplayContent display = createNewDisplay();
+ doReturn(false).when(display).supportsSystemDecorations();
+
+ assertNull(display.getRootHomeTask());
+ assertNull(display.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_untrustedVirtualDisplay() {
+ DisplayContent display = createNewDisplay();
+ doReturn(true).when(display).isUntrustedVirtualDisplay();
+
+ assertNull(display.getRootHomeTask());
+ assertNull(display.getOrCreateRootHomeTask());
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 79db927..dd46673 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -860,6 +860,26 @@
secondaryDisplay.mDisplayId, result.getDisplayId());
}
+ @Test
+ public void testSwitchUser_missingHomeRootTask() {
+ doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
+
+ DisplayContent defaultDisplay = mRootWindowContainer.getDefaultDisplay();
+ ActivityStack homeStack = defaultDisplay.getRootHomeTask();
+ if (homeStack != null) {
+ homeStack.removeImmediately();
+ }
+ assertNull(defaultDisplay.getRootHomeTask());
+
+ int currentUser = mRootWindowContainer.mCurrentUser;
+ int otherUser = currentUser + 1;
+
+ mRootWindowContainer.switchUser(otherUser, null);
+
+ assertNotNull(defaultDisplay.getRootHomeTask());
+ assertEquals(defaultDisplay.getTopStack(), defaultDisplay.getRootHomeTask());
+ }
+
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
* info for test cases (the original implementation will resolve from the real package manager).
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 51b4a31..c51a852 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2377,7 +2377,7 @@
* {@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 @@
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 1cd45e9..2529387 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -181,7 +181,7 @@
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 @@
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 c40573b..6fdc13e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -492,6 +492,7 @@
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 95b8f67..da45d9a 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 @@
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/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index dc5ac1e..e1450cb 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -486,7 +486,7 @@
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- requirePMF = true;
+ requirePmf = true;
break;
case SECURITY_TYPE_EAP_SUITE_B:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
@@ -496,14 +496,14 @@
allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
// Note: allowedSuiteBCiphers bitset will be set by the service once the
// certificates are attached to this profile
- requirePMF = true;
+ requirePmf = true;
break;
case SECURITY_TYPE_OWE:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- requirePMF = true;
+ requirePmf = true;
break;
case SECURITY_TYPE_WAPI_PSK:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK);
@@ -662,7 +662,7 @@
* @hide
*/
@SystemApi
- public boolean requirePMF;
+ public boolean requirePmf;
/**
* Update identifier, for Passpoint network.
@@ -2169,7 +2169,7 @@
append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
.append(" PRIO: ").append(this.priority)
.append(" HIDDEN: ").append(this.hiddenSSID)
- .append(" PMF: ").append(this.requirePMF)
+ .append(" PMF: ").append(this.requirePmf)
.append("CarrierId: ").append(this.carrierId)
.append('\n');
@@ -2761,7 +2761,7 @@
mRandomizedMacAddress = source.mRandomizedMacAddress;
macRandomizationSetting = source.macRandomizationSetting;
randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
- requirePMF = source.requirePMF;
+ requirePmf = source.requirePmf;
updateIdentifier = source.updateIdentifier;
carrierId = source.carrierId;
mPasspointUniqueId = source.mPasspointUniqueId;
@@ -2793,7 +2793,7 @@
dest.writeInt(wepTxKeyIndex);
dest.writeInt(priority);
dest.writeInt(hiddenSSID ? 1 : 0);
- dest.writeInt(requirePMF ? 1 : 0);
+ dest.writeInt(requirePmf ? 1 : 0);
dest.writeString(updateIdentifier);
writeBitSet(dest, allowedKeyManagement);
@@ -2869,7 +2869,7 @@
config.wepTxKeyIndex = in.readInt();
config.priority = in.readInt();
config.hiddenSSID = in.readInt() != 0;
- config.requirePMF = in.readInt() != 0;
+ config.requirePmf = in.readInt() != 0;
config.updateIdentifier = in.readString();
config.allowedKeyManagement = readBitSet(in);
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 05a3dce..00790d5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -363,7 +363,7 @@
assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE));
assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(config.requirePMF);
+ assertTrue(config.requirePmf);
}
/**
@@ -380,7 +380,7 @@
assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE));
assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(config.requirePMF);
+ assertTrue(config.requirePmf);
}
/**
@@ -399,7 +399,7 @@
assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
assertTrue(config.allowedGroupManagementCiphers
.get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
- assertTrue(config.requirePMF);
+ assertTrue(config.requirePmf);
}
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index af6fb5c..51bf738 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -140,7 +140,7 @@
assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.OWE));
assertNull(suggestion.wifiConfiguration.preSharedKey);
- assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
assertFalse(suggestion.isUserAllowedToManuallyConnect);
assertTrue(suggestion.isInitialAutoJoinEnabled);
}
@@ -163,7 +163,7 @@
.get(WifiConfiguration.KeyMgmt.SAE));
assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
suggestion.wifiConfiguration.preSharedKey);
- assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
assertTrue(suggestion.isUserAllowedToManuallyConnect);
assertFalse(suggestion.isInitialAutoJoinEnabled);
}
@@ -191,7 +191,7 @@
.get(WifiConfiguration.GroupCipher.GCMP_256));
assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
.get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
- assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
assertNull(suggestion.wifiConfiguration.preSharedKey);
// allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
// here.