Merge "Implement lenient background check option." into nyc-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index c9b519a..984d925 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -48559,6 +48559,7 @@
method public abstract void onConfigurationChanged(android.content.res.Configuration);
method public abstract android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
method public abstract void onDetachedFromWindow();
+ method public abstract boolean onDragEvent(android.view.DragEvent);
method public abstract void onDraw(android.graphics.Canvas);
method public abstract void onDrawVerticalScrollBar(android.graphics.Canvas, android.graphics.drawable.Drawable, int, int, int, int);
method public abstract void onFinishTemporaryDetach();
diff --git a/core/java/android/text/style/ReplacementSpan.java b/core/java/android/text/style/ReplacementSpan.java
index 26c725f..07190b2 100644
--- a/core/java/android/text/style/ReplacementSpan.java
+++ b/core/java/android/text/style/ReplacementSpan.java
@@ -16,18 +16,49 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Paint;
import android.graphics.Canvas;
import android.text.TextPaint;
public abstract class ReplacementSpan extends MetricAffectingSpan {
- public abstract int getSize(Paint paint, CharSequence text,
- int start, int end,
- Paint.FontMetricsInt fm);
- public abstract void draw(Canvas canvas, CharSequence text,
- int start, int end, float x,
- int top, int y, int bottom, Paint paint);
+ /**
+ * Returns the width of the span. Extending classes can set the height of the span by updating
+ * attributes of {@link android.graphics.Paint.FontMetricsInt}. If the span covers the whole
+ * text, and the height is not set,
+ * {@link #draw(Canvas, CharSequence, int, int, float, int, int, int, Paint)} will not be
+ * called for the span.
+ *
+ * @param paint Paint instance.
+ * @param text Current text.
+ * @param start Start character index for span.
+ * @param end End character index for span.
+ * @param fm Font metrics, can be null.
+ * @return Width of the span.
+ */
+ public abstract int getSize(@NonNull Paint paint, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @Nullable Paint.FontMetricsInt fm);
+
+ /**
+ * Draws the span into the canvas.
+ *
+ * @param canvas Canvas into which the span should be rendered.
+ * @param text Current text.
+ * @param start Start character index for span.
+ * @param end End character index for span.
+ * @param x Edge of the replacement closest to the leading margin.
+ * @param top Top of the line.
+ * @param y Baseline.
+ * @param bottom Bottom of the line.
+ * @param paint Paint instance.
+ */
+ public abstract void draw(@NonNull Canvas canvas, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+ int top, int y, int bottom, @NonNull Paint paint);
/**
* This method does nothing, since ReplacementSpans are measured
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 0f58ba3..647d4dc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -39,6 +39,7 @@
import android.security.KeyChain;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -2528,6 +2529,11 @@
}
@Override
+ public boolean onDragEvent(DragEvent event) {
+ return mProvider.getViewDelegate().onDragEvent(event);
+ }
+
+ @Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
// This method may be called in the constructor chain, before the WebView provider is
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 3ce034c..94d231c 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.Message;
import android.print.PrintDocumentAdapter;
+import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -334,6 +335,8 @@
public InputConnection onCreateInputConnection(EditorInfo outAttrs);
+ public boolean onDragEvent(DragEvent event);
+
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
public boolean onKeyDown(int keyCode, KeyEvent event);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index 34dfd6c..58c3b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -64,6 +64,14 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Since we control our own bottom, be whatever size we want.
+ // Otherwise the QSPanel ends up with 0 height when the window is only the
+ // size of the status bar.
+ super.onMeasure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateBottom();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4be6833..388c8b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2162,8 +2162,13 @@
mIntent = new Intent().setComponent(mComponentName);
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.accessibility_binding_label);
- mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+ final long idendtity = Binder.clearCallingIdentity();
+ try {
+ mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+ } finally {
+ Binder.restoreCallingIdentity(idendtity);
+ }
}
setDynamicallyConfigurableProperties(accessibilityServiceInfo);
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 57cede8..25b54ac 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -78,13 +78,15 @@
* Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
* @hide
*/
-public class JobSchedulerService extends com.android.server.SystemService
+public final class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener {
public static final boolean DEBUG = false;
/** The number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT
= ActivityManager.isLowRamDeviceStatic() ? 3 : 6;
static final String TAG = "JobSchedulerService";
+ /** Global local for all job scheduler state. */
+ final Object mLock = new Object();
/** Master list of jobs. */
final JobStore mJobs;
@@ -207,6 +209,10 @@
}
};
+ public Object getLock() {
+ return mLock;
+ }
+
@Override
public void onStartUser(int userHandle) {
mStartedUsers.add(userHandle);
@@ -231,7 +237,7 @@
}
public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId) {
- JobStatus jobStatus = new JobStatus(job, uId, packageName, userId);
+ JobStatus jobStatus = new JobStatus(getLock(), job, uId, packageName, userId);
try {
if (ActivityManagerNative.getDefault().getAppStartMode(uId,
job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
@@ -243,7 +249,7 @@
}
if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
JobStatus toCancel;
- synchronized (mJobs) {
+ synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
}
startTrackingJob(jobStatus, toCancel);
@@ -256,7 +262,7 @@
public List<JobInfo> getPendingJobs(int uid) {
ArrayList<JobInfo> outList = new ArrayList<JobInfo>();
- synchronized (mJobs) {
+ synchronized (mLock) {
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
@@ -270,7 +276,7 @@
void cancelJobsForUser(int userHandle) {
List<JobStatus> jobsForUser;
- synchronized (mJobs) {
+ synchronized (mLock) {
jobsForUser = mJobs.getJobsByUser(userHandle);
}
for (int i=0; i<jobsForUser.size(); i++) {
@@ -289,7 +295,7 @@
*/
public void cancelJobsForUid(int uid, boolean forceAll) {
List<JobStatus> jobsForUid;
- synchronized (mJobs) {
+ synchronized (mLock) {
jobsForUid = mJobs.getJobsByUid(uid);
}
for (int i=0; i<jobsForUid.size(); i++) {
@@ -317,7 +323,7 @@
*/
public void cancelJob(int uid, int jobId) {
JobStatus toCancel;
- synchronized (mJobs) {
+ synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
}
if (toCancel != null) {
@@ -328,7 +334,7 @@
private void cancelJobImpl(JobStatus cancelled) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
stopTrackingJob(cancelled, true /* writeBack */);
- synchronized (mJobs) {
+ synchronized (mLock) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
// Cancel if running.
@@ -340,7 +346,7 @@
void updateIdleMode(boolean enabled) {
boolean changed = false;
boolean rocking;
- synchronized (mJobs) {
+ synchronized (mLock) {
if (mDeviceIdleMode != enabled) {
changed = true;
}
@@ -352,7 +358,7 @@
mControllers.get(i).deviceIdleModeChanged(enabled);
}
}
- synchronized (mJobs) {
+ synchronized (mLock) {
mDeviceIdleMode = enabled;
if (enabled) {
// When becoming idle, make sure no jobs are actively running.
@@ -451,7 +457,7 @@
// ignored; both services live in system_server
}
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
- synchronized (mJobs) {
+ synchronized (mLock) {
// Let's go!
mReadyToRock = true;
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
@@ -487,7 +493,7 @@
private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
boolean update;
boolean rocking;
- synchronized (mJobs) {
+ synchronized (mLock) {
update = mJobs.add(jobStatus);
rocking = mReadyToRock;
}
@@ -509,7 +515,7 @@
private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
boolean removed;
boolean rocking;
- synchronized (mJobs) {
+ synchronized (mLock) {
// Remove from store as well as controllers.
removed = mJobs.remove(jobStatus, writeBack);
rocking = mReadyToRock;
@@ -693,14 +699,14 @@
@Override
public void handleMessage(Message message) {
- synchronized (mJobs) {
+ synchronized (mLock) {
if (!mReadyToRock) {
return;
}
}
switch (message.what) {
case MSG_JOB_EXPIRED:
- synchronized (mJobs) {
+ synchronized (mLock) {
JobStatus runNow = (JobStatus) message.obj;
// runNow can be null, which is a controller's way of indicating that its
// state is such that all ready jobs should be run immediately.
@@ -712,7 +718,7 @@
}
break;
case MSG_CHECK_JOB:
- synchronized (mJobs) {
+ synchronized (mLock) {
if (mReportedActive) {
// if jobs are currently being run, queue all ready jobs for execution.
queueReadyJobsForExecutionLockedH();
@@ -723,7 +729,7 @@
}
break;
case MSG_CHECK_JOB_GREEDY:
- synchronized (mJobs) {
+ synchronized (mLock) {
queueReadyJobsForExecutionLockedH();
}
break;
@@ -879,7 +885,7 @@
* here is where we decide whether to actually execute it.
*/
private void maybeRunPendingJobsH() {
- synchronized (mJobs) {
+ synchronized (mLock) {
if (mDeviceIdleMode) {
// If device is idle, we will not schedule jobs to run.
return;
@@ -1185,7 +1191,7 @@
void dumpInternal(PrintWriter pw) {
final long now = SystemClock.elapsedRealtime();
- synchronized (mJobs) {
+ synchronized (mLock) {
pw.print("Started users: ");
for (int i=0; i<mStartedUsers.size(); i++) {
pw.print("u" + mStartedUsers.get(i) + " ");
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index b249739..48549ce 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -103,6 +103,7 @@
private final JobCompletedListener mCompletedListener;
/** Used for service binding, etc. */
private final Context mContext;
+ private final Object mLock;
private final IBatteryStats mBatteryStats;
private PowerManager.WakeLock mWakeLock;
@@ -124,7 +125,6 @@
private int mPreferredUid;
IJobService service;
- private final Object mLock = new Object();
/**
* Whether this context is free. This is set to false at the start of execution, and reset to
* true when execution is complete.
@@ -137,13 +137,14 @@
private long mTimeoutElapsed;
JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, Looper looper) {
- this(service.getContext(), batteryStats, service, looper);
+ this(service.getContext(), service.getLock(), batteryStats, service, looper);
}
@VisibleForTesting
- JobServiceContext(Context context, IBatteryStats batteryStats,
+ JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
JobCompletedListener completedListener, Looper looper) {
mContext = context;
+ mLock = lock;
mBatteryStats = batteryStats;
mCallbackHandler = new JobServiceHandler(looper);
mCompletedListener = completedListener;
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 3565fc1..fa4190b 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -70,6 +70,7 @@
/** Threshold to adjust how often we want to write to the db. */
private static final int MAX_OPS_BEFORE_WRITE = 1;
final ArraySet<JobStatus> mJobSet;
+ final Object mLock;
final Context mContext;
private int mDirtyOperations;
@@ -85,7 +86,7 @@
synchronized (sSingletonLock) {
if (sSingleton == null) {
sSingleton = new JobStore(jobManagerService.getContext(),
- Environment.getDataDirectory());
+ jobManagerService.getLock(), Environment.getDataDirectory());
}
return sSingleton;
}
@@ -96,7 +97,7 @@
*/
@VisibleForTesting
public static JobStore initAndGetForTesting(Context context, File dataDir) {
- JobStore jobStoreUnderTest = new JobStore(context, dataDir);
+ JobStore jobStoreUnderTest = new JobStore(context, new Object(), dataDir);
jobStoreUnderTest.clear();
return jobStoreUnderTest;
}
@@ -104,7 +105,8 @@
/**
* Construct the instance of the job store. This results in a blocking read from disk.
*/
- private JobStore(Context context, File dataDir) {
+ private JobStore(Context context, Object lock, File dataDir) {
+ mLock = lock;
mContext = context;
mDirtyOperations = 0;
@@ -266,14 +268,14 @@
/**
* Runnable that writes {@link #mJobSet} out to xml.
- * NOTE: This Runnable locks on JobStore.this
+ * NOTE: This Runnable locks on mLock
*/
private class WriteJobsMapToDiskRunnable implements Runnable {
@Override
public void run() {
final long startElapsed = SystemClock.elapsedRealtime();
List<JobStatus> mStoreCopy = new ArrayList<JobStatus>();
- synchronized (JobStore.this) {
+ synchronized (mLock) {
// Copy over the jobs so we can release the lock before writing.
for (int i=0; i<mJobSet.size(); i++) {
JobStatus jobStatus = mJobSet.valueAt(i);
@@ -454,7 +456,7 @@
try {
List<JobStatus> jobs;
FileInputStream fis = mJobsFile.openRead();
- synchronized (JobStore.this) {
+ synchronized (mLock) {
jobs = readJobMapImpl(fis);
if (jobs != null) {
for (int i=0; i<jobs.size(); i++) {
@@ -678,8 +680,8 @@
parser.nextTag(); // Consume </extras>
JobStatus js = new JobStatus(
- jobBuilder.build(), uid, sourcePackageName, sourceUserId, elapsedRuntimes.first,
- elapsedRuntimes.second);
+ mLock, jobBuilder.build(), uid, sourcePackageName, sourceUserId,
+ elapsedRuntimes.first, elapsedRuntimes.second);
return js;
}
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 5f3da75..f7f34ae 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -48,14 +48,16 @@
public static AppIdleController get(JobSchedulerService service) {
synchronized (sCreationLock) {
if (sController == null) {
- sController = new AppIdleController(service, service.getContext());
+ sController = new AppIdleController(service, service.getContext(),
+ service.getLock());
}
return sController;
}
}
- private AppIdleController(StateChangedListener stateChangedListener, Context context) {
- super(stateChangedListener, context);
+ private AppIdleController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
+ super(stateChangedListener, context, lock);
mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
@@ -63,7 +65,7 @@
@Override
public void maybeStartTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
mTrackedTasks.add(jobStatus);
String packageName = jobStatus.getSourcePackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
@@ -78,7 +80,7 @@
@Override
public void maybeStopTrackingJob(JobStatus jobStatus, boolean forUpdate) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
mTrackedTasks.remove(jobStatus);
}
}
@@ -87,7 +89,7 @@
public void dumpControllerState(PrintWriter pw) {
pw.println("AppIdle");
pw.println("Parole On: " + mAppIdleParoleOn);
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
for (JobStatus task : mTrackedTasks) {
pw.print(task.getSourcePackageName());
pw.print(":idle=" + !task.appNotIdleConstraintSatisfied.get());
@@ -100,7 +102,7 @@
void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
// Flag if any app's idle state has changed
boolean changed = false;
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
if (mAppIdleParoleOn == isAppIdleParoleOn) {
return;
}
@@ -128,7 +130,7 @@
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
boolean changed = false;
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
if (mAppIdleParoleOn) {
return;
}
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index b322a3e..b101c82 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -53,7 +53,7 @@
synchronized (sCreationLock) {
if (sController == null) {
sController = new BatteryController(taskManagerService,
- taskManagerService.getContext());
+ taskManagerService.getContext(), taskManagerService.getLock());
}
}
return sController;
@@ -67,11 +67,12 @@
@VisibleForTesting
public static BatteryController getForTesting(StateChangedListener stateChangedListener,
Context context) {
- return new BatteryController(stateChangedListener, context);
+ return new BatteryController(stateChangedListener, context, new Object());
}
- private BatteryController(StateChangedListener stateChangedListener, Context context) {
- super(stateChangedListener, context);
+ private BatteryController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
+ super(stateChangedListener, context, lock);
mChargeTracker = new ChargingTracker();
mChargeTracker.startTracking();
}
@@ -80,7 +81,7 @@
public void maybeStartTrackingJob(JobStatus taskStatus, JobStatus lastJob) {
final boolean isOnStablePower = mChargeTracker.isOnStablePower();
if (taskStatus.hasChargingConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
mTrackedTasks.add(taskStatus);
taskStatus.chargingConstraintSatisfied.set(isOnStablePower);
}
@@ -90,7 +91,7 @@
@Override
public void maybeStopTrackingJob(JobStatus taskStatus, boolean forUpdate) {
if (taskStatus.hasChargingConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
mTrackedTasks.remove(taskStatus);
}
}
@@ -102,7 +103,7 @@
Slog.d(TAG, "maybeReportNewChargingState: " + stablePower);
}
boolean reportChange = false;
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
for (JobStatus ts : mTrackedTasks) {
boolean previous = ts.chargingConstraintSatisfied.getAndSet(stablePower);
if (previous != stablePower) {
@@ -200,7 +201,7 @@
public void dumpControllerState(PrintWriter pw) {
pw.println("Batt.");
pw.println("Stable power: " + mChargeTracker.isOnStablePower());
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
Iterator<JobStatus> it = mTrackedTasks.iterator();
if (it.hasNext()) {
pw.print(String.valueOf(it.next().hashCode()));
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index b84658a..29b54c2 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -58,14 +58,15 @@
public static ConnectivityController get(JobSchedulerService jms) {
synchronized (sCreationLock) {
if (mSingleton == null) {
- mSingleton = new ConnectivityController(jms, jms.getContext());
+ mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
}
return mSingleton;
}
}
- private ConnectivityController(StateChangedListener stateChangedListener, Context context) {
- super(stateChangedListener, context);
+ private ConnectivityController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
+ super(stateChangedListener, context, lock);
// Register connectivity changed BR.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
@@ -84,7 +85,7 @@
@Override
public void maybeStartTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
- synchronized (mTrackedJobs) {
+ synchronized (mLock) {
jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected);
jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered);
mTrackedJobs.add(jobStatus);
@@ -95,7 +96,7 @@
@Override
public void maybeStopTrackingJob(JobStatus jobStatus, boolean forUpdate) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
- synchronized (mTrackedJobs) {
+ synchronized (mLock) {
mTrackedJobs.remove(jobStatus);
}
}
@@ -105,7 +106,7 @@
* @param userId Id of the user for whom we are updating the connectivity state.
*/
private void updateTrackedJobs(int userId) {
- synchronized (mTrackedJobs) {
+ synchronized (mLock) {
boolean changed = false;
for (JobStatus js : mTrackedJobs) {
if (js.getUserId() != userId) {
@@ -128,7 +129,7 @@
* We know the network has just come up. We want to run any jobs that are ready.
*/
public synchronized void onNetworkActive() {
- synchronized (mTrackedJobs) {
+ synchronized (mLock) {
for (JobStatus js : mTrackedJobs) {
if (js.isReady()) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index 212cc94..af994f0 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -57,7 +57,7 @@
synchronized (sCreationLock) {
if (sController == null) {
sController = new ContentObserverController(taskManagerService,
- taskManagerService.getContext());
+ taskManagerService.getContext(), taskManagerService.getLock());
}
}
return sController;
@@ -66,17 +66,18 @@
@VisibleForTesting
public static ContentObserverController getForTesting(StateChangedListener stateChangedListener,
Context context) {
- return new ContentObserverController(stateChangedListener, context);
+ return new ContentObserverController(stateChangedListener, context, new Object());
}
- private ContentObserverController(StateChangedListener stateChangedListener, Context context) {
- super(stateChangedListener, context);
+ private ContentObserverController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
+ super(stateChangedListener, context, lock);
}
@Override
public void maybeStartTrackingJob(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasContentTriggerConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
if (taskStatus.contentObserverJobInstance == null) {
taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
}
@@ -128,7 +129,7 @@
@Override
public void prepareForExecution(JobStatus taskStatus) {
if (taskStatus.hasContentTriggerConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
if (taskStatus.contentObserverJobInstance != null) {
taskStatus.changedUris = taskStatus.contentObserverJobInstance.mChangedUris;
taskStatus.changedAuthorities
@@ -143,7 +144,7 @@
@Override
public void maybeStopTrackingJob(JobStatus taskStatus, boolean forUpdate) {
if (taskStatus.hasContentTriggerConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
if (!forUpdate) {
// We won't do this reset if being called for an update, because
// we know it will be immediately followed by maybeStartTrackingJob...
@@ -162,7 +163,7 @@
public void rescheduleForFailure(JobStatus newJob, JobStatus failureToReschedule) {
if (failureToReschedule.hasContentTriggerConstraint()
&& newJob.hasContentTriggerConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
// Our job has failed, and we are scheduling a new job for it.
// Copy the last reported content changes in to the new job, so when
// we schedule the new one we will pick them up and report them again.
@@ -184,7 +185,7 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
boolean reportChange = false;
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
final int N = mJobs.size();
for (int i=0; i<N; i++) {
JobInstance inst = mJobs.get(i);
@@ -256,7 +257,7 @@
@Override
public void dumpControllerState(PrintWriter pw) {
pw.println("Content.");
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
Iterator<JobStatus> it = mTrackedTasks.iterator();
if (it.hasNext()) {
pw.print(String.valueOf(it.next().hashCode()));
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 9f4cdef..6b85f22 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -51,14 +51,15 @@
public static IdleController get(JobSchedulerService service) {
synchronized (sCreationLock) {
if (sController == null) {
- sController = new IdleController(service, service.getContext());
+ sController = new IdleController(service, service.getContext(), service.getLock());
}
return sController;
}
}
- private IdleController(StateChangedListener stateChangedListener, Context context) {
- super(stateChangedListener, context);
+ private IdleController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
+ super(stateChangedListener, context, lock);
initIdleStateTracking();
}
@@ -68,7 +69,7 @@
@Override
public void maybeStartTrackingJob(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasIdleConstraint()) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
mTrackedTasks.add(taskStatus);
taskStatus.idleConstraintSatisfied.set(mIdleTracker.isIdle());
}
@@ -77,7 +78,7 @@
@Override
public void maybeStopTrackingJob(JobStatus taskStatus, boolean forUpdate) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
mTrackedTasks.remove(taskStatus);
}
}
@@ -86,7 +87,7 @@
* Interaction with the task manager service
*/
void reportNewIdleState(boolean isIdle) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
for (JobStatus task : mTrackedTasks) {
task.idleConstraintSatisfied.set(isIdle);
}
@@ -194,7 +195,7 @@
@Override
public void dumpControllerState(PrintWriter pw) {
- synchronized (mTrackedTasks) {
+ synchronized (mLock) {
pw.print("Idle: ");
pw.println(mIdleTracker.isIdle() ? "true" : "false");
pw.println(mTrackedTasks.size());
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index c4d564c..3cf1054 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -46,6 +46,11 @@
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
+ /**
+ * Service global lock. NOTE: this should be removed, having this class just rely on
+ * its callers doing the appropriate locking.
+ */
+ final Object lock;
final JobInfo job;
/** Uid of the package requesting this job. */
final int callingUid;
@@ -94,8 +99,9 @@
return callingUid;
}
- private JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
- int numFailures) {
+ private JobStatus(Object lock, JobInfo job, int callingUid, String sourcePackageName,
+ int sourceUserId, int numFailures) {
+ this.lock = lock;
this.job = job;
this.callingUid = callingUid;
this.name = job.getService().flattenToShortString();
@@ -124,8 +130,9 @@
/** Copy constructor. */
public JobStatus(JobStatus jobStatus) {
- this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getSourcePackageName(),
- jobStatus.getSourceUserId(), jobStatus.getNumFailures());
+ this(jobStatus.lock, jobStatus.getJob(), jobStatus.getUid(),
+ jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
+ jobStatus.getNumFailures());
this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime();
this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed();
}
@@ -138,8 +145,9 @@
* @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
* calling userId.
*/
- public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId) {
- this(job, callingUid, sourcePackageName, sourceUserId, 0);
+ public JobStatus(Object lock, JobInfo job, int callingUid, String sourcePackageName,
+ int sourceUserId) {
+ this(lock, job, callingUid, sourcePackageName, sourceUserId, 0);
final long elapsedNow = SystemClock.elapsedRealtime();
@@ -161,9 +169,9 @@
* wallclock runtime rather than resetting it on every boot.
* We consider a freshly loaded job to no longer be in back-off.
*/
- public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
- long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
- this(job, callingUid, sourcePackageName, sourceUserId, 0);
+ public JobStatus(Object lock, JobInfo job, int callingUid, String sourcePackageName,
+ int sourceUserId, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+ this(lock, job, callingUid, sourcePackageName, sourceUserId, 0);
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
@@ -172,7 +180,8 @@
/** Create a new job to be rescheduled with the provided parameters. */
public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
long newLatestRuntimeElapsedMillis, int backoffAttempt) {
- this(rescheduling.job, rescheduling.getUid(), rescheduling.getSourcePackageName(),
+ this(rescheduling.lock, rescheduling.job, rescheduling.getUid(),
+ rescheduling.getSourcePackageName(),
rescheduling.getSourceUserId(), backoffAttempt);
earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis;
@@ -275,27 +284,31 @@
* @return Whether or not this job is ready to run, based on its requirements. This is true if
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
*/
- public synchronized boolean isReady() {
- // Deadline constraint trumps other constraints (except for periodic jobs where deadline
- // (is an implementation detail. A periodic job should only run if it's constraints are
- // satisfied).
- // AppNotIdle implicit constraint trumps all!
- return (isConstraintsSatisfied()
+ public boolean isReady() {
+ synchronized (lock) {
+ // Deadline constraint trumps other constraints (except for periodic jobs where deadline
+ // (is an implementation detail. A periodic job should only run if it's constraints are
+ // satisfied).
+ // AppNotIdle implicit constraint trumps all!
+ return (isConstraintsSatisfied()
|| (!job.isPeriodic()
- && hasDeadlineConstraint() && deadlineConstraintSatisfied.get()))
- && appNotIdleConstraintSatisfied.get();
+ && hasDeadlineConstraint() && deadlineConstraintSatisfied.get()))
+ && appNotIdleConstraintSatisfied.get();
+ }
}
/**
* @return Whether the constraints set on this job are satisfied.
*/
- public synchronized boolean isConstraintsSatisfied() {
- return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
- && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
- && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
- && (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
- && (!hasIdleConstraint() || idleConstraintSatisfied.get())
- && (!hasContentTriggerConstraint() || contentTriggerConstraintSatisfied.get());
+ public boolean isConstraintsSatisfied() {
+ synchronized (lock) {
+ return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
+ && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
+ && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
+ && (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
+ && (!hasIdleConstraint() || idleConstraintSatisfied.get())
+ && (!hasContentTriggerConstraint() || contentTriggerConstraintSatisfied.get());
+ }
}
public boolean matches(int uid, int jobId) {
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index b619ea8..8b8611a 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -30,13 +30,16 @@
*/
public abstract class StateController {
protected static final boolean DEBUG = JobSchedulerService.DEBUG;
- protected Context mContext;
- protected StateChangedListener mStateChangedListener;
+ protected final Context mContext;
+ protected final Object mLock;
+ protected final StateChangedListener mStateChangedListener;
protected boolean mDeviceIdleMode;
- public StateController(StateChangedListener stateChangedListener, Context context) {
+ public StateController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
mStateChangedListener = stateChangedListener;
mContext = context;
+ mLock = lock;
}
public void deviceIdleModeChanged(boolean enabled) {
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index a68c3ad..bf17827 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -54,13 +54,14 @@
public static synchronized TimeController get(JobSchedulerService jms) {
if (mSingleton == null) {
- mSingleton = new TimeController(jms, jms.getContext());
+ mSingleton = new TimeController(jms, jms.getContext(), jms.getLock());
}
return mSingleton;
}
- private TimeController(StateChangedListener stateChangedListener, Context context) {
- super(stateChangedListener, context);
+ private TimeController(StateChangedListener stateChangedListener, Context context,
+ Object lock) {
+ super(stateChangedListener, context, lock);
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
@@ -71,27 +72,28 @@
* list.
*/
@Override
- public synchronized void maybeStartTrackingJob(JobStatus job, JobStatus lastJob) {
- if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
- maybeStopTrackingJob(job, false);
- boolean isInsert = false;
- ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
- while (it.hasPrevious()) {
- JobStatus ts = it.previous();
- if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
- // Insert
- isInsert = true;
- break;
+ public void maybeStartTrackingJob(JobStatus job, JobStatus lastJob) {
+ synchronized (mLock) {
+ if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
+ maybeStopTrackingJob(job, false);
+ boolean isInsert = false;
+ ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
+ while (it.hasPrevious()) {
+ JobStatus ts = it.previous();
+ if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
+ // Insert
+ isInsert = true;
+ break;
+ }
}
+ if (isInsert) {
+ it.next();
+ }
+ it.add(job);
+ maybeUpdateAlarms(
+ job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
+ job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE);
}
- if(isInsert)
- {
- it.next();
- }
- it.add(job);
- maybeUpdateAlarms(
- job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
- job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE);
}
}
@@ -101,10 +103,12 @@
* Really an == comparison should be enough, but why play with fate? We'll do <=.
*/
@Override
- public synchronized void maybeStopTrackingJob(JobStatus job, boolean forUpdate) {
- if (mTrackedJobs.remove(job)) {
- checkExpiredDelaysAndResetAlarm();
- checkExpiredDeadlinesAndResetAlarm();
+ public void maybeStopTrackingJob(JobStatus job, boolean forUpdate) {
+ synchronized (mLock) {
+ if (mTrackedJobs.remove(job)) {
+ checkExpiredDelaysAndResetAlarm();
+ checkExpiredDeadlinesAndResetAlarm();
+ }
}
}
@@ -131,63 +135,67 @@
* Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler
* if so, removing them from this list, and updating the alarm for the next expiry time.
*/
- private synchronized void checkExpiredDeadlinesAndResetAlarm() {
- long nextExpiryTime = Long.MAX_VALUE;
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ private void checkExpiredDeadlinesAndResetAlarm() {
+ synchronized (mLock) {
+ long nextExpiryTime = Long.MAX_VALUE;
+ final long nowElapsedMillis = SystemClock.elapsedRealtime();
- Iterator<JobStatus> it = mTrackedJobs.iterator();
- while (it.hasNext()) {
- JobStatus job = it.next();
- if (!job.hasDeadlineConstraint()) {
- continue;
- }
- final long jobDeadline = job.getLatestRunTimeElapsed();
+ Iterator<JobStatus> it = mTrackedJobs.iterator();
+ while (it.hasNext()) {
+ JobStatus job = it.next();
+ if (!job.hasDeadlineConstraint()) {
+ continue;
+ }
+ final long jobDeadline = job.getLatestRunTimeElapsed();
- if (jobDeadline <= nowElapsedMillis) {
- job.deadlineConstraintSatisfied.set(true);
- mStateChangedListener.onRunJobNow(job);
- it.remove();
- } else { // Sorted by expiry time, so take the next one and stop.
- nextExpiryTime = jobDeadline;
- break;
+ if (jobDeadline <= nowElapsedMillis) {
+ job.deadlineConstraintSatisfied.set(true);
+ mStateChangedListener.onRunJobNow(job);
+ it.remove();
+ } else { // Sorted by expiry time, so take the next one and stop.
+ nextExpiryTime = jobDeadline;
+ break;
+ }
}
+ setDeadlineExpiredAlarm(nextExpiryTime);
}
- setDeadlineExpiredAlarm(nextExpiryTime);
}
/**
* Handles alarm that notifies us that a job's delay has expired. Iterates through the list of
* tracked jobs and marks them as ready as appropriate.
*/
- private synchronized void checkExpiredDelaysAndResetAlarm() {
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
- long nextDelayTime = Long.MAX_VALUE;
- boolean ready = false;
- Iterator<JobStatus> it = mTrackedJobs.iterator();
- while (it.hasNext()) {
- final JobStatus job = it.next();
- if (!job.hasTimingDelayConstraint()) {
- continue;
- }
- final long jobDelayTime = job.getEarliestRunTime();
- if (jobDelayTime <= nowElapsedMillis) {
- job.timeDelayConstraintSatisfied.set(true);
- if (canStopTrackingJob(job)) {
- it.remove();
+ private void checkExpiredDelaysAndResetAlarm() {
+ synchronized (mLock) {
+ final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ long nextDelayTime = Long.MAX_VALUE;
+ boolean ready = false;
+ Iterator<JobStatus> it = mTrackedJobs.iterator();
+ while (it.hasNext()) {
+ final JobStatus job = it.next();
+ if (!job.hasTimingDelayConstraint()) {
+ continue;
}
- if (job.isReady()) {
- ready = true;
- }
- } else { // Keep going through list to get next delay time.
- if (nextDelayTime > jobDelayTime) {
- nextDelayTime = jobDelayTime;
+ final long jobDelayTime = job.getEarliestRunTime();
+ if (jobDelayTime <= nowElapsedMillis) {
+ job.timeDelayConstraintSatisfied.set(true);
+ if (canStopTrackingJob(job)) {
+ it.remove();
+ }
+ if (job.isReady()) {
+ ready = true;
+ }
+ } else { // Keep going through list to get next delay time.
+ if (nextDelayTime > jobDelayTime) {
+ nextDelayTime = jobDelayTime;
+ }
}
}
+ if (ready) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ setDelayExpiredAlarm(nextDelayTime);
}
- if (ready) {
- mStateChangedListener.onControllerStateChanged();
- }
- setDelayExpiredAlarm(nextDelayTime);
}
private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 14722bf..7378bde 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1087,19 +1087,17 @@
}
synchronized (mLock) {
- WallpaperData wallpaper = null;
- if (which == FLAG_SET_LOCK) {
- wallpaper = mLockWallpaperMap.get(wallpaperUserId);
+ final SparseArray<WallpaperData> whichSet =
+ (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
+ WallpaperData wallpaper = whichSet.get(wallpaperUserId);
+ if (wallpaper == null) {
+ // common case, this is the first lookup post-boot of the system or
+ // unified lock, so we bring up the saved state lazily now and recheck.
+ loadSettingsLocked(wallpaperUserId);
+ wallpaper = whichSet.get(wallpaperUserId);
if (wallpaper == null) {
- // If you ask for the lock wallpaper specifically and there isn't one,
- // we say so rather than returning the system wallpaper as fallback.
return null;
}
- } else {
- wallpaper = mWallpaperMap.get(wallpaperUserId);
- }
- if (wallpaper == null) {
- return null;
}
try {
if (outParams != null) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index ba9e640..5cb7099 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -216,6 +216,9 @@
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
+ private int mLastClipRevealMaxTranslation;
+ private boolean mLastHadClipReveal;
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
@@ -337,6 +340,9 @@
if (!isRunning()) {
mAppTransitionState = APP_STATE_IDLE;
notifyAppTransitionPendingLocked();
+ mLastHadClipReveal = false;
+ mLastClipRevealMaxTranslation = 0;
+ mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
return true;
}
return false;
@@ -641,11 +647,28 @@
bitmap, new Rect(left, top, left + width, top + height));
}
+ /**
+ * @return the duration of the last clip reveal animation
+ */
long getLastClipRevealTransitionDuration() {
return mLastClipRevealTransitionDuration;
}
/**
+ * @return the maximum distance the app surface is traveling of the last clip reveal animation
+ */
+ int getLastClipRevealMaxTranslation() {
+ return mLastClipRevealMaxTranslation;
+ }
+
+ /**
+ * @return true if in the last app transition had a clip reveal animation, false otherwise
+ */
+ boolean hadClipRevealAnimation() {
+ return mLastHadClipReveal;
+ }
+
+ /**
* Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
* the start rect is outside of the target rect, and there is a lot of movement going on.
*
@@ -748,7 +771,13 @@
set.setZAdjustment(Animation.ZORDER_TOP);
set.initialize(appWidth, appHeight, appWidth, appHeight);
anim = set;
+ mLastHadClipReveal = true;
mLastClipRevealTransitionDuration = duration;
+
+ // If the start rect was full inside the target rect (cutOff == false), we don't need
+ // to store the translation, because it's only used if cutOff == true.
+ mLastClipRevealMaxTranslation = cutOff
+ ? Math.max(Math.abs(translationY), Math.abs(translationX)) : 0;
} else {
final long duration;
switch (transit) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index d7c1b32..b6aa3f2 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -21,14 +21,12 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDockedStackListener;
import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import com.android.server.wm.DimLayer.DimLayerUser;
@@ -52,6 +50,30 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
+ /**
+ * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
+ * revealing surface at the earliest.
+ */
+ private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
+
+ /**
+ * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
+ * revealing surface at the latest.
+ */
+ private static final float CLIP_REVEAL_MEET_LAST = 1f;
+
+ /**
+ * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
+ * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
+ */
+ private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
+
+ /**
+ * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
+ * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
+ */
+ private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
+
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final int mDividerWindowWidth;
@@ -74,6 +96,7 @@
private float mAnimationTarget;
private long mAnimationDuration;
private final Interpolator mMinimizedDockInterpolator;
+ private float mMaximizeMeetFraction;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -342,6 +365,7 @@
return false;
}
+ final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
@@ -350,16 +374,15 @@
: DEFAULT_APP_TRANSITION_DURATION;
mAnimationDuration = (long)
(transitionDuration * mService.getTransitionAnimationScaleLocked());
+ mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
notifyDockedStackMinimizedChanged(mMinimizedDock,
- mAnimationDuration);
+ (long) (mAnimationDuration * mMaximizeMeetFraction));
}
float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
.getInterpolation(t);
- final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
if (stack != null) {
- final float amount = t * mAnimationTarget + (1 - t) * mAnimationStart;
- if (stack.setAdjustedForMinimizedDock(amount)) {
+ if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
mService.mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -371,6 +394,54 @@
}
}
+ /**
+ * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
+ */
+ private float getMinimizeAmount(TaskStack stack, float t) {
+ final float naturalAmount = t * mAnimationTarget + (1 - t) * mAnimationStart;
+ if (isAnimationMaximizing()) {
+ return adjustMaximizeAmount(stack, t, naturalAmount);
+ } else {
+ return naturalAmount;
+ }
+ }
+
+ /**
+ * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
+ * during the transition such that the edge of the clip reveal rect is met earlier in the
+ * transition so we don't create a visible "hole", but only if both the clip reveal and the
+ * docked stack divider start from about the same portion on the screen.
+ */
+ private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
+ if (mMaximizeMeetFraction == 1f) {
+ return naturalAmount;
+ }
+ final int minimizeDistance = stack.getMinimizeDistance();
+ float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
+ / (float) minimizeDistance;
+ final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
+ final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
+ return amountPrime * t2 + naturalAmount * (1 - t2);
+ }
+
+ /**
+ * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
+ * edge. See {@link #adjustMaximizeAmount}.
+ */
+ private float getClipRevealMeetFraction(TaskStack stack) {
+ if (!isAnimationMaximizing() || stack == null ||
+ !mService.mAppTransition.hadClipRevealAnimation()) {
+ return 1f;
+ }
+ final int minimizeDistance = stack.getMinimizeDistance();
+ final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
+ / (float) minimizeDistance;
+ final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
+ / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
+ return CLIP_REVEAL_MEET_EARLIEST
+ + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
+ }
+
@Override
public boolean isFullscreen() {
return false;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c9873a5..2293e4d 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -877,6 +877,26 @@
}
/**
+ * @return the distance in pixels how much the stack gets minimized from it's original size
+ */
+ int getMinimizeDistance() {
+ final int dockSide = getDockSide();
+ if (dockSide == DOCKED_INVALID) {
+ return 0;
+ }
+
+ if (dockSide == DOCKED_TOP) {
+ mService.getStableInsetsLocked(mTmpRect);
+ int topInset = mTmpRect.top;
+ return mBounds.bottom - topInset;
+ } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
+ return mBounds.width() - mDockedStackMinimizeThickness;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
* Updates the adjustment depending on it's current state.
*/
void updateAdjustedBounds() {
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 577c3a1..fbcd156 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -58,7 +58,7 @@
.setMinimumLatency(runFromMillis)
.setPersisted(true)
.build();
- final JobStatus ts = new JobStatus(task, SOME_UID, null, -1);
+ final JobStatus ts = new JobStatus(this, task, SOME_UID, null, -1);
mTaskStoreUnderTest.add(ts);
Thread.sleep(IO_WAIT);
// Manually load tasks from xml file.
@@ -91,8 +91,8 @@
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setPersisted(true)
.build();
- final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID, null, -1);
- final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID, null, -1);
+ final JobStatus taskStatus1 = new JobStatus(this, task1, SOME_UID, null, -1);
+ final JobStatus taskStatus2 = new JobStatus(this, task2, SOME_UID, null, -1);
mTaskStoreUnderTest.add(taskStatus1);
mTaskStoreUnderTest.add(taskStatus2);
Thread.sleep(IO_WAIT);
@@ -140,7 +140,7 @@
extras.putInt("into", 3);
b.setExtras(extras);
final JobInfo task = b.build();
- JobStatus taskStatus = new JobStatus(task, SOME_UID, null, -1);
+ JobStatus taskStatus = new JobStatus(this, task, SOME_UID, null, -1);
mTaskStoreUnderTest.add(taskStatus);
Thread.sleep(IO_WAIT);
@@ -157,7 +157,8 @@
.setPeriodic(10000L)
.setRequiresCharging(true)
.setPersisted(true);
- JobStatus taskStatus = new JobStatus(b.build(), SOME_UID, "com.google.android.gms", 0);
+ JobStatus taskStatus = new JobStatus(this, b.build(), SOME_UID,
+ "com.google.android.gms", 0);
mTaskStoreUnderTest.add(taskStatus);
Thread.sleep(IO_WAIT);
@@ -178,7 +179,7 @@
.setPeriodic(5*60*60*1000, 1*60*60*1000)
.setRequiresCharging(true)
.setPersisted(true);
- JobStatus taskStatus = new JobStatus(b.build(), SOME_UID, null, -1);
+ JobStatus taskStatus = new JobStatus(this, b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(taskStatus);
Thread.sleep(IO_WAIT);
@@ -203,7 +204,8 @@
SystemClock.elapsedRealtime() + (TWO_HOURS * ONE_HOUR) + TWO_HOURS; // > period+flex
final long invalidEarlyRuntimeElapsedMillis =
invalidLateRuntimeElapsedMillis - TWO_HOURS; // Early is (late - period).
- final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage", 0 /* sourceUserId */,
+ final JobStatus js = new JobStatus(this, b.build(), SOME_UID, "somePackage",
+ 0 /* sourceUserId */,
invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
mTaskStoreUnderTest.add(js);
@@ -229,7 +231,7 @@
.setOverrideDeadline(5000)
.setPriority(42)
.setPersisted(true);
- final JobStatus js = new JobStatus(b.build(), SOME_UID, null, -1);
+ final JobStatus js = new JobStatus(this, b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(js);
Thread.sleep(IO_WAIT);
final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
@@ -245,12 +247,12 @@
JobInfo.Builder b = new Builder(42, mComponent)
.setOverrideDeadline(10000)
.setPersisted(false);
- JobStatus jsNonPersisted = new JobStatus(b.build(), SOME_UID, null, -1);
+ JobStatus jsNonPersisted = new JobStatus(this, b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(jsNonPersisted);
b = new Builder(43, mComponent)
.setOverrideDeadline(10000)
.setPersisted(true);
- JobStatus jsPersisted = new JobStatus(b.build(), SOME_UID, null, -1);
+ JobStatus jsPersisted = new JobStatus(this, b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(jsPersisted);
Thread.sleep(IO_WAIT);
final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();