Merge "Always submit after texture uploads" into sc-dev
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index b52a503..666d497 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -795,7 +795,7 @@
AppSearchUserInstance instance =
mAppSearchUserInstanceManager.getUserInstance(callingUser);
SearchResultPage searchResultPage =
- instance.getAppSearchImpl().getNextPage(nextPageToken);
+ instance.getAppSearchImpl().getNextPage(packageName, nextPageToken);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -821,7 +821,7 @@
verifyNotInstantApp(userContext, packageName);
AppSearchUserInstance instance =
mAppSearchUserInstanceManager.getUserInstance(callingUser);
- instance.getAppSearchImpl().invalidateNextPageToken(nextPageToken);
+ instance.getAppSearchImpl().invalidateNextPageToken(packageName, nextPageToken);
} catch (Throwable t) {
Log.e(TAG, "Unable to invalidate the query page token", t);
}
@@ -871,7 +871,7 @@
.getGenericDocument().getBundle());
}
searchResultPage = instance.getAppSearchImpl().getNextPage(
- searchResultPage.getNextPageToken());
+ packageName, searchResultPage.getNextPageToken());
}
}
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index a1b93ce..830e76c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -173,6 +173,21 @@
@GuardedBy("mReadWriteLock")
private final Map<String, Integer> mDocumentCountMapLocked = new ArrayMap<>();
+ // Maps packages to the set of valid nextPageTokens that the package can manipulate. A token
+ // is unique and constant per query (i.e. the same token '123' is used to iterate through
+ // pages of search results). The tokens themselves are generated and tracked by
+ // IcingSearchEngine. IcingSearchEngine considers a token valid and won't be reused
+ // until we call invalidateNextPageToken on the token.
+ //
+ // Note that we synchronize on itself because the nextPageToken cache is checked at
+ // query-time, and queries are done in parallel with a read lock. Ideally, this would be
+ // guarded by the normal mReadWriteLock.writeLock, but ReentrantReadWriteLocks can't upgrade
+ // read to write locks. This lock should be acquired at the smallest scope possible.
+ // mReadWriteLock is a higher-level lock, so calls shouldn't be made out
+ // to any functions that grab the lock.
+ @GuardedBy("mNextPageTokensLocked")
+ private final Map<String, Set<Long>> mNextPageTokensLocked = new ArrayMap<>();
+
/**
* The counter to check when to call {@link #checkForOptimize}. The interval is {@link
* #CHECK_OPTIMIZE_INTERVAL}.
@@ -837,12 +852,15 @@
String prefix = createPrefix(packageName, databaseName);
Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemasLocked(prefix, searchSpec);
- return doQueryLocked(
- Collections.singleton(createPrefix(packageName, databaseName)),
- allowedPrefixedSchemas,
- queryExpression,
- searchSpec,
- sStatsBuilder);
+ SearchResultPage searchResultPage =
+ doQueryLocked(
+ Collections.singleton(createPrefix(packageName, databaseName)),
+ allowedPrefixedSchemas,
+ queryExpression,
+ searchSpec,
+ sStatsBuilder);
+ addNextPageToken(packageName, searchResultPage.getNextPageToken());
+ return searchResultPage;
} finally {
mReadWriteLock.readLock().unlock();
if (logger != null) {
@@ -956,12 +974,15 @@
}
}
- return doQueryLocked(
- prefixFilters,
- prefixedSchemaFilters,
- queryExpression,
- searchSpec,
- sStatsBuilder);
+ SearchResultPage searchResultPage =
+ doQueryLocked(
+ prefixFilters,
+ prefixedSchemaFilters,
+ queryExpression,
+ searchSpec,
+ sStatsBuilder);
+ addNextPageToken(callerPackageName, searchResultPage.getNextPageToken());
+ return searchResultPage;
} finally {
mReadWriteLock.readLock().unlock();
@@ -1093,17 +1114,20 @@
*
* <p>This method belongs to query group.
*
+ * @param packageName Package name of the caller.
* @param nextPageToken The token of pre-loaded results of previously executed query.
* @return The next page of results of previously executed query.
- * @throws AppSearchException on IcingSearchEngine error.
+ * @throws AppSearchException on IcingSearchEngine error or if can't advance on nextPageToken.
*/
@NonNull
- public SearchResultPage getNextPage(long nextPageToken) throws AppSearchException {
+ public SearchResultPage getNextPage(@NonNull String packageName, long nextPageToken)
+ throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
mLogUtil.piiTrace("getNextPage, request", nextPageToken);
+ checkNextPageToken(packageName, nextPageToken);
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken);
mLogUtil.piiTrace(
@@ -1122,16 +1146,26 @@
*
* <p>This method belongs to query group.
*
+ * @param packageName Package name of the caller.
* @param nextPageToken The token of pre-loaded results of previously executed query to be
* Invalidated.
+ * @throws AppSearchException if nextPageToken is unusable.
*/
- public void invalidateNextPageToken(long nextPageToken) {
+ public void invalidateNextPageToken(@NonNull String packageName, long nextPageToken)
+ throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
mLogUtil.piiTrace("invalidateNextPageToken, request", nextPageToken);
+ checkNextPageToken(packageName, nextPageToken);
mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken);
+
+ synchronized (mNextPageTokensLocked) {
+ // At this point, we're guaranteed that this nextPageToken exists for this package,
+ // otherwise checkNextPageToken would've thrown an exception.
+ mNextPageTokensLocked.get(packageName).remove(nextPageToken);
+ }
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -1568,6 +1602,9 @@
Set<String> databaseNames = entry.getValue();
if (!installedPackages.contains(packageName) && databaseNames != null) {
mDocumentCountMapLocked.remove(packageName);
+ synchronized (mNextPageTokensLocked) {
+ mNextPageTokensLocked.remove(packageName);
+ }
for (String databaseName : databaseNames) {
String removedPrefix = createPrefix(packageName, databaseName);
mSchemaMapLocked.remove(removedPrefix);
@@ -1601,6 +1638,9 @@
mSchemaMapLocked.clear();
mNamespaceMapLocked.clear();
mDocumentCountMapLocked.clear();
+ synchronized (mNextPageTokensLocked) {
+ mNextPageTokensLocked.clear();
+ }
if (initStatsBuilder != null) {
initStatsBuilder
.setHasReset(true)
@@ -2015,6 +2055,32 @@
return schemaProto.getSchema();
}
+ private void addNextPageToken(String packageName, long nextPageToken) {
+ synchronized (mNextPageTokensLocked) {
+ Set<Long> tokens = mNextPageTokensLocked.get(packageName);
+ if (tokens == null) {
+ tokens = new ArraySet<>();
+ mNextPageTokensLocked.put(packageName, tokens);
+ }
+ tokens.add(nextPageToken);
+ }
+ }
+
+ private void checkNextPageToken(String packageName, long nextPageToken)
+ throws AppSearchException {
+ synchronized (mNextPageTokensLocked) {
+ Set<Long> nextPageTokens = mNextPageTokensLocked.get(packageName);
+ if (nextPageTokens == null || !nextPageTokens.contains(nextPageToken)) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_SECURITY_ERROR,
+ "Package \""
+ + packageName
+ + "\" cannot use nextPageToken: "
+ + nextPageToken);
+ }
+ }
+ }
+
private static void addToMap(
Map<String, Set<String>> map, String prefix, String prefixedValue) {
Set<String> values = map.get(prefix);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index ed80ddb..9f52954 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -256,6 +256,7 @@
AlarmHandler mHandler;
AppWakeupHistory mAppWakeupHistory;
AppWakeupHistory mAllowWhileIdleHistory;
+ AppWakeupHistory mAllowWhileIdleCompatHistory;
private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
ClockReceiver mClockReceiver;
@@ -1633,6 +1634,7 @@
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
mAllowWhileIdleHistory = new AppWakeupHistory(INTERVAL_HOUR);
+ mAllowWhileIdleCompatHistory = new AppWakeupHistory(INTERVAL_HOUR);
mNextWakeup = mNextNonWakeup = 0;
@@ -2142,20 +2144,23 @@
final int userId = UserHandle.getUserId(alarm.creatorUid);
final int quota;
final long window;
+ final AppWakeupHistory history;
if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) {
quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
window = mConstants.ALLOW_WHILE_IDLE_WINDOW;
+ history = mAllowWhileIdleHistory;
} else {
quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
+ history = mAllowWhileIdleCompatHistory;
}
- final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
+ final int dispatchesInHistory = history.getTotalWakeupsInWindow(
alarm.sourcePackage, userId);
- if (dispatchesInWindow < quota) {
+ if (dispatchesInHistory < quota) {
// fine to go out immediately.
batterySaverPolicyElapsed = nowElapsed;
} else {
- batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
+ batterySaverPolicyElapsed = history.getNthLastWakeupForPackage(
alarm.sourcePackage, userId, quota) + window;
}
} else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
@@ -2201,20 +2206,23 @@
final int userId = UserHandle.getUserId(alarm.creatorUid);
final int quota;
final long window;
+ final AppWakeupHistory history;
if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) {
quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
window = mConstants.ALLOW_WHILE_IDLE_WINDOW;
+ history = mAllowWhileIdleHistory;
} else {
quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
+ history = mAllowWhileIdleCompatHistory;
}
- final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
+ final int dispatchesInHistory = history.getTotalWakeupsInWindow(
alarm.sourcePackage, userId);
- if (dispatchesInWindow < quota) {
+ if (dispatchesInHistory < quota) {
// fine to go out immediately.
deviceIdlePolicyTime = nowElapsed;
} else {
- final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
+ final long whenInQuota = history.getNthLastWakeupForPackage(
alarm.sourcePackage, userId, quota) + window;
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
}
@@ -2502,6 +2510,7 @@
Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
// The API doesn't allow using both together.
flags &= ~FLAG_ALLOW_WHILE_IDLE;
+ // Prioritized alarms don't need any extra permission to be exact.
} else if (exact || allowWhileIdle) {
final boolean needsPermission;
boolean lowerQuota;
@@ -2992,6 +3001,10 @@
mAllowWhileIdleHistory.dump(pw, nowELAPSED);
pw.println();
+ pw.println("Allow while idle compat history:");
+ mAllowWhileIdleCompatHistory.dump(pw, nowELAPSED);
+ pw.println();
+
if (mLastPriorityAlarmDispatch.size() > 0) {
pw.println("Last priority alarm dispatches:");
pw.increaseIndent();
@@ -4553,6 +4566,7 @@
removeUserLocked(userHandle);
mAppWakeupHistory.removeForUser(userHandle);
mAllowWhileIdleHistory.removeForUser(userHandle);
+ mAllowWhileIdleCompatHistory.removeForUser(userHandle);
}
return;
case Intent.ACTION_UID_REMOVED:
@@ -4588,6 +4602,8 @@
// package-removed and package-restarted case
mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
+ mAllowWhileIdleCompatHistory.removeForPackage(pkg,
+ UserHandle.getUserId(uid));
removeLocked(uid, REMOVE_REASON_UNDEFINED);
} else {
// external-applications-unavailable case
@@ -4965,7 +4981,10 @@
if (isAllowedWhileIdleRestricted(alarm)) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
// device was in doze or battery saver.
- mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
+ final AppWakeupHistory history = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
+ ? mAllowWhileIdleHistory
+ : mAllowWhileIdleCompatHistory;
+ history.recordAlarmForPackage(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
mAlarmStore.updateAlarmDeliveries(a -> {
if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 91e2d88..4376d22 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -406,6 +406,12 @@
public static final int BROADCAST_FAILED_USER_STOPPED = -2;
/**
+ * Type for IActivityManaqer.getIntentSender: this PendingIntent type is unknown.
+ * @hide
+ */
+ public static final int INTENT_SENDER_UNKNOWN = 0;
+
+ /**
* Type for IActivityManaqer.getIntentSender: this PendingIntent is
* for a sendBroadcast operation.
* @hide
@@ -4860,12 +4866,12 @@
*/
public static final class PendingIntentInfo implements Parcelable {
- private final String mCreatorPackage;
+ @Nullable private final String mCreatorPackage;
private final int mCreatorUid;
private final boolean mImmutable;
private final int mIntentSenderType;
- public PendingIntentInfo(String creatorPackage, int creatorUid, boolean immutable,
+ public PendingIntentInfo(@Nullable String creatorPackage, int creatorUid, boolean immutable,
int intentSenderType) {
mCreatorPackage = creatorPackage;
mCreatorUid = creatorUid;
@@ -4873,6 +4879,7 @@
mIntentSenderType = intentSenderType;
}
+ @Nullable
public String getCreatorPackage() {
return mCreatorPackage;
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 444cc4e..85758a9 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -28,11 +28,13 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
+import android.view.DisplayCutout;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -174,6 +176,15 @@
public PictureInPictureParams pictureInPictureParams;
/**
+ * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of
+ * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS),
+ * {@code null} otherwise.
+ * @hide
+ */
+ @Nullable
+ public Rect displayCutoutInsets;
+
+ /**
* The activity type of the top activity in this task.
* @hide
*/
@@ -332,6 +343,7 @@
&& supportsMultiWindow == that.supportsMultiWindow
&& Objects.equals(positionInParent, that.positionInParent)
&& Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
+ && Objects.equals(displayCutoutInsets, that.displayCutoutInsets)
&& getWindowingMode() == that.getWindowingMode()
&& Objects.equals(taskDescription, that.taskDescription)
&& isFocused == that.isFocused
@@ -382,6 +394,7 @@
token = WindowContainerToken.CREATOR.createFromParcel(source);
topActivityType = source.readInt();
pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
+ displayCutoutInsets = source.readTypedObject(Rect.CREATOR);
topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
isResizeable = source.readBoolean();
source.readBinderList(launchCookies);
@@ -419,6 +432,7 @@
token.writeToParcel(dest, flags);
dest.writeInt(topActivityType);
dest.writeTypedObject(pictureInPictureParams, flags);
+ dest.writeTypedObject(displayCutoutInsets, flags);
dest.writeTypedObject(topActivityInfo, flags);
dest.writeBoolean(isResizeable);
dest.writeBinderList(launchCookies);
@@ -447,6 +461,7 @@
+ " token=" + token
+ " topActivityType=" + topActivityType
+ " pictureInPictureParams=" + pictureInPictureParams
+ + " displayCutoutSafeInsets=" + displayCutoutInsets
+ " topActivityInfo=" + topActivityInfo
+ " launchCookies=" + launchCookies
+ " positionInParent=" + positionInParent
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 0e22705..bdb7900 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -252,7 +252,8 @@
*/
public boolean checkCallingUid() {
final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID
+ if (callingUid != Process.ROOT_UID
+ && callingUid != Process.SYSTEM_UID
&& callingUid != mAttributionSourceState.uid) {
return false;
}
diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java
index 35d00f0..9309c5b 100644
--- a/core/java/android/hardware/location/ContextHubClientCallback.java
+++ b/core/java/android/hardware/location/ContextHubClientCallback.java
@@ -68,8 +68,11 @@
/**
* Callback invoked when a nanoapp is dynamically loaded at the attached Context Hub through
- * the {@link android.hardware.location.ContextHubManager}. This callback is not invoked for a
- * nanoapp that is loaded internally by CHRE (e.g. nanoapps that are preloaded by the system).
+ * the {@link android.hardware.location.ContextHubManager}.
+ *
+ * NOTE: This callback is <b>not</b> invoked for a nanoapp that is loaded internally by CHRE
+ * (e.g. nanoapps that are preloaded by the system). To check the availability of these
+ * nanoapps, use the {@link ContextHubManager#queryNanoApps(ContextHubInfo)} API.
*
* @param client the client that is associated with this callback
* @param nanoAppId the ID of the nanoapp that had been loaded
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index f69a7d7..9af0e09 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -128,7 +128,8 @@
public static final int AUTHORIZATION_GRANTED = 2;
/**
- * Constants describing the type of events from a Context Hub.
+ * Constants describing the type of events from a Context Hub, as defined in
+ * {@link ContextHubClientCallback}.
* {@hide}
*/
@Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2806850..3550a31 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1955,22 +1955,23 @@
return mBoundsLayer;
}
- Surface getOrCreateBLASTSurface(int width, int height,
- @Nullable WindowManager.LayoutParams params) {
+ Surface getOrCreateBLASTSurface() {
if (!mSurfaceControl.isValid()) {
return null;
}
- int format = params == null ? PixelFormat.TRANSLUCENT : params.format;
Surface ret = null;
if (mBlastBufferQueue == null) {
- mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, width, height,
- format);
+ mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
+ mSurfaceSize.x, mSurfaceSize.y,
+ mWindowAttributes.format);
// We only return the Surface the first time, as otherwise
// it hasn't changed and there is no need to update.
ret = mBlastBufferQueue.createSurface();
} else {
- mBlastBufferQueue.update(mSurfaceControl, width, height, format);
+ mBlastBufferQueue.update(mSurfaceControl,
+ mSurfaceSize.x, mSurfaceSize.y,
+ mWindowAttributes.format);
}
return ret;
@@ -7784,8 +7785,7 @@
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
- final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x, mSurfaceSize.y,
- params);
+ final Surface blastSurface = getOrCreateBLASTSurface();
// If blastSurface == null that means it hasn't changed since the last time we
// called. In this situation, avoid calling transferFrom as we would then
// inc the generation ID and cause EGL resources to be recreated.
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 472e3e7..c110ab9 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -365,6 +365,10 @@
mDuration = PULL_TIME;
mPullDistance += deltaDistance;
+ if (edgeEffectBehavior == TYPE_STRETCH) {
+ // Don't allow stretch beyond 1
+ mPullDistance = Math.min(1f, mPullDistance);
+ }
mDistance = Math.max(0f, mPullDistance);
mVelocity = 0;
@@ -783,6 +787,10 @@
+ mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
mDistance = (float) distance / mHeight;
mVelocity = (float) velocity;
+ if (mDistance > 1f) {
+ mDistance = 1f;
+ mVelocity = 0f;
+ }
if (isAtEquilibrium()) {
mDistance = 0;
mVelocity = 0;
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 667dd9c..93e25a6 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2287,10 +2287,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Nú geturðu stækkað hluta skjásins"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Kveikja á í stillingum"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Hunsa"</string>
- <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Opna á hljóðnema tækisins"</string>
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Opna fyrir hljóðnema tækisins"</string>
<string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Opna fyrir myndavél tækisins"</string>
<string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Fyrir <b><xliff:g id="APP">%s</xliff:g></b> og öll forrit og þjónustur"</string>
- <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Opna á"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Opna fyrir"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Persónuvernd skynjara"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Forritstákn"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Mynd af merki forrits"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5139522..68a5ce7 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -142,7 +142,7 @@
<string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WLAN"</string>
<string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"WLAN 通话"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
- <string name="wifi_calling_off_summary" msgid="5626710010766902560">"关闭"</string>
+ <string name="wifi_calling_off_summary" msgid="5626710010766902560">"已关闭"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"通过 WLAN 进行通话"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"通过移动网络进行通话"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"仅限 WLAN"</string>
@@ -1696,8 +1696,8 @@
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同时按住两个音量键几秒钟,即可开启<xliff:g id="SERVICE">%1$s</xliff:g>无障碍功能。这样做可能会改变您设备的工作方式。\n\n您可以在“设置”>“无障碍”中将此快捷方式更改为开启另一项功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"开启"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不开启"</string>
- <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"开启"</string>
- <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"关闭"</string>
+ <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"已开启"</string>
+ <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"已关闭"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"要允许<xliff:g id="SERVICE">%1$s</xliff:g>完全控制您的设备吗?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"如果您开启<xliff:g id="SERVICE">%1$s</xliff:g>,您的设备将无法使用屏幕锁定功能来增强数据加密效果。"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"对于能满足您的无障碍功能需求的应用,可授予其完全控制权限;但对大部分应用来说,都不适合授予此权限。"</string>
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index d8735ce..a743d30 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -183,8 +183,10 @@
SkPaint paint;
paint.setAlpha(255);
paint.setBlendMode(SkBlendMode::kSrc);
- canvas->drawImageRect(image, imageSrcRect, imageDstRect, sampling, &paint,
- SkCanvas::kFast_SrcRectConstraint);
+ const bool hasBufferCrop = cropRect.left < cropRect.right && cropRect.top < cropRect.bottom;
+ auto constraint =
+ hasBufferCrop ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint;
+ canvas->drawImageRect(image, imageSrcRect, imageDstRect, sampling, &paint, constraint);
canvas->restore();
if (!tmpSurface->readPixels(*bitmap, 0, 0)) {
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 2cf872c..1dcd337 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -20,7 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour gérer votre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à gérer votre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 761b1f4..1b15d20 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -36,7 +36,6 @@
static_libs: [
"PluginCoreLib",
- "WindowManager-Shell",
],
manifest: "AndroidManifest.xml",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8505a62..a50efd7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -7,6 +7,7 @@
import android.app.ActivityTaskManager
import android.app.AppGlobals
import android.app.PendingIntent
+import android.app.TaskInfo
import android.content.Context
import android.graphics.Matrix
import android.graphics.PorterDuff
@@ -30,8 +31,6 @@
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
-import com.android.wm.shell.startingsurface.SplashscreenContentDrawer
-import com.android.wm.shell.startingsurface.StartingSurface
import kotlin.math.roundToInt
private const val TAG = "ActivityLaunchAnimator"
@@ -41,8 +40,7 @@
* nicely into the starting window.
*/
class ActivityLaunchAnimator(
- private val keyguardHandler: KeyguardHandler,
- private val startingSurface: StartingSurface?,
+ private val callback: Callback,
context: Context
) {
companion object {
@@ -120,7 +118,7 @@
Log.d(TAG, "Starting intent with a launch animation")
val runner = Runner(controller)
- val isOnKeyguard = keyguardHandler.isOnKeyguard()
+ val isOnKeyguard = callback.isOnKeyguard()
// Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard.
val animationAdapter = if (!isOnKeyguard) {
@@ -163,7 +161,7 @@
// Hide the keyguard using the launch animation instead of the default unlock animation.
if (isOnKeyguard) {
- keyguardHandler.hideKeyguardWithAnimation(runner)
+ callback.hideKeyguardWithAnimation(runner)
}
}
}
@@ -212,7 +210,7 @@
fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int
}
- interface KeyguardHandler {
+ interface Callback {
/** Whether we are currently on the keyguard or not. */
fun isOnKeyguard(): Boolean
@@ -221,6 +219,9 @@
/** Enable/disable window blur so they don't overlap with the window launch animation **/
fun setBlursDisabledForAppLaunch(disabled: Boolean)
+
+ /* Get the background color of [task]. */
+ fun getBackgroundColor(task: TaskInfo): Int
}
/**
@@ -484,12 +485,7 @@
// which is usually the same color of the app background. We first fade in this layer
// to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
// launch container and reveal the opening window.
- val windowBackgroundColor = if (startingSurface != null) {
- startingSurface.getBackgroundColor(window.taskInfo)
- } else {
- Log.w(TAG, "No starting surface, defaulting to SystemBGColor")
- SplashscreenContentDrawer.getSystemBGColor()
- }
+ val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
val windowBackgroundLayer = GradientDrawable().apply {
setColor(windowBackgroundColor)
alpha = 0
@@ -505,7 +501,7 @@
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
Log.d(TAG, "Animation started")
- keyguardHandler.setBlursDisabledForAppLaunch(true)
+ callback.setBlursDisabledForAppLaunch(true)
controller.onLaunchAnimationStart(isExpandingFullyAbove)
// Add the drawable to the launch container overlay. Overlays always draw
@@ -516,7 +512,7 @@
override fun onAnimationEnd(animation: Animator?) {
Log.d(TAG, "Animation ended")
- keyguardHandler.setBlursDisabledForAppLaunch(false)
+ callback.setBlursDisabledForAppLaunch(false)
iCallback?.invoke()
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
launchContainerOverlay.remove(windowBackgroundLayer)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
index 95c2d2e..6d1408d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
@@ -72,7 +72,8 @@
}
/**
- * @return if detail panel should animate when shown or closed
+ * Indicates whether the detail view wants to animate when shown. This has no affect over the
+ * closing animation. Detail panels will always animate when closed.
*/
default boolean shouldAnimate() {
return true;
diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml
index 2b0ab6f..558ec08 100644
--- a/packages/SystemUI/res/drawable/fingerprint_bg.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml
@@ -14,10 +14,11 @@
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="oval">
<solid
- android:color="?android:attr/colorBackground"/>
+ android:color="?androidprv:attr/colorSurface"/>
<size
android:width="64dp"
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 99e8116..3c9e44e 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -52,6 +52,7 @@
android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
android:layout_gravity="center"
+ android:contentDescription="@null"
android:scaleType="fitXY" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 1a91202..b0f1f48 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,9 +55,23 @@
android:id="@+id/lock_icon_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="48px"
- android:layout_gravity="center"
- android:scaleType="centerCrop"/>
+ android:layout_gravity="center">
+ <!-- Background protection -->
+ <ImageView
+ android:id="@+id/lock_icon_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/fingerprint_bg"
+ android:visibility="invisible"/>
+
+ <ImageView
+ android:id="@+id/lock_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="48px"
+ android:layout_gravity="center"
+ android:scaleType="centerCrop"/>
+ </com.android.keyguard.LockIconView>
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2e6f01b..cd3966d 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -275,7 +275,7 @@
<string name="accessibility_quick_settings_location_off" msgid="6122523378294740598">"स्थान अहवाल बंद."</string>
<string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"स्थान अहवाल सुरू."</string>
<string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"स्थान अहवाल बंद केला."</string>
- <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"स्थान अहवाल सुरू केला."</string>
+ <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"स्थान अहवाल देणे सुरू केले."</string>
<string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> साठी अलार्म सेट केला."</string>
<string name="accessibility_quick_settings_close" msgid="2974895537860082341">"पॅनल बंद करा."</string>
<string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"अधिक वेळ."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 409eced..2bc2923 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1149,8 +1149,7 @@
<string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> మెసేజ్ను పంపారు: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ఇమేజ్ను పంపారు"</string>
<string name="new_status_content_description" msgid="6046637888641308327">"<xliff:g id="NAME">%1$s</xliff:g>, స్టేటస్ను గురించిన అప్డేట్ను కలిగి ఉన్నారు: <xliff:g id="STATUS">%2$s</xliff:g>"</string>
- <!-- no translation found for person_available (2318599327472755472) -->
- <skip />
+ <string name="person_available" msgid="2318599327472755472">"అందుబాటులో ఉంది"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"మీ బ్యాటరీ మీటర్ను చదవడంలో సమస్య"</string>
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"మరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"అలారం సెట్ చేయలేదు"</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
deleted file mode 100644
index 7b9ebc0..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.app.ActivityManager;
-import android.app.PictureInPictureParams;
-import android.app.TaskInfo;
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
-
-public class TaskInfoCompat {
-
- public static int getUserId(TaskInfo info) {
- return info.userId;
- }
-
- public static int getActivityType(TaskInfo info) {
- return info.configuration.windowConfiguration.getActivityType();
- }
-
- public static int getWindowingMode(TaskInfo info) {
- return info.configuration.windowConfiguration.getWindowingMode();
- }
-
- public static Rect getWindowConfigurationBounds(TaskInfo info) {
- return info.configuration.windowConfiguration.getBounds();
- }
-
- public static boolean supportsSplitScreenMultiWindow(TaskInfo info) {
- return info.supportsSplitScreenMultiWindow;
- }
-
- public static ComponentName getTopActivity(TaskInfo info) {
- return info.topActivity;
- }
-
- public static ActivityManager.TaskDescription getTaskDescription(TaskInfo info) {
- return info.taskDescription;
- }
-
- public static ActivityInfo getTopActivityInfo(TaskInfo info) {
- return info.topActivityInfo;
- }
-
- public static boolean isAutoEnterPipEnabled(PictureInPictureParams params) {
- return params.isAutoEnterEnabled();
- }
-
- public static Rect getPipSourceRectHint(PictureInPictureParams params) {
- return params.getSourceRectHint();
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bd000b2..e6e2ac9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -285,7 +285,7 @@
@VisibleForTesting
protected boolean mTelephonyCapable;
- private final boolean mAcquiredHapticEnabled;
+ private final boolean mAcquiredHapticEnabled = false;
@Nullable private final Vibrator mVibrator;
// Device provisioning state
@@ -1413,11 +1413,7 @@
@VisibleForTesting
public void playAcquiredHaptic() {
if (mAcquiredHapticEnabled && mVibrator != null) {
- String effect = Settings.Global.getString(
- mContext.getContentResolver(),
- "udfps_acquired_type");
- mVibrator.vibrate(UdfpsController.getVibration(effect,
- UdfpsController.EFFECT_TICK),
+ mVibrator.vibrate(UdfpsController.EFFECT_CLICK,
UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
}
}
@@ -1730,8 +1726,6 @@
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
dumpManager.registerDumpable(getClass().getName(), this);
- mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- "udfps_acquired", 0) == 1;
mVibrator = vibrator;
mHandler = new Handler(mainLooper) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index c1d448d..867e3ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -16,16 +16,29 @@
package com.android.keyguard;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.NonNull;
+import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -33,16 +46,96 @@
/**
* A view positioned under the notification shade.
*/
-public class LockIconView extends ImageView implements Dumpable {
+public class LockIconView extends FrameLayout implements Dumpable {
@NonNull private final RectF mSensorRect;
@NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
private int mRadius;
+ private ImageView mLockIcon;
+ private ImageView mUnlockBgView;
+
+ private AnimatorSet mBgAnimator;
+ private int mLockIconColor;
+ private int mUnlockStartColor;
+ private int mUnlockEndColor;
+
public LockIconView(Context context, AttributeSet attrs) {
super(context, attrs);
mSensorRect = new RectF();
}
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mLockIcon = findViewById(R.id.lock_icon);
+ mUnlockBgView = findViewById(R.id.lock_icon_bg);
+ }
+
+ void updateColor() {
+ mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+ R.attr.wallpaperTextColorAccent);
+ mUnlockStartColor = mLockIconColor;
+ mUnlockEndColor = Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.textColorPrimary);
+ mUnlockBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+ }
+
+ void setImageDrawable(Drawable drawable) {
+ mLockIcon.setImageDrawable(drawable);
+ }
+
+ void hideBg() {
+ mUnlockBgView.setVisibility(View.INVISIBLE);
+ mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
+ }
+
+ void animateBg() {
+ ValueAnimator bgAlphaAnimator = ObjectAnimator.ofFloat(mUnlockBgView, View.ALPHA, 0f, 1f);
+ bgAlphaAnimator.setDuration(133);
+
+ Interpolator interpolator = new PathInterpolator(0f, 0f, 0f, 1f);
+ Animator scaleXAnimator = ObjectAnimator.ofFloat(mUnlockBgView, View.SCALE_X, .9f, 1f);
+ scaleXAnimator.setInterpolator(interpolator);
+ scaleXAnimator.setDuration(300);
+ Animator scaleYAnimator = ObjectAnimator.ofFloat(mUnlockBgView, View.SCALE_Y, .9f, 1f);
+ scaleYAnimator.setDuration(300);
+ scaleYAnimator.setInterpolator(interpolator);
+
+ ValueAnimator lockIconColorAnimator =
+ ValueAnimator.ofObject(new ArgbEvaluator(), mUnlockStartColor, mUnlockEndColor);
+ lockIconColorAnimator.addUpdateListener(
+ animation -> mLockIcon.setImageTintList(
+ ColorStateList.valueOf((int) animation.getAnimatedValue())));
+ lockIconColorAnimator.setDuration(150);
+
+ if (mBgAnimator != null) {
+ if (mBgAnimator.isRunning()) {
+ return;
+ }
+ mBgAnimator.cancel();
+ }
+ mBgAnimator = new AnimatorSet();
+ mBgAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mBgAnimator = null;
+ }
+ });
+ mBgAnimator.playTogether(
+ bgAlphaAnimator,
+ scaleYAnimator,
+ scaleXAnimator,
+ lockIconColorAnimator);
+ mBgAnimator.setStartDelay(167);
+ mUnlockBgView.setAlpha(0f);
+ mUnlockBgView.setScaleX(0);
+ mUnlockBgView.setScaleY(0);
+ mUnlockBgView.setVisibility(View.VISIBLE);
+
+ mBgAnimator.start();
+ }
+
void setCenterLocation(@NonNull PointF center, int radius) {
mLockIconCenter = center;
mRadius = radius;
@@ -70,7 +163,6 @@
return mLockIconCenter.y - mRadius;
}
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index afea272..a7bd4c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -40,7 +40,6 @@
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
@@ -145,6 +144,7 @@
mUnlockedLabel = context.getResources().getString(R.string.accessibility_unlock_button);
mLockedLabel = context.getResources().getString(R.string.accessibility_lock_icon);
dumpManager.registerDumpable("LockIconViewController", this);
+
}
@Override
@@ -224,6 +224,7 @@
mView.setImageDrawable(mLockIcon);
mView.setVisibility(View.VISIBLE);
mView.setContentDescription(mLockedLabel);
+ mView.hideBg();
} else if (mShowUnlockIcon) {
if (wasShowingFpIcon) {
mView.setImageDrawable(mFpToUnlockIcon);
@@ -234,9 +235,11 @@
mLockToUnlockIcon.forceAnimationOnUI();
mLockToUnlockIcon.start();
}
+ mView.animateBg();
mView.setVisibility(View.VISIBLE);
mView.setContentDescription(mUnlockedLabel);
} else {
+ mView.hideBg();
mView.setVisibility(View.INVISIBLE);
mView.setContentDescription(null);
}
@@ -281,11 +284,7 @@
}
private void updateColors() {
- final int color = Utils.getColorAttrDefaultColor(mView.getContext(),
- R.attr.wallpaperTextColorAccent);
- mFpToUnlockIcon.setTint(color);
- mLockToUnlockIcon.setTint(color);
- mLockIcon.setTint(color);
+ mView.updateColor();
}
private void updateConfiguration() {
@@ -445,6 +444,7 @@
@Override
public void onConfigChanged(Configuration newConfig) {
updateConfiguration();
+ updateColors();
}
};
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 099e6f4..57407f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -88,13 +88,10 @@
* Reload colors from resources.
**/
public void reloadColors() {
- if (mAnimator != null) {
- mAnimator.reloadColors(getContext());
- } else {
- // Needed for old style pin
- int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)
- .getDefaultColor();
- ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
- }
+ if (mAnimator != null) mAnimator.reloadColors(getContext());
+
+ int textColor = Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.colorBackground);
+ ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
index 45ee4ad..ee602bc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
@@ -23,6 +23,8 @@
import android.util.AttributeSet;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
public class AuthBiometricFingerprintView extends AuthBiometricView {
@@ -94,12 +96,37 @@
mIconView.setImageDrawable(icon);
+ final CharSequence iconContentDescription = getIconContentDescription(newState);
+ if (iconContentDescription != null) {
+ mIconView.setContentDescription(iconContentDescription);
+ }
+
if (animation != null && shouldAnimateForTransition(lastState, newState)) {
animation.forceAnimationOnUI();
animation.start();
}
}
+ @Nullable
+ private CharSequence getIconContentDescription(int newState) {
+ switch (newState) {
+ case STATE_IDLE:
+ case STATE_AUTHENTICATING_ANIMATING_IN:
+ case STATE_AUTHENTICATING:
+ case STATE_PENDING_CONFIRMATION:
+ case STATE_AUTHENTICATED:
+ return mContext.getString(
+ R.string.accessibility_fingerprint_dialog_fingerprint_icon);
+
+ case STATE_ERROR:
+ case STATE_HELP:
+ return mContext.getString(R.string.biometric_dialog_try_again);
+
+ default:
+ return null;
+ }
+ }
+
private boolean shouldAnimateForTransition(int oldState, int newState) {
switch (newState) {
case STATE_HELP:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index bebf813..60b0637 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,7 +636,6 @@
mIndicatorView.setText(message);
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
- mIndicatorView.setSelected(true);
mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e51baed..ab3e042e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -17,7 +17,6 @@
package com.android.systemui.biometrics;
import static android.hardware.fingerprint.IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD;
-import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -27,7 +26,6 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -47,8 +45,6 @@
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -160,16 +156,8 @@
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
- public static final VibrationEffect EFFECT_TICK =
- VibrationEffect.get(VibrationEffect.EFFECT_TICK);
- private static final VibrationEffect EFFECT_TEXTURE_TICK =
- VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
- @VisibleForTesting
- static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- private static final VibrationEffect EFFECT_HEAVY =
- VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
- private static final VibrationEffect EFFECT_DOUBLE_CLICK =
- VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ public static final VibrationEffect EFFECT_CLICK =
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
@@ -437,7 +425,6 @@
mTouchLogTime = SystemClock.elapsedRealtime();
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
- playStartHaptic();
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
@@ -552,18 +539,7 @@
@VisibleForTesting
public void playStartHaptic() {
if (mVibrator != null) {
- final ContentResolver contentResolver =
- mContext.getContentResolver();
- // TODO: these settings checks should eventually be removed after ux testing
- // (b/185124905)
- int startEnabled = Settings.Global.getInt(contentResolver,
- "udfps_start", 1);
- if (startEnabled > 0) {
- String startEffectSetting = Settings.Global.getString(
- contentResolver, "udfps_start_type");
- mVibrator.vibrate(getVibration(startEffectSetting,
- EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES);
- }
+ mVibrator.vibrate(EFFECT_CLICK, VIBRATION_SONIFICATION_ATTRIBUTES);
}
}
@@ -698,7 +674,8 @@
// This view overlaps the sensor area, so prevent it from being selectable
// during a11y.
if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
- || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+ || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING
+ || reason == IUdfpsOverlayController.REASON_AUTH_BP) {
mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
}
@@ -853,6 +830,9 @@
Log.w(TAG, "Null view in onFingerDown");
return;
}
+ if (!mOnFingerDown) {
+ playStartHaptic();
+ }
mOnFingerDown = true;
mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
@@ -880,38 +860,6 @@
}
}
- /**
- * get vibration to play given string
- * used for testing purposes (b/185124905)
- */
- public static VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
- if (TextUtils.isEmpty(effect)) {
- return defaultEffect;
- }
-
- switch (effect.toLowerCase()) {
- case "click":
- return EFFECT_CLICK;
- case "heavy":
- return EFFECT_HEAVY;
- case "texture_tick":
- return EFFECT_TEXTURE_TICK;
- case "tick":
- return EFFECT_TICK;
- case "double_tap":
- return EFFECT_DOUBLE_CLICK;
- default:
- try {
- int primitive = Integer.parseInt(effect);
- if (primitive <= PRIMITIVE_LOW_TICK && primitive > -1) {
- return VibrationEffect.startComposition().addPrimitive(primitive).compose();
- }
- } catch (NumberFormatException e) {
- }
- return defaultEffect;
- }
- }
-
private void updateTouchListener() {
if (mView == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 1316184..eb02aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -162,7 +162,12 @@
}
void updateColor() {
+ mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
+ R.attr.wallpaperTextColorAccent);
+ mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorPrimary);
mLockScreenFp.invalidate();
+ mBgProtection.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
}
private boolean showingUdfpsBouncer() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c33f4fa..c7c2590 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2311,7 +2311,6 @@
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
- Log.i("TEST", "playSounds: false");
playSounds(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 1d6c7c9..929927e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -212,6 +212,11 @@
Dependency.get(CommandQueue.class).animateCollapsePanels();
mTriggeredExpand = false;
}
+ // Always animate on close, even if the last opened detail adapter had shouldAnimate()
+ // return false. This is necessary to avoid a race condition which could leave the
+ // keyguard in a bad state where QS remains visible underneath the notifications, clock,
+ // and status area.
+ mShouldAnimate = true;
}
boolean visibleDiff = wasShowingDetail != showingDetail;
@@ -245,10 +250,15 @@
mClosingDetail = true;
mDetailAdapter = null;
listener = mTeardownDetailWhenDone;
- mHeader.setVisibility(View.VISIBLE);
- mFooter.setVisibility(View.VISIBLE);
- mQsPanelController.setGridContentVisibility(true);
- mQsPanelCallback.onScanStateChanged(false);
+ // Only update visibility if already expanded. Otherwise, a race condition can cause the
+ // keyguard to enter a bad state where the QS tiles are displayed underneath the
+ // notifications, clock, and status area.
+ if (mQsPanelController.isExpanded()) {
+ mHeader.setVisibility(View.VISIBLE);
+ mFooter.setVisibility(View.VISIBLE);
+ mQsPanelController.setGridContentVisibility(true);
+ mQsPanelCallback.onScanStateChanged(false);
+ }
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
animateDetailVisibleDiff(x, y, visibleDiff, listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e65038b..f460a13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -148,8 +148,22 @@
AmbientState ambientState) {
NotificationShelf shelf = ambientState.getShelf();
- if (shelf != null) {
- shelf.updateState(algorithmState, ambientState);
+ if (shelf == null) {
+ return;
+ }
+
+ shelf.updateState(algorithmState, ambientState);
+
+ // After the shelf has updated its yTranslation,
+ // explicitly hide views below the shelf to skip rendering them in the hardware layer.
+ final float shelfTop = shelf.getViewState().yTranslation;
+
+ for (ExpandableView view : algorithmState.visibleChildren) {
+ final float viewTop = view.getViewState().yTranslation;
+
+ if (viewTop >= shelfTop) {
+ view.getViewState().hidden = true;
+ }
}
}
@@ -411,8 +425,7 @@
} else {
if (view != ambientState.getTrackedHeadsUpRow()) {
if (ambientState.isExpansionChanging()) {
- // Show all views. Views below the shelf will later be clipped (essentially
- // hidden) in NotificationShelf.
+ // We later update shelf state, then hide views below the shelf.
viewState.hidden = false;
viewState.inShelf = algorithmState.firstViewInShelf != null
&& i >= algorithmState.visibleChildren.indexOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 16f8319..91d503b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -74,6 +74,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.Utils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -459,6 +460,10 @@
if (tileIcon != null) {
mWalletButton.setImageDrawable(tileIcon);
}
+ mWalletButton.getDrawable().setTint(
+ Utils.getColorAttr(
+ mContext,
+ com.android.internal.R.attr.textColorPrimary).getDefaultColor());
mWalletButton.setVisibility(VISIBLE);
mWalletButton.setOnClickListener(this::onWalletClick);
mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d6ea4a8..8c0dfc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -87,7 +87,7 @@
private final Runnable mResetRunnable = ()-> {
if (mKeyguardViewController != null) {
mKeyguardViewController.resetSecurityContainer();
- for (KeyguardResetCallback callback : mResetCallbacks) {
+ for (KeyguardResetCallback callback : new ArrayList<>(mResetCallbacks)) {
callback.onKeyguardReset();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 13aa15e..3c1892d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1575,7 +1575,10 @@
public void expandWithQsDetail(DetailAdapter qsDetailAdapter) {
traceQsJank(true /* startTracing */, false /* wasCancelled */);
flingSettings(0 /* velocity */, FLING_EXPAND);
- mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, 0, 0);
+ // When expanding with a panel, there's no meaningful touch point to correspond to. Set the
+ // origin to somewhere above the screen. This is used for animations.
+ int x = mQsFrame.getWidth() / 2;
+ mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight());
if (mAccessibilityManager.isEnabled()) {
mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 022faf78..5e105bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -55,16 +55,18 @@
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.google.android.collect.Lists;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -100,7 +102,7 @@
private ForcePluginOpenListener mForcePluginOpenListener;
private Consumer<Integer> mScrimsVisibilityListener;
private final ArrayList<WeakReference<StatusBarWindowCallback>>
- mCallbacks = Lists.newArrayList();
+ mCallbacks = new ArrayList<>();
private final SysuiColorExtractor mColorExtractor;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -464,13 +466,15 @@
@Override
public void notifyStateChangedCallbacks() {
- for (int i = 0; i < mCallbacks.size(); i++) {
- StatusBarWindowCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onStateChanged(mCurrentState.mKeyguardShowing,
- mCurrentState.mKeyguardOccluded,
- mCurrentState.mBouncerShowing);
- }
+ // Copy callbacks to separate ArrayList to avoid concurrent modification
+ List<StatusBarWindowCallback> activeCallbacks = mCallbacks.stream()
+ .map(Reference::get)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ for (StatusBarWindowCallback cb : activeCallbacks) {
+ cb.onStateChanged(mCurrentState.mKeyguardShowing,
+ mCurrentState.mKeyguardOccluded,
+ mCurrentState.mBouncerShowing);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6b8686b..db7ead7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -55,6 +55,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.app.TaskInfo;
import android.app.UiModeManager;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
@@ -247,6 +248,7 @@
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
import java.io.FileDescriptor;
@@ -269,7 +271,7 @@
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
StatusBarStateController.StateListener,
LifecycleOwner, BatteryController.BatteryStateChangeCallback,
- ActivityLaunchAnimator.KeyguardHandler {
+ ActivityLaunchAnimator.Callback {
public static final boolean MULTIUSER_DEBUG = false;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -1421,9 +1423,7 @@
private void setUpPresenter() {
// Set up the initial notification state.
- mActivityLaunchAnimator = new ActivityLaunchAnimator(this,
- mStartingSurfaceOptional.orElse(null),
- mContext);
+ mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
mStackScrollerController.getNotificationListContainer(),
@@ -2123,6 +2123,16 @@
mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
}
+ @Override
+ public int getBackgroundColor(TaskInfo task) {
+ if (!mStartingSurfaceOptional.isPresent()) {
+ Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
+ return SplashscreenContentDrawer.getSystemBGColor();
+ }
+
+ return mStartingSurfaceOptional.get().getBackgroundColor(task);
+ }
+
public boolean isDeviceInVrMode() {
return mPresenter.isDeviceInVrMode();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 081fe5a..a8097c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -41,6 +41,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashSet;
/**
@@ -157,7 +158,7 @@
NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
}
- for (OnHeadsUpChangedListener listener : mListeners) {
+ for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
if (isPinned) {
listener.onHeadsUpPinned(entry);
} else {
@@ -177,7 +178,7 @@
entry.setHeadsUp(true);
setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
- for (OnHeadsUpChangedListener listener : mListeners) {
+ for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
listener.onHeadsUpStateChanged(entry, true);
}
}
@@ -188,7 +189,7 @@
entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
- for (OnHeadsUpChangedListener listener : mListeners) {
+ for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
listener.onHeadsUpStateChanged(entry, false);
}
}
@@ -206,7 +207,7 @@
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
}
- for (OnHeadsUpChangedListener listener : mListeners) {
+ for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index b18dfd2..bbaf65a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -807,7 +807,7 @@
}
private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) {
- for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) {
+ for (View.OnFocusChangeListener listener : new ArrayList<>(mEditTextFocusChangeListeners)) {
listener.onFocusChange(remoteEditText, focused);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 33cc782..14f112b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -10,6 +10,7 @@
import android.os.Looper
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.util.Log
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
@@ -21,12 +22,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
-import com.android.wm.shell.startingsurface.StartingSurface
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
+import kotlin.concurrent.thread
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -39,24 +40,23 @@
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
-import kotlin.concurrent.thread
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- @Mock lateinit var keyguardHandler: ActivityLaunchAnimator.KeyguardHandler
+ @Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
- @Mock lateinit var startingSurface: StartingSurface
+ @Mock lateinit var failHandler: Log.TerribleFailureHandler
private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setup() {
- activityLaunchAnimator = ActivityLaunchAnimator(keyguardHandler, startingSurface, mContext)
+ activityLaunchAnimator = ActivityLaunchAnimator(callback, mContext)
}
private fun startIntentWithAnimation(
@@ -119,8 +119,8 @@
@Test
fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
- `when`(keyguardHandler.isOnKeyguard()).thenReturn(true)
- val animator = ActivityLaunchAnimator(keyguardHandler, startingSurface, context)
+ `when`(callback.isOnKeyguard()).thenReturn(true)
+ val animator = ActivityLaunchAnimator(callback, context)
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
var animationAdapter: RemoteAnimationAdapter? = null
@@ -132,7 +132,7 @@
waitForIdleSync()
verify(controller).onIntentStarted(willAnimateCaptor.capture())
- verify(keyguardHandler).hideKeyguardWithAnimation(any())
+ verify(callback).hideKeyguardWithAnimation(any())
assertTrue(willAnimateCaptor.value)
assertNull(animationAdapter)
@@ -174,13 +174,15 @@
val runner = activityLaunchAnimator.createRunner(controller)
runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
- verify(keyguardHandler).setBlursDisabledForAppLaunch(eq(true))
+ verify(callback).setBlursDisabledForAppLaunch(eq(true))
verify(controller).onLaunchAnimationStart(anyBoolean())
}
@Test
- fun controllerFromOrphanViewReturnsNull() {
+ fun controllerFromOrphanViewReturnsNullAndIsATerribleFailure() {
+ Log.setWtfHandler(failHandler)
assertNull(ActivityLaunchAnimator.Controller.fromView(View(mContext)))
+ verify(failHandler).onTerribleFailure(any(), any(), anyBoolean())
}
private fun fakeWindow(): RemoteAnimationTarget {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index c2bd024f..84776c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -148,8 +148,10 @@
eq(true) /* in */, any());
clearInvocations(mQsDetail.mClipper);
+ // Detail adapters should always animate on close. shouldAnimate() should only affect the
+ // open transition
mQsDetail.handleShowingDetail(null, 0, 0, false);
- verify(mQsDetail.mClipper).updateCircularClip(eq(false) /* animate */, anyInt(), anyInt(),
+ verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
eq(false) /* in */, any());
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index e1af2c4..d7bc040 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -258,7 +258,12 @@
super.onMotionEvent(event, rawEvent, policyFlags);
return;
}
-
+ try {
+ checkForMalformedEvent(event);
+ } catch (IllegalArgumentException e) {
+ Slog.e(LOG_TAG, "Ignoring malformed event: " + event.toString(), e);
+ return;
+ }
if (DEBUG) {
Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
@@ -1223,6 +1228,32 @@
}
/**
+ * Checks to see whether an event is consistent with itself.
+ *
+ * @throws IllegalArgumentException in the case of a malformed event.
+ */
+ private static void checkForMalformedEvent(MotionEvent event) {
+ if (event.getPointerCount() < 0) {
+ throw new IllegalArgumentException("Invalid pointer count: " + event.getPointerCount());
+ }
+ for (int i = 0; i < event.getPointerCount(); ++i) {
+ try {
+ int pointerId = event.getPointerId(i);
+ float x = event.getX(i);
+ float y = event.getY(i);
+ if (Float.isNaN(x) || Float.isNaN(y) || x < 0.0f || y < 0.0f) {
+ throw new IllegalArgumentException(
+ "Invalid coordinates: (" + x + ", " + y + ")");
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Encountered exception getting details of pointer " + i + " / "
+ + event.getPointerCount(), e);
+ }
+ }
+ }
+
+ /**
* Class for delayed sending of hover enter and move events.
*/
class SendHoverEnterAndMoveDelayed implements Runnable {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4ec5559..2057a7e4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5003,7 +5003,7 @@
(res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0,
res.key.type);
} else {
- throw new IllegalArgumentException();
+ return new PendingIntentInfo(null, -1, false, ActivityManager.INTENT_SENDER_UNKNOWN);
}
}
@@ -12425,6 +12425,15 @@
return sticky;
}
+ // SafetyNet logging for b/177931370. If any process other than system_server tries to
+ // listen to this broadcast action, then log it.
+ if (callingPid != Process.myPid()) {
+ if (filter.hasAction("com.android.server.net.action.SNOOZE_WARNING")
+ || filter.hasAction("com.android.server.net.action.SNOOZE_RAPID")) {
+ EventLog.writeEvent(0x534e4554, "177931370", callingUid, "");
+ }
+ }
+
synchronized (this) {
IApplicationThread thread;
if (callerApp != null && ((thread = callerApp.getThread()) == null
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index ca357b4c..f11fe8a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -188,18 +188,7 @@
mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
}
- protected boolean successHapticsEnabled() {
- return true;
- }
-
- protected boolean errorHapticsEnabled() {
- return true;
- }
-
protected final void vibrateSuccess() {
- if (!successHapticsEnabled()) {
- return;
- }
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
if (vibrator != null) {
vibrator.vibrate(SUCCESS_VIBRATION_EFFECT, VIBRATION_SONIFICATION_ATTRIBUTES);
@@ -207,9 +196,6 @@
}
protected final void vibrateError() {
- if (!errorHapticsEnabled()) {
- return;
- }
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
if (vibrator != null) {
vibrator.vibrate(ERROR_VIBRATION_EFFECT, VIBRATION_SONIFICATION_ATTRIBUTES);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index db927b2..3757404 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.NotificationManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -33,7 +32,6 @@
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.Slog;
import com.android.internal.R;
@@ -59,9 +57,6 @@
@Nullable private final NotificationManager mNotificationManager;
@Nullable private ICancellationSignal mCancellationSignal;
- @NonNull private final ContentResolver mContentResolver;
- private final boolean mCustomHaptics;
-
private final int[] mBiometricPromptIgnoreList;
private final int[] mBiometricPromptIgnoreListVendor;
private final int[] mKeyguardIgnoreList;
@@ -92,10 +87,6 @@
R.array.config_face_acquire_keyguard_ignorelist);
mKeyguardIgnoreListVendor = resources.getIntArray(
R.array.config_face_acquire_vendor_keyguard_ignorelist);
-
- mContentResolver = context.getContentResolver();
- mCustomHaptics = Settings.Global.getInt(mContentResolver,
- "face_custom_success_error", 0) == 1;
}
@NonNull
@@ -261,18 +252,4 @@
Slog.e(TAG, "Remote exception", e);
}
}
-
- @Override
- protected boolean successHapticsEnabled() {
- return mCustomHaptics
- ? Settings.Global.getInt(mContentResolver, "face_success_enabled", 1) == 0
- : super.successHapticsEnabled();
- }
-
- @Override
- protected boolean errorHapticsEnabled() {
- return mCustomHaptics
- ? Settings.Global.getInt(mContentResolver, "face_error_enabled", 1) == 0
- : super.errorHapticsEnabled();
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 6c0adaf..c3de7aa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -28,7 +27,6 @@
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.Slog;
import com.android.internal.R;
@@ -49,8 +47,6 @@
private static final String TAG = "FaceAuthenticationClient";
- @NonNull private final ContentResolver mContentResolver;
- private final boolean mCustomHaptics;
private final UsageStats mUsageStats;
private final int[] mBiometricPromptIgnoreList;
@@ -81,10 +77,6 @@
R.array.config_face_acquire_keyguard_ignorelist);
mKeyguardIgnoreListVendor = resources.getIntArray(
R.array.config_face_acquire_vendor_keyguard_ignorelist);
-
- mContentResolver = context.getContentResolver();
- mCustomHaptics = Settings.Global.getInt(mContentResolver,
- "face_custom_success_error", 0) == 1;
}
@NonNull
@@ -200,18 +192,4 @@
final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
-
- @Override
- protected boolean successHapticsEnabled() {
- return mCustomHaptics
- ? Settings.Global.getInt(mContentResolver, "face_success_enabled", 1) == 0
- : super.successHapticsEnabled();
- }
-
- @Override
- protected boolean errorHapticsEnabled() {
- return mCustomHaptics
- ? Settings.Global.getInt(mContentResolver, "face_error_enabled", 1) == 0
- : super.errorHapticsEnabled();
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 6a05ed4..19134e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskStackListener;
-import android.content.ContentResolver;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
@@ -30,7 +29,6 @@
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.Slog;
import com.android.server.biometrics.Utils;
@@ -57,9 +55,6 @@
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ICancellationSignal mCancellationSignal;
- @NonNull private final ContentResolver mContentResolver;
- private final boolean mCustomHaptics;
-
FingerprintAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
@@ -74,10 +69,6 @@
lockoutCache, allowBackgroundAuthentication);
mLockoutCache = lockoutCache;
mUdfpsOverlayController = udfpsOverlayController;
-
- mContentResolver = context.getContentResolver();
- mCustomHaptics = Settings.Global.getInt(mContentResolver,
- "fp_custom_success_error", 0) == 1;
}
@NonNull
@@ -213,18 +204,4 @@
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
-
- @Override
- protected boolean successHapticsEnabled() {
- return mCustomHaptics
- ? Settings.Global.getInt(mContentResolver, "fp_success_enabled", 1) == 0
- : super.successHapticsEnabled();
- }
-
- @Override
- protected boolean errorHapticsEnabled() {
- return mCustomHaptics
- ? Settings.Global.getInt(mContentResolver, "fp_error_enabled", 1) == 0
- : super.errorHapticsEnabled();
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 04771b9..ee44c10 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7955,12 +7955,9 @@
} catch (PackageManagerException e) {
Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
- final int[] userIds = mUserManager.getUserIds();
- for (final int userId : userIds) {
- mPermissionManager.onPackageInstalled(pkg,
- PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
- userId);
- }
+ mPermissionManager.onPackageInstalled(pkg,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ UserHandle.USER_ALL);
writeSettingsLPrTEMP();
}
} catch (PackageManagerException e) {
@@ -19213,12 +19210,7 @@
}
final int autoRevokePermissionsMode = installArgs.autoRevokePermissionsMode;
permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
- for (int currentUserId : allUsersList) {
- if (ps.getInstalled(currentUserId)) {
- mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(),
- currentUserId);
- }
- }
+ mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(), userId);
}
res.name = pkgName;
res.uid = pkg.getUid();
@@ -21862,10 +21854,8 @@
if (sharedUserPkgs == null) {
sharedUserPkgs = Collections.emptyList();
}
- for (final int userId : allUserHandles) {
- mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId,
- deletedPs.pkg, sharedUserPkgs, userId);
- }
+ mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId,
+ deletedPs.pkg, sharedUserPkgs, UserHandle.USER_ALL);
}
clearPackagePreferredActivitiesLPw(
deletedPs.name, changedUsers, UserHandle.USER_ALL);
@@ -22082,11 +22072,12 @@
}
}
+ // The method below will take care of removing obsolete permissions and granting
+ // install permissions.
+ mPermissionManager.onPackageInstalled(pkg,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ UserHandle.USER_ALL);
for (final int userId : allUserHandles) {
- // The method below will take care of removing obsolete permissions and granting
- // install permissions.
- mPermissionManager.onPackageInstalled(pkg,
- PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, userId);
if (applyUserRestrictions) {
mSettings.writePermissionStateForUserLPr(userId, false);
}
@@ -22409,10 +22400,9 @@
}
removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId);
clearPackagePreferredActivities(ps.name, nextUserId);
- mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs,
- nextUserId);
mDomainVerificationManager.clearPackageForUser(ps.name, nextUserId);
}
+ mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs, userId);
if (outInfo != null) {
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 97ccfe6..dfc14bd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4040,17 +4040,14 @@
*
* @param packageName The package that is updated
* @param pkg The package that is updated, or {@code null} if package is deleted
- * @param filterUserId If not {@link UserHandle.USER_ALL}, only restore the permission state for
- * this particular user
*/
- private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg,
- @UserIdInt int filterUserId) {
+ private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
// If the package is being deleted, update the permissions of all the apps
final int flags =
(pkg == null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG
: UPDATE_PERMISSIONS_REPLACE_PKG);
- updatePermissions(packageName, pkg, getVolumeUuidForPackage(pkg), flags,
- mDefaultPermissionCallback, filterUserId);
+ updatePermissions(
+ packageName, pkg, getVolumeUuidForPackage(pkg), flags, mDefaultPermissionCallback);
}
/**
@@ -4072,8 +4069,7 @@
(fingerprintChanged
? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
: 0);
- updatePermissions(null, null, volumeUuid, flags, mDefaultPermissionCallback,
- UserHandle.USER_ALL);
+ updatePermissions(null, null, volumeUuid, flags, mDefaultPermissionCallback);
} finally {
PackageManager.uncorkPackageInfoCache();
}
@@ -4122,14 +4118,12 @@
* all volumes
* @param flags Control permission for which apps should be updated
* @param callback Callback to call after permission changes
- * @param filterUserId If not {@link UserHandle.USER_ALL}, only restore the permission state for
- * this particular user
*/
private void updatePermissions(final @Nullable String changingPkgName,
final @Nullable AndroidPackage changingPkg,
final @Nullable String replaceVolumeUuid,
@UpdatePermissionFlags int flags,
- final @Nullable PermissionCallback callback, @UserIdInt int filterUserId) {
+ final @Nullable PermissionCallback callback) {
// TODO: Most of the methods exposing BasePermission internals [source package name,
// etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
// have package settings, we should make note of it elsewhere [map between
@@ -4165,7 +4159,8 @@
// Only replace for packages on requested volume
final String volumeUuid = getVolumeUuidForPackage(pkg);
final boolean replace = replaceAll && Objects.equals(replaceVolumeUuid, volumeUuid);
- restorePermissionState(pkg, replace, changingPkgName, callback, filterUserId);
+ restorePermissionState(pkg, replace, changingPkgName, callback,
+ UserHandle.USER_ALL);
});
}
@@ -4174,7 +4169,8 @@
final String volumeUuid = getVolumeUuidForPackage(changingPkg);
final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
&& Objects.equals(replaceVolumeUuid, volumeUuid);
- restorePermissionState(changingPkg, replace, changingPkgName, callback, filterUserId);
+ restorePermissionState(changingPkg, replace, changingPkgName, callback,
+ UserHandle.USER_ALL);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -4841,18 +4837,20 @@
private void onPackageInstalledInternal(@NonNull AndroidPackage pkg,
@NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
- @UserIdInt int userId) {
- updatePermissions(pkg.getPackageName(), pkg, userId);
- addAllowlistedRestrictedPermissionsInternal(pkg,
- params.getAllowlistedRestrictedPermissions(),
- FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
- final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
- if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
- || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
- setAutoRevokeExemptedInternal(pkg,
- autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
+ @UserIdInt int[] userIds) {
+ updatePermissions(pkg.getPackageName(), pkg);
+ for (final int userId : userIds) {
+ addAllowlistedRestrictedPermissionsInternal(pkg,
+ params.getAllowlistedRestrictedPermissions(),
+ FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
+ final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
+ if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
+ || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
+ setAutoRevokeExemptedInternal(pkg,
+ autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
+ }
+ grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId);
}
- grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId);
}
private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg,
@@ -4875,7 +4873,7 @@
private void onPackageUninstalledInternal(@NonNull String packageName, int appId,
@Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
- @UserIdInt int userId) {
+ @UserIdInt int[] userIds) {
// TODO: Move these checks to check PackageState to be more reliable.
// System packages should always have an available APK.
if (pkg != null && pkg.isSystem()
@@ -4886,27 +4884,31 @@
// If we are only marking a system package as uninstalled, we need to keep its
// pregranted permission state so that it still works once it gets reinstalled, thus
// only reset the user modifications to its permission state.
- resetRuntimePermissionsInternal(pkg, userId);
+ for (final int userId : userIds) {
+ resetRuntimePermissionsInternal(pkg, userId);
+ }
return;
}
- updatePermissions(packageName, null, userId);
- if (sharedUserPkgs.isEmpty()) {
- removeUidStateAndResetPackageInstallPermissionsFixed(appId, packageName, userId);
- } else {
- // Remove permissions associated with package. Since runtime
- // permissions are per user we have to kill the removed package
- // or packages running under the shared user of the removed
- // package if revoking the permissions requested only by the removed
- // package is successful and this causes a change in gids.
- final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg,
- sharedUserPkgs, userId);
- final boolean shouldKill = userIdToKill != UserHandle.USER_NULL;
- // If gids changed, kill all affected packages.
- if (shouldKill) {
- mHandler.post(() -> {
- // This has to happen with no lock held.
- killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED);
- });
+ updatePermissions(packageName, null);
+ for (final int userId : userIds) {
+ if (sharedUserPkgs.isEmpty()) {
+ removeUidStateAndResetPackageInstallPermissionsFixed(appId, packageName, userId);
+ } else {
+ // Remove permissions associated with package. Since runtime
+ // permissions are per user we have to kill the removed package
+ // or packages running under the shared user of the removed
+ // package if revoking the permissions requested only by the removed
+ // package is successful and this causes a change in gids.
+ final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg,
+ sharedUserPkgs, userId);
+ final boolean shouldKill = userIdToKill != UserHandle.USER_NULL;
+ // If gids changed, kill all affected packages.
+ if (shouldKill) {
+ mHandler.post(() -> {
+ // This has to happen with no lock held.
+ killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED);
+ });
+ }
}
}
}
@@ -5181,8 +5183,11 @@
@NonNull PackageInstalledParams params, @UserIdInt int userId) {
Objects.requireNonNull(pkg, "pkg");
Objects.requireNonNull(params, "params");
- Preconditions.checkArgumentNonNegative(userId, "userId");
- onPackageInstalledInternal(pkg, params, userId);
+ Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM
+ || userId == UserHandle.USER_ALL, "userId");
+ final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds()
+ : new int[] { userId };
+ onPackageInstalledInternal(pkg, params, userIds);
}
@Override
@@ -5197,8 +5202,11 @@
@UserIdInt int userId) {
Objects.requireNonNull(packageName, "packageName");
Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs");
- Preconditions.checkArgumentNonNegative(userId, "userId");
- onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userId);
+ Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM
+ || userId == UserHandle.USER_ALL, "userId");
+ final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds()
+ : new int[] { userId };
+ onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds);
}
@NonNull
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 9026262..ab71355 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -189,12 +189,16 @@
return false;
}
- // 3. The app has WRITE_MEDIA_STORAGE, OR
- // the app already has legacy external storage or requested it,
- // and is < R.
- return hasWriteMediaStorageGrantedForUid
- || ((hasLegacyExternalStorage || hasRequestedLegacyExternalStorage)
- && targetSDK < Build.VERSION_CODES.R);
+ // 3. The app targetSDK should be less than R
+ if (targetSDK >= Build.VERSION_CODES.R) {
+ return false;
+ }
+
+ // 4. The app has WRITE_MEDIA_STORAGE,
+ // OR the app already has legacy external storage
+ // OR the app requested legacy external storage
+ return hasWriteMediaStorageGrantedForUid || hasLegacyExternalStorage
+ || hasRequestedLegacyExternalStorage;
}
@Override
public boolean mayDenyExtraAppOpIfGranted() {
@@ -216,10 +220,8 @@
return true;
}
- // The package doesn't have WRITE_MEDIA_STORAGE,
- // AND didn't request legacy storage to be preserved
- if (!hasWriteMediaStorageGrantedForUid
- && !hasRequestedPreserveLegacyExternalStorage) {
+ // The package doesn't request legacy storage to be preserved
+ if (!hasRequestedPreserveLegacyExternalStorage) {
return true;
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 0386372..dd4e260 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -1294,21 +1294,32 @@
@Override
public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
if (controller.getVibratorInfo().getId() == vibratorId) {
+ mVibratorCallbackReceived = true;
mNextOffTime = SystemClock.uptimeMillis();
}
- // Timings are tightly controlled here, so never anticipate when vibrator is complete.
- return false;
+ // Timings are tightly controlled here, so only anticipate if the vibrator was supposed
+ // to be ON but has completed prematurely, to turn it back on as soon as possible.
+ return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
}
@Override
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
try {
+ long now = SystemClock.uptimeMillis();
+ long latency = now - startTime;
if (DEBUG) {
- long latency = SystemClock.uptimeMillis() - startTime;
Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
}
+ if (mVibratorCallbackReceived && latency < 0) {
+ // This step was anticipated because the vibrator turned off prematurely.
+ // Turn it back on and return this same step to run at the exact right time.
+ mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
+ return Arrays.asList(new AmplitudeStep(startTime, controller, effect,
+ segmentIndex, mNextOffTime));
+ }
+
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
if (!(segment instanceof StepSegment)) {
Slog.w(TAG, "Ignoring wrong segment for a AmplitudeStep: " + segment);
@@ -1321,17 +1332,16 @@
return skipToNextSteps(/* segmentsSkipped= */ 1);
}
- long now = SystemClock.uptimeMillis();
float amplitude = stepSegment.getAmplitude();
if (amplitude == 0) {
- if (mNextOffTime > now) {
+ if (vibratorOffTimeout > now) {
// Amplitude cannot be set to zero, so stop the vibrator.
stopVibrating();
mNextOffTime = now;
}
} else {
if (startTime >= mNextOffTime) {
- // Vibrator has stopped. Turn vibrator back on for the duration of another
+ // Vibrator is OFF. Turn vibrator back on for the duration of another
// cycle before setting the amplitude.
long onDuration = getVibratorOnDuration(effect, segmentIndex);
if (onDuration > 0) {
@@ -1350,6 +1360,22 @@
}
}
+ private long turnVibratorBackOn(long remainingDuration) {
+ long onDuration = getVibratorOnDuration(effect, segmentIndex);
+ if (onDuration <= 0) {
+ // Vibrator is supposed to go back off when this step starts, so just leave it off.
+ return vibratorOffTimeout;
+ }
+ onDuration += remainingDuration;
+ float expectedAmplitude = controller.getCurrentAmplitude();
+ mVibratorOnResult = startVibrating(onDuration);
+ if (mVibratorOnResult > 0) {
+ // Set the amplitude back to the value it was supposed to be playing at.
+ changeAmplitude(expectedAmplitude);
+ }
+ return SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
+ }
+
private long startVibrating(long duration) {
if (DEBUG) {
Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
@@ -1383,7 +1409,10 @@
repeatIndex = -1;
}
if (i == startIndex) {
- return 1000;
+ // The repeating waveform keeps the vibrator ON all the time. Use a minimum
+ // of 1s duration to prevent short patterns from turning the vibrator ON too
+ // frequently.
+ return Math.max(timing, 1000);
}
}
if (i == segmentCount && effect.getRepeatIndex() < 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f3dbed5..4468252 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -801,6 +801,10 @@
// Tracking cookie for the launch of this activity and it's task.
IBinder mLaunchCookie;
+ // Entering PiP is usually done in two phases, we put the task into pinned mode first and
+ // SystemUi sets the pinned mode on activity after transition is done.
+ boolean mWaitForEnteringPinnedMode;
+
private final Runnable mPauseTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -7705,6 +7709,7 @@
// mode (see RootWindowContainer#moveActivityToPinnedRootTask). So once the windowing mode
// of activity is changed, it is the signal of the last step to update the PiP states.
if (!wasInPictureInPicture && inPinnedWindowingMode() && task != null) {
+ mWaitForEnteringPinnedMode = false;
mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, task.getBounds());
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 97c19ab..73d31bf 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1568,6 +1568,10 @@
layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
+ if (win.mActivityRecord != null && win.mActivityRecord.mWaitForEnteringPinnedMode) {
+ // Skip layout of the window when in transition to pip mode.
+ return;
+ }
final WindowManager.LayoutParams attrs = win.getAttrs();
final int type = attrs.type;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9a6a518..3214721 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2191,6 +2191,7 @@
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
r.setWindowingMode(intermediateWindowingMode);
+ r.mWaitForEnteringPinnedMode = true;
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
rootTask.setDeferTaskAppear(false);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 777306a..936b2ef 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -61,6 +61,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -4105,6 +4107,7 @@
info.positionInParent = getRelativePosition();
info.pictureInPictureParams = getPictureInPictureParams(top);
+ info.displayCutoutInsets = getDisplayCutoutInsets(top);
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
@@ -4139,6 +4142,18 @@
? null : new PictureInPictureParams(topVisibleActivity.pictureInPictureArgs);
}
+ private Rect getDisplayCutoutInsets(Task top) {
+ if (top == null || top.mDisplayContent == null
+ || top.getDisplayInfo().displayCutout == null) return null;
+ final WindowState w = top.getTopVisibleAppMainWindow();
+ final int displayCutoutMode = w == null
+ ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ : w.getAttrs().layoutInDisplayCutoutMode;
+ return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
+ ? null : top.getDisplayInfo().displayCutout.getSafeInsets();
+ }
+
/**
* Returns a {@link TaskInfo} with information from this task.
*/
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e9babce..193d92a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -15019,10 +15019,11 @@
}
if (active) {
if (shouldSendNotification) {
- sendNetworkLoggingNotification();
+ mHandler.post(() -> sendNetworkLoggingNotification());
}
} else {
- mInjector.getNotificationManager().cancel(SystemMessage.NOTE_NETWORK_LOGGING);
+ mHandler.post(() -> mInjector.getNotificationManager().cancel(
+ SystemMessage.NOTE_NETWORK_LOGGING));
}
});
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 583797e..a254f68 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -514,8 +514,16 @@
}
private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
- boolean unrestricted) {
- final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
+ boolean unrestricted, boolean compat) {
+ assertFalse("Alarm cannot be compat and unrestricted", unrestricted && compat);
+ final int flags;
+ if (unrestricted) {
+ flags = FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+ } else if (compat) {
+ flags = FLAG_ALLOW_WHILE_IDLE_COMPAT;
+ } else {
+ flags = FLAG_ALLOW_WHILE_IDLE;
+ }
setTestAlarm(type, triggerTime, pi, 0, flags, TEST_CALLING_UID);
}
@@ -1600,13 +1608,13 @@
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent(), false);
+ getNewMockPendingIntent(), false, false);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
// This one should get deferred on set.
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent(), false);
+ getNewMockPendingIntent(), false, false);
final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
mTestTimer.getElapsed());
@@ -1619,6 +1627,108 @@
}
@Test
+ public void allowWhileIdleCompatAlarmsWhileDeviceIdle() throws Exception {
+ setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);
+
+ final long window = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + window + 1000,
+ getNewMockPendingIntent());
+ assertNotNull(mService.mPendingIdleUntil);
+
+ final int quota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ getNewMockPendingIntent(), false, true);
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ // This one should get deferred on set.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
+ getNewMockPendingIntent(), false, true);
+ final long expectedNextTrigger = firstTrigger + window;
+ assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
+ mTestTimer.getElapsed());
+
+ // Bring the idle until alarm back.
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger - 50,
+ getNewMockPendingIntent());
+ assertEquals(expectedNextTrigger - 50, mService.mPendingIdleUntil.getWhenElapsed());
+ assertEquals(expectedNextTrigger - 50, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void allowWhileIdleCompatHistorySeparate() throws Exception {
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+ when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
+
+ final int fullQuota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int compatQuota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
+
+ final long fullWindow = mAllowWhileIdleWindow;
+ final long compatWindow = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
+
+ final long firstFullTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < fullQuota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstFullTrigger + i,
+ getNewMockPendingIntent(), false, false);
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ // This one should get deferred on set, as full quota is not available.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstFullTrigger + fullQuota,
+ getNewMockPendingIntent(), false, false);
+ final long expectedNextFullTrigger = firstFullTrigger + fullWindow;
+ assertEquals("Incorrect trigger when no quota left", expectedNextFullTrigger,
+ mTestTimer.getElapsed());
+ mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
+
+ // The following should be allowed, as compat quota should be free.
+ for (int i = 0; i < compatQuota; i++) {
+ final long trigger = mNowElapsedTest + 1;
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, getNewMockPendingIntent(),
+ false, true);
+ assertEquals(trigger, mTestTimer.getElapsed());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+
+ // Refresh the state
+ mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
+ mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
+ mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
+ TEST_CALLING_USER);
+
+ // Now test with flipped order
+
+ final long firstCompatTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < compatQuota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstCompatTrigger + i,
+ getNewMockPendingIntent(), false, true);
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ // This one should get deferred on set, as full quota is not available.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstCompatTrigger + compatQuota,
+ getNewMockPendingIntent(), false, true);
+ final long expectedNextCompatTrigger = firstCompatTrigger + compatWindow;
+ assertEquals("Incorrect trigger when no quota left", expectedNextCompatTrigger,
+ mTestTimer.getElapsed());
+ mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
+
+ // The following should be allowed, as full quota should be free.
+ for (int i = 0; i < fullQuota; i++) {
+ final long trigger = mNowElapsedTest + 1;
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, getNewMockPendingIntent(),
+ false, false);
+ assertEquals(trigger, mTestTimer.getElapsed());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ }
+
+ @Test
public void allowWhileIdleUnrestricted() throws Exception {
setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);
@@ -1634,7 +1744,7 @@
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < numAlarms; i++) {
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent(), true);
+ getNewMockPendingIntent(), true, false);
}
// All of them should fire as expected.
for (int i = 0; i < numAlarms; i++) {
@@ -1736,7 +1846,7 @@
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
- getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
+ getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
// Refresh the state
mService.removeLocked(TEST_CALLING_UID,
@@ -1744,7 +1854,7 @@
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
- trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
+ trigger, getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
// Refresh the state
mService.removeLocked(TEST_CALLING_UID,
@@ -1752,7 +1862,36 @@
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
- getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
+ getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
+ }
+
+ @Test
+ public void allowWhileIdleCompatAlarmsInBatterySaver() throws Exception {
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+ when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
+
+ final int quota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
+ final long window = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
+
+ testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent(), false, true), quota, window);
+
+ // Refresh the state
+ mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
+ mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
+ TEST_CALLING_USER);
+
+ testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
+ trigger, getNewMockPendingIntent(), false, true), quota, window);
+
+ // Refresh the state
+ mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
+ mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
+ TEST_CALLING_USER);
+
+ testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent(), false, true), quota, window);
}
@Test
@@ -2123,7 +2262,7 @@
final PendingIntent alarmPi = getNewMockPendingIntent();
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
- alarmPi, null, null, null, alarmClock);
+ alarmPi, null, null, null, alarmClock);
// Correct permission checks are invoked.
verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 91f4922..5b067bc 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -869,6 +869,446 @@
}
@Test
+ public void testGetNextPageToken_query() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+ searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
+ }
+
+ @Test
+ public void testGetNextPageWithDifferentPackage_query() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+
+ // Try getting next page with the wrong package, package2
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.getNextPage("package2", nextPageToken));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+
+ // Can continue getting next page for package1
+ searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
+ }
+
+ @Test
+ public void testGetNextPageToken_globalQuery() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ searchSpec,
+ "package1",
+ /*visibilityStore=*/ null,
+ Process.myUid(),
+ /*callerHasSystemAccess=*/ false,
+ /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+ searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
+ }
+
+ @Test
+ public void testGetNextPageWithDifferentPackage_globalQuery() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ searchSpec,
+ "package1",
+ /*visibilityStore=*/ null,
+ Process.myUid(),
+ /*callerHasSystemAccess=*/ false,
+ /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+
+ // Try getting next page with the wrong package, package2
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.getNextPage("package2", nextPageToken));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+
+ // Can continue getting next page for package1
+ searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
+ }
+
+ @Test
+ public void testInvalidateNextPageToken_query() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+
+ // Invalidate the token
+ mAppSearchImpl.invalidateNextPageToken("package1", nextPageToken);
+
+ // Can't get next page because we invalidated the token.
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.getNextPage("package1", nextPageToken));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+ }
+
+ @Test
+ public void testInvalidateNextPageTokenWithDifferentPackage_query() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+
+ // Try getting next page with the wrong package, package2
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.invalidateNextPageToken("package2", nextPageToken));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+
+ // Can continue getting next page for package1
+ searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
+ }
+
+ @Test
+ public void testInvalidateNextPageToken_globalQuery() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ searchSpec,
+ "package1",
+ /*visibilityStore=*/ null,
+ Process.myUid(),
+ /*callerHasSystemAccess=*/ false,
+ /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+
+ // Invalidate the token
+ mAppSearchImpl.invalidateNextPageToken("package1", nextPageToken);
+
+ // Can't get next page because we invalidated the token.
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.getNextPage("package1", nextPageToken));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+ }
+
+ @Test
+ public void testInvalidateNextPageTokenWithDifferentPackage_globalQuery() throws Exception {
+ // Insert package1 schema
+ List<AppSearchSchema> schema1 =
+ ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schema1,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert two package1 documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema1").build();
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
+ mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null);
+
+ // Query for only 1 result per page
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ searchSpec,
+ "package1",
+ /*visibilityStore=*/ null,
+ Process.myUid(),
+ /*callerHasSystemAccess=*/ false,
+ /*logger=*/ null);
+
+ // Document2 will come first because it was inserted last and default return order is
+ // most recent.
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+
+ long nextPageToken = searchResultPage.getNextPageToken();
+
+ // Try getting next page with the wrong package, package2
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.invalidateNextPageToken("package2", nextPageToken));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+
+ // Can continue getting next page for package1
+ searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
+ }
+
+ @Test
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder()
@@ -1777,11 +2217,11 @@
assertThrows(
IllegalStateException.class,
- () -> appSearchImpl.getNextPage(/*nextPageToken=*/ 1L));
+ () -> appSearchImpl.getNextPage("package", /*nextPageToken=*/ 1L));
assertThrows(
IllegalStateException.class,
- () -> appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L));
+ () -> appSearchImpl.invalidateNextPageToken("package", /*nextPageToken=*/ 1L));
assertThrows(
IllegalStateException.class,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 1596483..2e5c24c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -246,6 +246,81 @@
}
@Test
+ public void vibrate_singleVibratorRepeatingShortAlwaysOnWaveform_turnsVibratorOnForASecond()
+ throws Exception {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ long vibrationId = 1;
+ int[] amplitudes = new int[]{1, 2, 3};
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[]{1, 10, 100}, amplitudes, 0);
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+
+ assertTrue(waitUntil(t -> !fakeVibrator.getAmplitudes().isEmpty(), thread,
+ TEST_TIMEOUT_MILLIS));
+ thread.cancel();
+ waitForCompletion(thread);
+
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertEquals(Arrays.asList(expectedOneShot(1000)), fakeVibrator.getEffectSegments());
+ }
+
+ @Test
+ public void vibrate_singleVibratorRepeatingLongAlwaysOnWaveform_turnsVibratorOnForACycle()
+ throws Exception {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ long vibrationId = 1;
+ int[] amplitudes = new int[]{1, 2, 3};
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[]{5000, 500, 50}, amplitudes, 0);
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+
+ assertTrue(waitUntil(t -> !fakeVibrator.getAmplitudes().isEmpty(), thread,
+ TEST_TIMEOUT_MILLIS));
+ thread.cancel();
+ waitForCompletion(thread);
+
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments());
+ }
+
+
+ @Test
+ public void vibrate_singleVibratorRepeatingAlwaysOnWaveform_turnsVibratorBackOn()
+ throws Exception {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ long vibrationId = 1;
+ int[] amplitudes = new int[]{1, 2};
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[]{900, 50}, amplitudes, 0);
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+
+ assertTrue(waitUntil(t -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
+ thread, 1000 + TEST_TIMEOUT_MILLIS));
+ thread.cancel();
+ waitForCompletion(thread);
+
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertEquals(2, fakeVibrator.getEffectSegments().size());
+ // First time turn vibrator ON for minimum of 1s.
+ assertEquals(1000L, fakeVibrator.getEffectSegments().get(0).getDuration());
+ // Vibrator turns off in the middle of the second execution of first step, turn it back ON
+ // for another 1s + remaining of 850ms.
+ assertEquals(1850, fakeVibrator.getEffectSegments().get(1).getDuration(), /* delta= */ 20);
+ // Set amplitudes for a cycle {1, 2}, start second loop then turn it back on to same value.
+ assertEquals(expectedAmplitudes(1, 2, 1, 1),
+ mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().subList(0, 4));
+ }
+
+ @Test
public void vibrate_singleVibratorPredefinedCancel_cancelsVibrationImmediately()
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);