Merge "Add missing cast tile" into nyc-dev
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index b2e8d33..627183f 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -16,8 +16,6 @@
 
 package android.transition;
 
-import com.android.internal.R;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -28,6 +26,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.R;
+
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes and fades views in or out when they become visible
@@ -144,12 +144,9 @@
             Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
                     startView + ", " + view);
         }
-        float startAlpha = 0;
-        if (startValues != null) {
-            startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
-            if (startAlpha == 1) {
-                startAlpha = 0;
-            }
+        float startAlpha = getStartAlpha(startValues, 0);
+        if (startAlpha == 1) {
+            startAlpha = 0;
         }
         return createAnimation(view, startAlpha, 1);
     }
@@ -157,13 +154,21 @@
     @Override
     public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
             TransitionValues endValues) {
-        float startAlpha = 1;
-        if (startValues != null) {
-            startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
-        }
+        float startAlpha = getStartAlpha(startValues, 1);
         return createAnimation(view, startAlpha, 0);
     }
 
+    private static float getStartAlpha(TransitionValues startValues, float fallbackValue) {
+        float startAlpha = fallbackValue;
+        if (startValues != null) {
+            Float startAlphaFloat = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+            if (startAlphaFloat != null) {
+                startAlpha = startAlphaFloat;
+            }
+        }
+        return startAlpha;
+    }
+
     private static class FadeAnimatorListener extends AnimatorListenerAdapter {
         private final View mView;
         private boolean mLayerTypeChanged = false;
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 2507e4dd..de32dd9 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -1,5 +1,4 @@
 #include "GraphicsJNI.h"
-#include "SkComposeShader.h"
 #include "SkGradientShader.h"
 #include "SkShader.h"
 #include "SkXfermode.h"
@@ -232,7 +231,7 @@
     SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
     SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
     SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
-    SkShader* shader = new SkComposeShader(shaderA, shaderB, mode);
+    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, mode);
     return reinterpret_cast<jlong>(shader);
 }
 
@@ -243,7 +242,7 @@
     SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
     SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
     SkAutoTUnref<SkXfermode> xfermode(SkXfermode::Create(mode));
-    SkShader* shader = new SkComposeShader(shaderA, shaderB, xfermode.get());
+    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, xfermode.get());
     return reinterpret_cast<jlong>(shader);
 }
 
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 9c691e5..ee0861a 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -251,6 +251,7 @@
         boolean enabled = false;
         boolean pressed = false;
         boolean focused = false;
+        boolean hovered = false;
 
         for (int state : stateSet) {
             if (state == R.attr.state_enabled) {
@@ -259,11 +260,13 @@
                 focused = true;
             } else if (state == R.attr.state_pressed) {
                 pressed = true;
+            } else if (state == R.attr.state_hovered) {
+                hovered = true;
             }
         }
 
         setRippleActive(enabled && pressed);
-        setBackgroundActive(focused || (enabled && pressed), focused);
+        setBackgroundActive(hovered || focused || (enabled && pressed), focused || hovered);
 
         return changed;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index 7b62f4e..f154331 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -65,7 +65,7 @@
     }
 
     public TaskStackHorizontalViewAdapter(List tasks) {
-        mTaskList = new ArrayList<>(tasks);
+        mTaskList = new ArrayList<Task>(tasks);
     }
 
     public void setNewStackTasks(List tasks) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 0febbd2..97d7dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -496,8 +496,6 @@
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
 
-        updateRTLOrder();
-
         try {
             WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() {
                 @Override
@@ -590,7 +588,6 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         boolean uiCarModeChanged = updateCarMode(newConfig);
-        updateRTLOrder();
         updateTaskSwitchHelper();
         if (uiCarModeChanged) {
             // uiMode changed either from carmode or to carmode.
@@ -618,59 +615,6 @@
         return uiCarModeChanged;
     }
 
-    /**
-     * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
-     * have to do it manually
-     */
-    private void updateRTLOrder() {
-        boolean isLayoutRtl = getResources().getConfiguration()
-                .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        if (mIsLayoutRtl != isLayoutRtl) {
-
-            // We swap all children of the 90 and 270 degree layouts, since they are vertical
-            View rotation90 = mRotatedViews[Surface.ROTATION_90];
-            swapChildrenOrderIfVertical(rotation90);
-
-            View rotation270 = mRotatedViews[Surface.ROTATION_270];
-            if (rotation90 != rotation270) {
-                swapChildrenOrderIfVertical(rotation270);
-            }
-            mIsLayoutRtl = isLayoutRtl;
-        }
-    }
-
-    /**
-     * Swaps the children order of a LinearLayout if it's orientation is Vertical
-     *
-     * @param group The LinearLayout to swap the children from.
-     */
-    private void swapChildrenOrderIfVertical(View group) {
-        if (group instanceof LinearLayout) {
-            LinearLayout linearLayout = (LinearLayout) group;
-            if (linearLayout.getOrientation() == VERTICAL) {
-                if ((linearLayout.getGravity() & Gravity.TOP) != 0) {
-                    linearLayout.setGravity(Gravity.BOTTOM);
-                } else if ((linearLayout.getGravity() & Gravity.BOTTOM) != 0) {
-                    linearLayout.setGravity(Gravity.TOP);
-                }
-                int childCount = linearLayout.getChildCount();
-                ArrayList<View> childList = new ArrayList<>(childCount);
-                for (int i = 0; i < childCount; i++) {
-                    childList.add(linearLayout.getChildAt(i));
-                }
-                linearLayout.removeAllViews();
-                for (int i = childCount - 1; i >= 0; i--) {
-                    linearLayout.addView(childList.get(i));
-                }
-            }
-        } else if (group instanceof ViewGroup) {
-            ViewGroup viewGroup = (ViewGroup) group;
-            for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                swapChildrenOrderIfVertical(viewGroup.getChildAt(i));
-            }
-        }
-    }
-
     /*
     @Override
     protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
index da16954..3682aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
@@ -16,31 +16,79 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import java.util.ArrayList;
+
 /**
  * Automatically reverses the order of children as they are added.
  * Also reverse the width and height values of layout params
  */
 public class ReverseLinearLayout extends LinearLayout {
 
+    private boolean mIsLayoutRtl;
+
     public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 
     @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIsLayoutRtl = getResources().getConfiguration()
+                .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    }
+
+    @Override
     public void addView(View child) {
         reversParams(child.getLayoutParams());
-        super.addView(child, 0);
+        if (mIsLayoutRtl) {
+            super.addView(child);
+        } else {
+            super.addView(child, 0);
+        }
     }
 
     @Override
     public void addView(View child, ViewGroup.LayoutParams params) {
         reversParams(params);
-        super.addView(child, 0, params);
+        if (mIsLayoutRtl) {
+            super.addView(child, params);
+        } else {
+            super.addView(child, 0, params);
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateRTLOrder();
+    }
+
+    /**
+     * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
+     * have to do it manually
+     */
+    private void updateRTLOrder() {
+        boolean isLayoutRtl = getResources().getConfiguration()
+                .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        if (mIsLayoutRtl != isLayoutRtl) {
+            // RTL changed, swap the order of all views.
+            int childCount = getChildCount();
+            ArrayList<View> childList = new ArrayList<>(childCount);
+            for (int i = 0; i < childCount; i++) {
+                childList.add(getChildAt(i));
+            }
+            removeAllViews();
+            for (int i = childCount - 1; i >= 0; i--) {
+                super.addView(childList.get(i));
+            }
+            mIsLayoutRtl = isLayoutRtl;
+        }
     }
 
     private void reversParams(ViewGroup.LayoutParams params) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 40283728..57cede8 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -231,10 +231,7 @@
     }
 
     public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId) {
-        JobStatus jobStatus = new JobStatus(job, uId);
-        if (packageName != null) {
-            jobStatus.setSource(packageName, userId);
-        }
+        JobStatus jobStatus = new JobStatus(job, uId, packageName, userId);
         try {
             if (ActivityManagerNative.getDefault().getAppStartMode(uId,
                     job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f6a6778..3565fc1 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -282,8 +282,7 @@
                         continue;
                     }
 
-                    JobStatus copy = new JobStatus(jobStatus.getJob(), jobStatus.getUid(),
-                            jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed());
+                    JobStatus copy = new JobStatus(jobStatus);
                     mStoreCopy.add(copy);
                 }
             }
@@ -544,7 +543,7 @@
         private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             JobInfo.Builder jobBuilder;
-            int uid, userId;
+            int uid, sourceUserId;
 
             // Read out job identifier attributes and priority.
             try {
@@ -557,7 +556,7 @@
                     jobBuilder.setPriority(Integer.valueOf(val));
                 }
                 val = parser.getAttributeValue(null, "sourceUserId");
-                userId = val == null ? -1 : Integer.valueOf(val);
+                sourceUserId = val == null ? -1 : Integer.valueOf(val);
             } catch (NumberFormatException e) {
                 Slog.e(TAG, "Error parsing job's required fields, skipping");
                 return null;
@@ -608,9 +607,9 @@
                 try {
                     String val = parser.getAttributeValue(null, "period");
                     final long periodMillis = Long.valueOf(val);
-                    jobBuilder.setPeriodic(periodMillis);
                     val = parser.getAttributeValue(null, "flex");
                     final long flexMillis = (val != null) ? Long.valueOf(val) : periodMillis;
+                    jobBuilder.setPeriodic(periodMillis, flexMillis);
                     // As a sanity check, cap the recreated run time to be no later than flex+period
                     // from now. This is the latest the periodic could be pushed out. This could
                     // happen if the periodic ran early (at flex time before period), and then the
@@ -679,10 +678,8 @@
             parser.nextTag(); // Consume </extras>
 
             JobStatus js = new JobStatus(
-                    jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second);
-            if (userId != -1) {
-                js.setSource(sourcePackageName, userId);
-            }
+                    jobBuilder.build(), uid, sourcePackageName, sourceUserId, elapsedRuntimes.first,
+                    elapsedRuntimes.second);
             return js;
         }
 
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 e749433..c4d564c 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -48,13 +48,13 @@
 
     final JobInfo job;
     /** Uid of the package requesting this job. */
-    final int uId;
+    final int callingUid;
     final String name;
     final String tag;
 
-    String sourcePackageName;
-    int sourceUserId = -1;
-    int sourceUid = -1;
+    final String sourcePackageName;
+    final int sourceUserId;
+    final int sourceUid;
 
     // Constraints.
     final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
@@ -91,30 +91,55 @@
 
     /** Provide a handle to the service that this job will be run on. */
     public int getServiceToken() {
-        return uId;
+        return callingUid;
     }
 
-    private JobStatus(JobInfo job, int uId, int numFailures) {
+    private JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
+                      int numFailures) {
         this.job = job;
-        this.uId = uId;
-        this.sourceUid = uId;
+        this.callingUid = callingUid;
         this.name = job.getService().flattenToShortString();
         this.tag = "*job*/" + this.name;
         this.numFailures = numFailures;
+
+        int tempSourceUid = -1;
+        if (sourceUserId != -1 && sourcePackageName != null) {
+            try {
+                tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
+                        sourceUserId);
+            } catch (RemoteException ex) {
+                // Can't happen, PackageManager runs in the same process.
+            }
+        }
+        if (tempSourceUid == -1) {
+            this.sourceUid = callingUid;
+            this.sourceUserId = UserHandle.getUserId(callingUid);
+            this.sourcePackageName = job.getService().getPackageName();
+        } else {
+            this.sourceUid = tempSourceUid;
+            this.sourceUserId = sourceUserId;
+            this.sourcePackageName = sourcePackageName;
+        }
     }
 
     /** Copy constructor. */
     public JobStatus(JobStatus jobStatus) {
-        this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getNumFailures());
-        this.sourceUserId = jobStatus.sourceUserId;
-        this.sourcePackageName = jobStatus.sourcePackageName;
+        this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getSourcePackageName(),
+                jobStatus.getSourceUserId(), jobStatus.getNumFailures());
         this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime();
         this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed();
     }
 
-    /** Create a newly scheduled job. */
-    public JobStatus(JobInfo job, int uId) {
-        this(job, uId, 0);
+    /**
+     * Create a newly scheduled job.
+     * @param callingUid Uid of the package that scheduled this job.
+     * @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates
+     *                          the calling package is the source.
+     * @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);
 
         final long elapsedNow = SystemClock.elapsedRealtime();
 
@@ -136,9 +161,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 uId, long earliestRunTimeElapsedMillis,
-                      long latestRunTimeElapsedMillis) {
-        this(job, uId, 0);
+    public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
+                     long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+        this(job, callingUid, sourcePackageName, sourceUserId, 0);
 
         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
@@ -147,9 +172,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(), backoffAttempt);
-        this.sourceUserId = rescheduling.sourceUserId;
-        this.sourcePackageName = rescheduling.sourcePackageName;
+        this(rescheduling.job, rescheduling.getUid(), rescheduling.getSourcePackageName(),
+                rescheduling.getSourceUserId(), backoffAttempt);
 
         earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis;
         latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis;
@@ -172,7 +196,7 @@
     }
 
     public String getSourcePackageName() {
-        return sourcePackageName != null ? sourcePackageName : job.getService().getPackageName();
+        return sourcePackageName;
     }
 
     public int getSourceUid() {
@@ -180,18 +204,15 @@
     }
 
     public int getSourceUserId() {
-        if (sourceUserId == -1) {
-            sourceUserId = getUserId();
-        }
         return sourceUserId;
     }
 
     public int getUserId() {
-        return UserHandle.getUserId(uId);
+        return UserHandle.getUserId(callingUid);
     }
 
     public int getUid() {
-        return uId;
+        return callingUid;
     }
 
     public String getName() {
@@ -278,7 +299,7 @@
     }
 
     public boolean matches(int uid, int jobId) {
-        return this.job.getId() == jobId && this.uId == uid;
+        return this.job.getId() == jobId && this.callingUid == uid;
     }
 
     @Override
@@ -312,22 +333,6 @@
         }
     }
 
-    public void setSource(String sourcePackageName, int sourceUserId) {
-        this.sourcePackageName = sourcePackageName;
-        this.sourceUserId = sourceUserId;
-        try {
-            sourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
-                    sourceUserId);
-        } catch (RemoteException ex) {
-            // Can't happen, PackageManager runs in the same process.
-        }
-        if (sourceUid == -1) {
-            sourceUid = uId;
-            this.sourceUserId = getUserId();
-            this.sourcePackageName = null;
-        }
-    }
-
     /**
      * Convenience function to identify a job uniquely without pulling all the data that
      * {@link #toString()} returns.
@@ -338,7 +343,7 @@
         sb.append(" jId=");
         sb.append(job.getId());
         sb.append(" uid=");
-        UserHandle.formatUid(sb, uId);
+        UserHandle.formatUid(sb, callingUid);
         sb.append(' ');
         sb.append(job.getService().flattenToShortString());
         return sb.toString();
@@ -346,7 +351,7 @@
 
     // Dumpsys infrastructure
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); UserHandle.formatUid(pw, uId);
+        pw.print(prefix); UserHandle.formatUid(pw, callingUid);
         pw.print(" tag="); pw.println(tag);
         pw.print(prefix);
         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
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 ae0a25e..577c3a1 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);
+        final JobStatus ts = new JobStatus(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);
-        final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID);
+        final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID, null, -1);
+        final JobStatus taskStatus2 = new JobStatus(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);
+        JobStatus taskStatus = new JobStatus(task, SOME_UID, null, -1);
 
         mTaskStoreUnderTest.add(taskStatus);
         Thread.sleep(IO_WAIT);
@@ -151,17 +151,59 @@
         JobStatus loaded = jobStatusSet.iterator().next();
         assertTasksEqual(task, loaded.getJob());
     }
+    public void testWritingTaskWithSourcePackage() throws Exception {
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setRequiresDeviceIdle(true)
+                .setPeriodic(10000L)
+                .setRequiresCharging(true)
+                .setPersisted(true);
+        JobStatus taskStatus = new JobStatus(b.build(), SOME_UID, "com.google.android.gms", 0);
+
+        mTaskStoreUnderTest.add(taskStatus);
+        Thread.sleep(IO_WAIT);
+
+        final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.iterator().next();
+        assertEquals("Source package not equal.", loaded.getSourcePackageName(),
+                taskStatus.getSourcePackageName());
+        assertEquals("Source user not equal.", loaded.getSourceUserId(),
+                taskStatus.getSourceUserId());
+    }
+
+    public void testWritingTaskWithFlex() throws Exception {
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setRequiresDeviceIdle(true)
+                .setPeriodic(5*60*60*1000, 1*60*60*1000)
+                .setRequiresCharging(true)
+                .setPersisted(true);
+        JobStatus taskStatus = new JobStatus(b.build(), SOME_UID, null, -1);
+
+        mTaskStoreUnderTest.add(taskStatus);
+        Thread.sleep(IO_WAIT);
+
+        final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.iterator().next();
+        assertEquals("Period not equal.", loaded.getJob().getIntervalMillis(),
+                taskStatus.getJob().getIntervalMillis());
+        assertEquals("Flex not equal.", loaded.getJob().getFlexMillis(),
+                taskStatus.getJob().getFlexMillis());
+    }
 
     public void testMassivePeriodClampedOnRead() throws Exception {
-        final long TEN_SECONDS = 10000L;
+        final long ONE_HOUR = 60*60*1000L; // flex
+        final long TWO_HOURS = 2 * ONE_HOUR; // period
         JobInfo.Builder b = new Builder(8, mComponent)
-                .setPeriodic(TEN_SECONDS)
+                .setPeriodic(TWO_HOURS, ONE_HOUR)
                 .setPersisted(true);
         final long invalidLateRuntimeElapsedMillis =
-                SystemClock.elapsedRealtime() + (TEN_SECONDS * 2) + 5000;  // >2P from now.
+                SystemClock.elapsedRealtime() + (TWO_HOURS * ONE_HOUR) + TWO_HOURS;  // > period+flex
         final long invalidEarlyRuntimeElapsedMillis =
-                invalidLateRuntimeElapsedMillis - TEN_SECONDS;  // Early is (late - period).
-        final JobStatus js = new JobStatus(b.build(), SOME_UID,
+                invalidLateRuntimeElapsedMillis - TWO_HOURS;  // Early is (late - period).
+        final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage", 0 /* sourceUserId */,
                 invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
 
         mTaskStoreUnderTest.add(js);
@@ -176,10 +218,10 @@
         // call SystemClock.elapsedRealtime after doing the disk i/o.
         final long newNowElapsed = SystemClock.elapsedRealtime();
         assertTrue("Early runtime wasn't correctly clamped.",
-                loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS);
-        // Assert late runtime was clamped to be now + period*2.
+                loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS);
+        // Assert late runtime was clamped to be now + period + flex.
         assertTrue("Early runtime wasn't correctly clamped.",
-                loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS * 2);
+                loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS + ONE_HOUR);
     }
 
     public void testPriorityPersisted() throws Exception {
@@ -187,7 +229,7 @@
                 .setOverrideDeadline(5000)
                 .setPriority(42)
                 .setPersisted(true);
-        final JobStatus js = new JobStatus(b.build(), SOME_UID);
+        final JobStatus js = new JobStatus(b.build(), SOME_UID, null, -1);
         mTaskStoreUnderTest.add(js);
         Thread.sleep(IO_WAIT);
         final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
@@ -203,12 +245,12 @@
         JobInfo.Builder b = new Builder(42, mComponent)
                 .setOverrideDeadline(10000)
                 .setPersisted(false);
-        JobStatus jsNonPersisted = new JobStatus(b.build(), SOME_UID);
+        JobStatus jsNonPersisted = new JobStatus(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);
+        JobStatus jsPersisted = new JobStatus(b.build(), SOME_UID, null, -1);
         mTaskStoreUnderTest.add(jsPersisted);
         Thread.sleep(IO_WAIT);
         final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();