Merge "leanback: ArrayObjectAdapter.setItems() with no DiffCallback" into oc-mr1-dev
am: 0ebd974f8f

Change-Id: Ib2e6b4e46cab80eb66497a89db34225cfd94fc3e
diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
index 8f60355..c4b3925 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -98,7 +98,8 @@
         "lifecycle" : "android.arch.lifecycle",
         "arch" : "android.arch.core",
         "paging" : "android.arch.paging",
-        "navigation" : "android.arch.navigation"]
+        "navigation" : "android.arch.navigation",
+        "background" : "android.arch.background.workmanager"]
 
 subprojects {
     repos.addMavenRepositories(project.repositories)
@@ -116,7 +117,7 @@
     def finalGroup = rootProject.flatfootProjectGroups[mavenGroup]
 
     if (finalGroup == null) {
-        return
+        throw new GradleException("bad finalGroup for $project with $project.version")
     }
     if (projectPath.size() == 2) {// root project.
         return
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index aa0db94..c6fa65d 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -96,6 +96,9 @@
 include ':room:integration-tests:kotlintestapp'
 project(':room:integration-tests:kotlintestapp').projectDir = new File(supportRoot, "room/integration-tests/kotlintestapp")
 
+include ':background:workmanager'
+project(':background:workmanager').projectDir = new File(supportRoot, "background/workmanager")
+
 /////////////////////////////
 //
 // SupportLib
diff --git a/background/workmanager/build.gradle b/background/workmanager/build.gradle
new file mode 100644
index 0000000..8d2e6db
--- /dev/null
+++ b/background/workmanager/build.gradle
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.support.LibraryVersions
+import android.support.SupportLibraryExtension
+
+apply plugin: android.support.FlatfootAndroidLibraryPlugin
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    api project(':lifecycle:extensions')
+    api project(':room:runtime')
+    annotationProcessor project(":room:compiler")
+    androidTestImplementation libs.test_runner, { exclude module: 'support-annotations' }
+    androidTestImplementation libs.espresso_core, { exclude module: 'support-annotations' }
+    androidTestImplementation libs.mockito_core, { exclude group: 'net.bytebuddy' }
+    // DexMaker has it"s own MockMaker
+    androidTestImplementation libs.dexmaker_mockito, { exclude group: 'net.bytebuddy' }
+    // DexMaker has it"s own MockMaker
+    testCompile libs.junit
+}
+
+createAndroidCheckstyle(project)
+
+version = LibraryVersions.WORKMANAGER.toString()
+
+supportLibrary {
+    name 'Android Support Library Background WorkManager'
+    publish true
+    inceptionYear '2017'
+    description "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
+    url SupportLibraryExtension.ARCHITECTURE_URL
+}
+
diff --git a/background/workmanager/proguard-rules.pro b/background/workmanager/proguard-rules.pro
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/background/workmanager/proguard-rules.pro
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/JobSchedulerConverterTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/JobSchedulerConverterTest.java
new file mode 100644
index 0000000..c1f779e
--- /dev/null
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/JobSchedulerConverterTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.job.JobInfo;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class JobSchedulerConverterTest {
+    private JobSchedulerConverter mConverter;
+
+    @Before
+    public void setUp() {
+        mConverter = new JobSchedulerConverter(InstrumentationRegistry.getTargetContext());
+    }
+
+    @Test
+    public void convert() {
+        String uuid = "026e3422-9cd1-11e7-abc4-cec278b6b50a";
+        // TODO(janclarin): Use int mapping for UUID from DB.
+        int expectedIntId = JobSchedulerConverter.generateJobId(uuid);
+        WorkSpec workSpec = new WorkSpec(uuid);
+        JobInfo jobInfo = mConverter.convert(workSpec);
+        assertEquals(expectedIntId, jobInfo.getId());
+    }
+
+    @Test
+    public void convertWithConstraints() {
+        @Constraints.NetworkType int workSpecNetworkType = Constraints.NETWORK_TYPE_UNMETERED;
+        Constraints expectedConstraints = new Constraints.Builder()
+                .setInitialDelay(12345)
+                .setRequiredNetworkType(workSpecNetworkType)
+                .setRequiresCharging(true)
+                .setRequiresDeviceIdle(true)
+                .setRequiresBatteryNotLow(true)
+                .setRequiresStorageNotLow(true)
+                .build();
+        WorkSpec workSpec = new WorkSpec("id");
+        workSpec.mConstraints = expectedConstraints;
+        JobInfo jobInfo = mConverter.convert(workSpec);
+
+        int expectedNetworkType = mConverter.convertNetworkType(workSpecNetworkType);
+        assertEquals(expectedNetworkType, jobInfo.getNetworkType());
+        assertEquals(expectedConstraints.mInitialDelay, jobInfo.getMinLatencyMillis());
+        assertEquals(expectedConstraints.mRequiresCharging, jobInfo.isRequireCharging());
+        assertEquals(expectedConstraints.mRequiresDeviceIdle, jobInfo.isRequireDeviceIdle());
+        assertEquals(expectedConstraints.mRequiresBatteryNotLow, jobInfo.isRequireBatteryNotLow());
+        assertEquals(expectedConstraints.mRequiresStorageNotLow, jobInfo.isRequireStorageNotLow());
+    }
+
+    @Test
+    public void convertNetworkTypeAny() {
+        convertNetworkTypeHelper(Constraints.NETWORK_TYPE_ANY, JobInfo.NETWORK_TYPE_NONE);
+    }
+
+    @Test
+    public void convertNetworkTypeConnected() {
+        convertNetworkTypeHelper(Constraints.NETWORK_TYPE_CONNECTED, JobInfo.NETWORK_TYPE_ANY);
+    }
+
+    @Test
+    public void convertNetworkTypeUnmetered() {
+        convertNetworkTypeHelper(
+                Constraints.NETWORK_TYPE_UNMETERED, JobInfo.NETWORK_TYPE_UNMETERED);
+    }
+
+    @Test
+    public void convertNetworkTypeNotRoaming() {
+        convertNetworkTypeHelper(
+                Constraints.NETWORK_TYPE_NOT_ROAMING, JobInfo.NETWORK_TYPE_NOT_ROAMING);
+    }
+
+    @Test
+    public void convertNetworkTypeMetered() {
+        convertNetworkTypeHelper(Constraints.NETWORK_TYPE_METERED, JobInfo.NETWORK_TYPE_METERED);
+    }
+
+    private void convertNetworkTypeHelper(@Constraints.NetworkType int constraintNetworkType,
+                                          int expectedJobInfoNetworkType) {
+        int convertedNetworkType = mConverter.convertNetworkType(constraintNetworkType);
+        assertEquals(expectedJobInfoNetworkType, convertedNetworkType);
+    }
+}
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/TestWorker.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/TestWorker.java
new file mode 100644
index 0000000..dd1f7b3
--- /dev/null
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/TestWorker.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.content.Context;
+
+/**
+ * Simple Test Worker
+ */
+
+public class TestWorker extends Worker<String> {
+    public TestWorker(Context appContext, WorkDatabase workDatabase, WorkSpec workSpec) {
+        super(appContext, workDatabase, workSpec);
+    }
+
+    @Override
+    public String doWork() {
+        return mAppContext.getPackageName();
+    }
+}
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/WorkDatabaseTests.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/WorkDatabaseTests.java
new file mode 100644
index 0000000..f857dff
--- /dev/null
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/WorkDatabaseTests.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WorkDatabaseTests {
+    private WorkDatabase mDatabase;
+    private WorkManager mWorkManager;
+
+    @Before
+    public void setUp() {
+        mDatabase = WorkDatabase.getInMemoryInstance(InstrumentationRegistry.getTargetContext());
+        mWorkManager = new WorkManager.Builder("test")
+                .build(InstrumentationRegistry.getTargetContext());
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void insert() throws InterruptedException, ExecutionException, TimeoutException {
+        final int workCount = 3;
+        final Work[] workArray = new Work[workCount];
+        for (int i = 0; i < workCount; ++i) {
+            workArray[i] = new Work.Builder(TestWorker.class).build();
+        }
+        mWorkManager.enqueue(workArray[0]).then(workArray[1]).then(workArray[2]);
+        Thread.sleep(5000);
+
+        for (int i = 0; i < workCount; ++i) {
+            String id = workArray[i].getId();
+            assertNotNull(mDatabase.workSpecDao().getWorkSpec(id));
+            assertEquals(mDatabase.dependencyDao().hasDependencies(id), (i > 0));
+        }
+    }
+
+    @Test
+    public void constraints() throws InterruptedException, ExecutionException, TimeoutException {
+        Work work0 = new Work.Builder(TestWorker.class)
+                .withConstraints(
+                        new Constraints.Builder()
+                                .setRequiresCharging(true)
+                                .setRequiresDeviceIdle(true)
+                                .setRequiredNetworkType(Constraints.NETWORK_TYPE_METERED)
+                                .setRequiresBatteryNotLow(true)
+                                .setRequiresStorageNotLow(true)
+                                .setInitialDelay(5000)
+                                .build())
+                .build();
+        Work work1 = new Work.Builder(TestWorker.class).build();
+        mWorkManager.enqueue(work0).then(work1);
+        Thread.sleep(5000);
+
+        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
+        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
+
+        assertNotNull(workSpec0.mConstraints);
+        assertTrue(workSpec0.mConstraints.mRequiresCharging);
+        assertTrue(workSpec0.mConstraints.mRequiresDeviceIdle);
+        assertTrue(workSpec0.mConstraints.mRequiresBatteryNotLow);
+        assertTrue(workSpec0.mConstraints.mRequiresStorageNotLow);
+        assertEquals(5000, workSpec0.mConstraints.mInitialDelay);
+        assertEquals(Constraints.NETWORK_TYPE_METERED, workSpec0.mConstraints.mRequiredNetworkType);
+
+        assertNotNull(workSpec1.mConstraints);
+        assertFalse(workSpec1.mConstraints.mRequiresCharging);
+        assertFalse(workSpec1.mConstraints.mRequiresDeviceIdle);
+        assertFalse(workSpec1.mConstraints.mRequiresBatteryNotLow);
+        assertFalse(workSpec1.mConstraints.mRequiresStorageNotLow);
+        assertEquals(0, workSpec1.mConstraints.mInitialDelay);
+        assertEquals(Constraints.NETWORK_TYPE_ANY, workSpec1.mConstraints.mRequiredNetworkType);
+    }
+
+    @Test
+    public void backoffPolicy() throws InterruptedException, ExecutionException, TimeoutException {
+        Work work0 = new Work.Builder(TestWorker.class)
+                .withBackoffCriteria(Work.BACKOFF_POLICY_LINEAR, 50000)
+                .build();
+        Work work1 = new Work.Builder(TestWorker.class).build();
+        mWorkManager.enqueue(work0).then(work1);
+        Thread.sleep(5000);
+
+        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
+        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
+
+        assertEquals(Work.BACKOFF_POLICY_LINEAR, workSpec0.mBackoffPolicy);
+        assertEquals(50000, workSpec0.mBackoffDelayDuration);
+
+        assertEquals(Work.BACKOFF_POLICY_EXPONENTIAL, workSpec1.mBackoffPolicy);
+        assertEquals(Work.DEFAULT_BACKOFF_DELAY_DURATION, workSpec1.mBackoffDelayDuration);
+    }
+
+    @Test
+    public void arguments() throws InterruptedException, ExecutionException, TimeoutException {
+        String key = "key";
+        String expectedValue = "value";
+
+        Arguments args = new Arguments();
+        args.putString(key, expectedValue);
+
+        Work work0 = new Work.Builder(TestWorker.class)
+                .withArguments(args)
+                .build();
+        Work work1 = new Work.Builder(TestWorker.class).build();
+        mWorkManager.enqueue(work0).then(work1);
+        Thread.sleep(5000);
+
+        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
+        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
+
+        assertNotNull(workSpec0.mArguments);
+        assertNotNull(workSpec1.mArguments);
+
+        assertEquals(1, workSpec0.mArguments.size());
+        assertEquals(0, workSpec1.mArguments.size());
+
+        String actualValue = workSpec0.mArguments.getString(key, null);
+        assertNotNull(actualValue);
+        assertEquals(expectedValue, actualValue);
+    }
+}
diff --git a/background/workmanager/src/main/AndroidManifest.xml b/background/workmanager/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a0f8c7b
--- /dev/null
+++ b/background/workmanager/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.background.workmanager">
+    <application>
+        <meta-data android:name="android.arch.background.workmanager.VERSION"
+                   android:value="${version}" />
+        <service
+            android:name=".WorkService"
+            android:permission="android.permission.BIND_JOB_SERVICE"
+            android:exported="true" />
+    </application>
+</manifest>
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/Arguments.java b/background/workmanager/src/main/java/android/arch/background/workmanager/Arguments.java
new file mode 100644
index 0000000..a47928a
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/Arguments.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.arch.persistence.room.TypeConverter;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Persistable set of Key/Value pairs which are passed to each {@link Worker}.
+ */
+
+public final class Arguments {
+    private Map<String, Object> mValues;
+
+    public Arguments() {
+        mValues = new HashMap<>();
+    }
+
+    Arguments(Map<? extends String, ?> values) {
+        mValues = new HashMap<>(values);
+    }
+
+    /**
+     * Insert boolean into arguments.
+     *
+     * @param key   String
+     * @param value boolean
+     */
+    public void putBoolean(String key, boolean value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get boolean matching key from arguments. If not found, use default value specified.
+     *
+     * @param key String
+     * @return boolean
+     */
+    public boolean getBoolean(String key, boolean defaultValue) {
+        Object value = mValues.get(key);
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Insert int into arguments.
+     *
+     * @param key   String
+     * @param value int
+     */
+    public void putInt(String key, int value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get int matching key from arguments. If not found, use default value specified.
+     *
+     * @param key String
+     * @return int
+     */
+    public int getInt(String key, int defaultValue) {
+        Object value = mValues.get(key);
+        if (value instanceof Integer) {
+            return (Integer) value;
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Insert int array into arguments.
+     *
+     * @param key   String
+     * @param value int array
+     */
+    public void putIntArray(String key, int[] value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get int array matching key from arguments. If not found, return null.
+     *
+     * @param key String
+     * @return int array
+     */
+    public int[] getIntArray(String key) {
+        Object value = mValues.get(key);
+        if (value instanceof int[]) {
+            return (int[]) value;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Insert long into arguments.
+     *
+     * @param key   String
+     * @param value long
+     */
+    public void putLong(String key, long value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get long matching key from arguments. If not found, use default value specified.
+     *
+     * @param key String
+     * @return long
+     */
+    public long getLong(String key, long defaultValue) {
+        Object value = mValues.get(key);
+        if (value instanceof Long) {
+            return (Long) value;
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Insert long array into arguments.
+     *
+     * @param key   String
+     * @param value long array
+     */
+    public void putLongArray(String key, long[] value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get long array matching key from arguments. If not found, return null.
+     *
+     * @param key String
+     * @return long array
+     */
+    public long[] getLongArray(String key) {
+        Object value = mValues.get(key);
+        if (value instanceof long[]) {
+            return (long[]) value;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Insert double into arguments.
+     *
+     * @param key   String
+     * @param value double
+     */
+    public void putDouble(String key, double value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get double matching key from arguments. If not found, use default value specified.
+     *
+     * @param key String
+     * @return long array
+     */
+    public double getDouble(String key, double defaultValue) {
+        Object value = mValues.get(key);
+        if (value instanceof Double) {
+            return (Double) value;
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Insert double array into arguments.
+     *
+     * @param key   String
+     * @param value double array
+     */
+    public void putDoubleArray(String key, double[] value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get double array matching key from arguments. If not found, return null.
+     *
+     * @param key String
+     * @return double array
+     */
+    public double[] getDoubleArray(String key) {
+        Object value = mValues.get(key);
+        if (value instanceof double[]) {
+            return (double[]) value;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Insert String into arguments.
+     *
+     * @param key   String
+     * @param value String
+     */
+    public void putString(String key, String value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get String matching key from arguments. If not found, use default value specified.
+     *
+     * @param key String
+     * @return String
+     */
+    public String getString(String key, String defaultValue) {
+        Object value = mValues.get(key);
+        if (value instanceof String) {
+            return (String) value;
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Insert String array into arguments.
+     *
+     * @param key   String
+     * @param value String array
+     */
+    public void putStringArray(String key, String[] value) {
+        mValues.put(key, value);
+    }
+
+    /**
+     * Get String array matching key from arguments. If not found, return null.
+     *
+     * @param key String
+     * @return String
+     */
+    public String[] getStringArray(String key) {
+        Object value = mValues.get(key);
+        if (value instanceof String[]) {
+            return (String[]) value;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Clears all arguments.
+     */
+    public void clear() {
+        mValues.clear();
+    }
+
+    /**
+     * Determine if key is present in arguments.
+     *
+     * @param key String
+     * @return true if key is present, false otherwise
+     */
+    public boolean containsKey(String key) {
+        return mValues.containsKey(key);
+    }
+
+    /**
+     * Determine if arguments are empty.
+     *
+     * @return true if arguments are empty, false otherwise
+     */
+    public boolean isEmpty() {
+        return mValues.isEmpty();
+    }
+
+    /**
+     * Get set of keys for arguments.
+     *
+     * @return Set<String> of keys
+     */
+    public Set<String> keySet() {
+        return mValues.keySet();
+    }
+
+    /**
+     * Removes a key/value pair from arguments.
+     *
+     * @param key String
+     */
+    public void remove(String key) {
+        mValues.remove(key);
+    }
+
+    /**
+     * Get number of arguments.
+     *
+     * @return int
+     */
+    public int size() {
+        return mValues.size();
+    }
+
+    /**
+     * Get set of key/value pairs for arguments.
+     *
+     * @return Set<Entry> of keys
+     */
+    public Set<Map.Entry<String, Object>> entrySet() {
+        return mValues.entrySet();
+    }
+
+    /**
+     * Converts {@link Arguments} to Byte Array for persistent storage.
+     *
+     * @param arguments {@link Arguments} object to convert
+     * @return byte array representation
+     */
+    @TypeConverter
+    public static byte[] toByteArray(Arguments arguments) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ObjectOutputStream objectOutputStream = null;
+        try {
+            objectOutputStream = new ObjectOutputStream(outputStream);
+            objectOutputStream.writeInt(arguments.size());
+            for (Map.Entry<String, Object> entry : arguments.entrySet()) {
+                objectOutputStream.writeUTF(entry.getKey());
+                objectOutputStream.writeObject(entry.getValue());
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (objectOutputStream != null) {
+                try {
+                    objectOutputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            try {
+                outputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return outputStream.toByteArray();
+    }
+
+    /**
+     * Converts Byte Array to {@link Arguments}.
+     *
+     * @param bytes byte array representation to convert
+     * @return {@link Arguments} object
+     */
+    @TypeConverter
+    public static Arguments fromByteArray(byte[] bytes) {
+        Map<String, Object> map = new HashMap<>();
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
+        ObjectInputStream objectInputStream = null;
+        try {
+            objectInputStream = new ObjectInputStream(inputStream);
+            for (int i = objectInputStream.readInt(); i > 0; i--) {
+                map.put(objectInputStream.readUTF(), objectInputStream.readObject());
+            }
+        } catch (IOException | ClassNotFoundException e) {
+            e.printStackTrace();
+        } finally {
+            if (objectInputStream != null) {
+                try {
+                    objectInputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            try {
+                inputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return new Arguments(map);
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/Constraints.java b/background/workmanager/src/main/java/android/arch/background/workmanager/Constraints.java
new file mode 100644
index 0000000..de181cc
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/Constraints.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * The constraints that can be applied to one {@link WorkSpec}.
+ */
+
+public class Constraints {
+    @Retention(SOURCE)
+    @IntDef({NETWORK_TYPE_CONNECTED, NETWORK_TYPE_METERED, NETWORK_TYPE_ANY,
+            NETWORK_TYPE_NOT_ROAMING, NETWORK_TYPE_UNMETERED})
+    @interface NetworkType {
+    }
+
+    // TODO(xbhatnag): Merge with JobScheduler values.
+    public static final int NETWORK_TYPE_ANY = 0;
+    public static final int NETWORK_TYPE_CONNECTED = 1;
+    public static final int NETWORK_TYPE_UNMETERED = 2;
+    public static final int NETWORK_TYPE_METERED = 3;
+    public static final int NETWORK_TYPE_NOT_ROAMING = 4;
+
+    @NetworkType
+    int mRequiredNetworkType;
+    boolean mRequiresCharging;
+    boolean mRequiresDeviceIdle;
+    boolean mRequiresBatteryNotLow;
+    boolean mRequiresStorageNotLow;
+    long mInitialDelay;
+
+    Constraints() { // stub required for room
+    }
+
+    private Constraints(Builder builder) {
+        mRequiresCharging = builder.mRequiresCharging;
+        mRequiresDeviceIdle = builder.mRequiresDeviceIdle;
+        mRequiredNetworkType = builder.mRequiredNetworkType;
+        mRequiresBatteryNotLow = builder.mRequiresBatteryNotLow;
+        mRequiresStorageNotLow = builder.mRequiresStorageNotLow;
+        mInitialDelay = builder.mInitialDelay;
+    }
+
+    /**
+     * Builder for {@link Constraints} class.
+     */
+    public static class Builder {
+        private boolean mRequiresCharging = false;
+        private boolean mRequiresDeviceIdle = false;
+        private int mRequiredNetworkType = NETWORK_TYPE_ANY;
+        private boolean mRequiresBatteryNotLow = false;
+        private boolean mRequiresStorageNotLow = false;
+        private long mInitialDelay = 0L;
+
+        /**
+         * Specify whether device should be plugged in for {@link WorkSpec} to run.
+         * Default is false.
+         *
+         * @param requiresCharging true if device must be plugged in, false otherwise
+         * @return current builder
+         */
+        public Builder setRequiresCharging(boolean requiresCharging) {
+            this.mRequiresCharging = requiresCharging;
+            return this;
+        }
+
+        /**
+         * Specify whether device should be idle for {@link WorkSpec} to run. Default is false.
+         *
+         * @param requiresDeviceIdle true if device must be idle, false otherwise
+         * @return current builder
+         */
+        public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
+            this.mRequiresDeviceIdle = requiresDeviceIdle;
+            return this;
+        }
+
+        /**
+         * Specify whether device should have a particular {@link NetworkType} for {@link WorkSpec}
+         * to run. Default is {@value #NETWORK_TYPE_ANY}
+         *
+         * @param networkType type of network required
+         * @return current builder
+         */
+        public Builder setRequiredNetworkType(@NetworkType int networkType) {
+            this.mRequiredNetworkType = networkType;
+            return this;
+        }
+
+        /**
+         * Specify whether device battery should not be below critical threshold for
+         * {@link WorkSpec} to run. Default is false.
+         *
+         * @param requiresBatteryNotLow true if battery should not be below critical threshold,
+         *                              false otherwise
+         * @return current builder
+         */
+        public Builder setRequiresBatteryNotLow(boolean requiresBatteryNotLow) {
+            this.mRequiresBatteryNotLow = requiresBatteryNotLow;
+            return this;
+        }
+
+        /**
+         * Specify whether device available storage should not be below critical threshold for
+         * {@link WorkSpec} to run. Default is false.
+         *
+         * @param requiresStorageNotLow true if available storage should not be below critical
+         *                              threshold, false otherwise
+         * @return current builder
+         */
+        public Builder setRequiresStorageNotLow(boolean requiresStorageNotLow) {
+            this.mRequiresStorageNotLow = requiresStorageNotLow;
+            return this;
+        }
+
+        /**
+         * Specify whether {@link WorkSpec} should run with an initial delay. Default is 0ms.
+         *
+         * @param duration initial delay before running WorkSpec (in milliseconds)
+         * @return current builder
+         */
+        public Builder setInitialDelay(long duration) {
+            // TODO(xbhatnag) : Does this affect rescheduled jobs?
+            this.mInitialDelay = duration;
+            return this;
+        }
+
+        /**
+         * Generates the {@link Constraints} from this Builder.
+         *
+         * @return new {@link Constraints} which can be attached to a {@link WorkSpec}
+         */
+        public Constraints build() {
+            return new Constraints(this);
+        }
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/Dependency.java b/background/workmanager/src/main/java/android/arch/background/workmanager/Dependency.java
new file mode 100644
index 0000000..bc0123b
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/Dependency.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.arch.persistence.room.Index;
+import android.arch.persistence.room.PrimaryKey;
+
+/**
+ * Database entity that defines a dependency between two {@link WorkSpec}s.
+ */
+
+// TODO(xbhatnag): Replace with single foreign key. (b/65681278)
+@Entity(foreignKeys = {
+        @ForeignKey(
+                entity = WorkSpec.class,
+                parentColumns = "id",
+                childColumns = "work_spec_id",
+                onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE),
+        @ForeignKey(
+                entity = WorkSpec.class,
+                parentColumns = "id",
+                childColumns = "prerequisite_id",
+                onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE)},
+        indices = {@Index(value = "work_spec_id"), @Index(value = "prerequisite_id")})
+class Dependency {
+    // Note: Since this is always null, SQLite will auto-increment the primary key id.
+    @ColumnInfo(name = "id")
+    @PrimaryKey
+    Integer mId;
+
+    @ColumnInfo(name = "work_spec_id")
+    String mWorkSpecId;
+
+    @ColumnInfo(name = "prerequisite_id")
+    String mPrerequisiteId;
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/DependencyDao.java b/background/workmanager/src/main/java/android/arch/background/workmanager/DependencyDao.java
new file mode 100644
index 0000000..197ac43
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/DependencyDao.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static android.arch.persistence.room.OnConflictStrategy.FAIL;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+
+/**
+ * The Data Access Object for {@link Dependency}.
+ */
+@Dao
+public interface DependencyDao {
+    /**
+     * Attempts to insert a {@link Dependency} into the database.
+     *
+     * @param dependency The {@link Dependency}s to insert
+     */
+    @Insert(onConflict = FAIL)
+    void insertDependency(Dependency dependency);
+
+    /**
+     * Determines if a {@link WorkSpec is dependent on other {@link WorkSpec}s
+     * that are not in a {@value Work#STATUS_SUCCEEDED} state.
+     *
+     * @param id The identifier for the {@link WorkSpec}
+     * @return true if the {@link WorkSpec} is dependent on other {@link WorkSpec}s
+     */
+    @Query("SELECT COUNT(id) > 0 FROM workspec WHERE status!=2 AND id IN"
+            + "(SELECT prerequisite_id FROM dependency WHERE work_spec_id=:id)")
+    boolean hasDependencies(String id); // TODO: Replace 2 with STATUS_SUCCEEDED constant
+                                     // TODO: Refactor this method to a separate DAO.
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/JobSchedulerConverter.java b/background/workmanager/src/main/java/android/arch/background/workmanager/JobSchedulerConverter.java
new file mode 100644
index 0000000..adcd57c
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/JobSchedulerConverter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+/**
+ * Converts a {@link WorkSpec} into a JobInfo.
+ */
+@RequiresApi(api = 21)
+class JobSchedulerConverter implements WorkSpecConverter<JobInfo> {
+    private static final String TAG = "JobSchedulerConverter";
+
+    private final ComponentName mWorkServiceComponent;
+
+    JobSchedulerConverter(@NonNull Context context) {
+        mWorkServiceComponent = new ComponentName(context, WorkService.class);
+    }
+
+    @Override
+    public JobInfo convert(WorkSpec workSpec) {
+        Constraints constraints = workSpec.mConstraints;
+        int jobId = generateJobId(workSpec.mId);
+        int jobNetworkType = convertNetworkType(constraints.mRequiredNetworkType);
+        JobInfo.Builder builder =
+                new JobInfo.Builder(jobId, mWorkServiceComponent)
+                        .setMinimumLatency(constraints.mInitialDelay)
+                        .setRequiredNetworkType(jobNetworkType)
+                        .setRequiresCharging(constraints.mRequiresCharging)
+                        .setRequiresDeviceIdle(constraints.mRequiresDeviceIdle);
+
+        if (Build.VERSION.SDK_INT >= 26) {
+            builder.setRequiresBatteryNotLow(constraints.mRequiresBatteryNotLow);
+            builder.setRequiresStorageNotLow(constraints.mRequiresStorageNotLow);
+        } else {
+            // TODO(janclarin): Create compat version of batteryNotLow/storageNotLow constraints.
+            Log.w(TAG, "Could not set requiresBatteryNowLow or requiresStorageNotLow constraints.");
+        }
+        return builder.build();
+    }
+
+    @Override
+    public int convertNetworkType(@Constraints.NetworkType int networkType)
+            throws IllegalArgumentException {
+        switch(networkType) {
+            case Constraints.NETWORK_TYPE_ANY:
+                return JobInfo.NETWORK_TYPE_NONE;
+            case Constraints.NETWORK_TYPE_CONNECTED:
+                return JobInfo.NETWORK_TYPE_ANY;
+            case Constraints.NETWORK_TYPE_UNMETERED:
+                return JobInfo.NETWORK_TYPE_UNMETERED;
+            case Constraints.NETWORK_TYPE_NOT_ROAMING:
+                if (Build.VERSION.SDK_INT >= 24) {
+                    return JobInfo.NETWORK_TYPE_NOT_ROAMING;
+                }
+                break;
+            case Constraints.NETWORK_TYPE_METERED:
+                if (Build.VERSION.SDK_INT >= 26) {
+                    return JobInfo.NETWORK_TYPE_METERED;
+                }
+                break;
+        }
+        throw new IllegalArgumentException("NetworkType of " + networkType + " is not supported.");
+    }
+
+    // TODO(janclarin): Store UUID mapping with incrementing integer work ID.
+    static int generateJobId(String uuid) {
+        return uuid.hashCode();
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/Scheduler.java b/background/workmanager/src/main/java/android/arch/background/workmanager/Scheduler.java
new file mode 100644
index 0000000..5d1fbd3
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/Scheduler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+/**
+ * Schedules {@link Work} depending on completion of {@link Dependency}s.
+ */
+abstract class Scheduler {
+    // TODO(janclarin): Listen for completion of works via ID and schedule jobs dependent on it.
+
+    /**
+     * @param workId ID of {@link Work} to schedule.
+     */
+    abstract void schedule(String workId);
+
+    /**
+     * @param workId ID of {@link Work} to cancel.
+     * @return boolean indicating if the work was canceled.
+     */
+    abstract boolean cancel(String workId);
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/SchedulerHelper.java b/background/workmanager/src/main/java/android/arch/background/workmanager/SchedulerHelper.java
new file mode 100644
index 0000000..1d5d7d7
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/SchedulerHelper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+/**
+ * Returns the proper {@link Scheduler} based on constraints.
+ */
+class SchedulerHelper {
+    private SchedulerHelper() {}
+
+    /**
+     * @return {@link Scheduler} based on constraints.
+     */
+    static Scheduler getScheduler() {
+        // TODO(janclarin): Determine criteria to pass in and return Scheduler.
+        return null;
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/Work.java b/background/workmanager/src/main/java/android/arch/background/workmanager/Work.java
new file mode 100644
index 0000000..32ddf3d
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/Work.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.util.UUID;
+
+/**
+ * A class to create a logical unit of work.
+ */
+
+public class Work {
+
+    @Retention(SOURCE)
+    @IntDef({STATUS_FAILED, STATUS_RUNNING, STATUS_SUCCEEDED, STATUS_ENQUEUED})
+    public @interface WorkStatus {
+    }
+
+    @Retention(SOURCE)
+    @IntDef({BACKOFF_POLICY_EXPONENTIAL, BACKOFF_POLICY_LINEAR})
+    public @interface BackoffPolicy {
+    }
+
+    public static final int STATUS_ENQUEUED = 0;
+    public static final int STATUS_RUNNING = 1;
+    public static final int STATUS_SUCCEEDED = 2;
+    public static final int STATUS_FAILED = 3;
+
+    public static final int BACKOFF_POLICY_EXPONENTIAL = 0;
+    public static final int BACKOFF_POLICY_LINEAR = 1;
+    public static final long DEFAULT_BACKOFF_DELAY_DURATION = 30000L;
+
+    private WorkSpec mWorkSpec;
+
+    private Work(WorkSpec workSpec) {
+        mWorkSpec = workSpec;
+    }
+
+    /**
+     * @return The id for this set of work.
+     */
+    public String getId() {
+        return mWorkSpec.mId;
+    }
+
+
+    WorkSpec getWorkSpec() {
+        return mWorkSpec;
+    }
+
+    /**
+     * Builder for {@link Work} class.
+     */
+    public static class Builder {
+        private WorkSpec mWorkSpec = new WorkSpec(UUID.randomUUID().toString());
+
+        public Builder(Class<? extends Worker> workerClass) {
+            mWorkSpec.mWorkerClassName = workerClass.getName();
+        }
+
+        /**
+         * Add constraints to the {@link Work}.
+         *
+         * @param constraints The constraints for the {@link Work}
+         * @return current builder
+         */
+        public Builder withConstraints(@NonNull Constraints constraints) {
+            mWorkSpec.mConstraints = constraints;
+            return this;
+        }
+
+        /**
+         * Change backoff policy and delay for the {@link Work}.
+         * Default is {@value Work#BACKOFF_POLICY_EXPONENTIAL} and 30 seconds.
+         *
+         * @param backoffPolicy Backoff Policy to use for {@link Work}
+         * @param backoffDelayDuration Time to wait before restarting {@link Worker}
+         *                             (in milliseconds)
+         * @return current builder
+         */
+        public Builder withBackoffCriteria(@BackoffPolicy int backoffPolicy,
+                                           long backoffDelayDuration) {
+            // TODO(xbhatnag): Enforce restrictions on backoff delay. 30 seconds?
+            mWorkSpec.mBackoffPolicy = backoffPolicy;
+            mWorkSpec.mBackoffDelayDuration = backoffDelayDuration;
+            return this;
+        }
+
+        /**
+         * Add arguments to the {@link Work}.
+         *
+         * @param arguments key/value pairs that will be provided to the {@link Worker} class
+         * @return current builder
+         */
+        public Builder withArguments(Arguments arguments) {
+            mWorkSpec.mArguments = arguments;
+            return this;
+        }
+
+        /**
+         * Add an optional tag to the {@link Work}.  This is particularly useful for modules or
+         * libraries who want to query for or cancel all of their own work.
+         *
+         * @param tag A tag for identifying the {@link Work} in queries.
+         */
+        public Builder withTag(String tag) {
+            mWorkSpec.mTag = tag;
+            return this;
+        }
+
+        /**
+         * Generates the {@link Work} from this builder
+         *
+         * @return new {@link Work}
+         */
+        public Work build() {
+            return new Work(mWorkSpec);
+        }
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java
new file mode 100644
index 0000000..3556b16
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.arch.background.workmanager;
+
+/**
+ * An opaque class that allows you to chain together {@link Work}.
+ */
+
+public class WorkContinuation {
+
+    WorkManager mWorkManager;
+    String mPrerequisiteId;
+
+    WorkContinuation(WorkManager workManager, String prerequisiteId) {
+        mWorkManager = workManager;
+        mPrerequisiteId = prerequisiteId;
+    }
+
+    /**
+     * Add new {@link Work} that depends on the previous one.
+     *
+     * @param work The {@link Work} to add
+     * @return A {@link WorkContinuation} that allows further chaining
+     */
+    public WorkContinuation then(Work work) {
+        return mWorkManager.enqueue(work, mPrerequisiteId);
+    }
+
+    /**
+     * Add new {@link Work} that depends on the previous one.
+     *
+     * @param workBuilder The {@link Work.Builder} to add; internally {@code build} is called on it
+     * @return A {@link WorkContinuation} that allows further chaining
+     */
+    public WorkContinuation then(Work.Builder workBuilder) {
+        return mWorkManager.enqueue(workBuilder.build(), mPrerequisiteId);
+    }
+
+    /**
+     * Add new {@link Work} that depends on the previous one.
+     *
+     * @param workerClass The {@link Worker} to enqueue; this is a convenience method that makes a
+     *                    {@link Work} object with default arguments using this Worker
+     * @return A {@link WorkContinuation} that allows further chaining
+     */
+    public WorkContinuation then(Class<? extends Worker> workerClass) {
+        return mWorkManager.enqueue(new Work.Builder(workerClass).build(), mPrerequisiteId);
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkDatabase.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkDatabase.java
new file mode 100644
index 0000000..af9dc09
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkDatabase.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+
+/**
+ * A Room database for keeping track of work statuses.
+ */
+@Database(entities = {WorkSpec.class, Dependency.class}, version = 1)
+public abstract class WorkDatabase extends RoomDatabase {
+
+    private static final String DB_NAME_PREFIX = "android.arch.background.workmanager.work.";
+
+    private static WorkDatabase sInstance;
+
+    /**
+     * Returns a static instance of the WorkDatabase.
+     *
+     * @param context A context (this method will use the application context from it)
+     * @param name The database name (will be prefixed by {@code DB_NAME_PREFIX})
+     * @return The singleton WorkDatabase for this process
+     */
+    public static WorkDatabase getInstance(Context context, String name) {
+        if (sInstance == null) {
+            sInstance = Room.databaseBuilder(
+                    context.getApplicationContext(),
+                    WorkDatabase.class,
+                    DB_NAME_PREFIX + name)
+                    .build();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Returns an in memory static instance of the WorkDatabase used for testing.
+     *
+     * @param context A context (this method will use the application context from it)
+     * @return The singleton WorkDatabase for this process
+     */
+    @VisibleForTesting
+    static WorkDatabase getInMemoryInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = Room.inMemoryDatabaseBuilder(
+                    context.getApplicationContext(),
+                    WorkDatabase.class)
+                    .build();
+        }
+        return sInstance;
+    }
+
+    /**
+     * @return The Data Access Object for {@link WorkSpec}s.
+     */
+    public abstract WorkSpecDao workSpecDao();
+
+    /**
+     * @return The Data Access Object for {@link Dependency}s.
+     */
+    public abstract DependencyDao dependencyDao();
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkExecutionManager.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkExecutionManager.java
new file mode 100644
index 0000000..2151b1d
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkExecutionManager.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static android.arch.background.workmanager.Work.STATUS_ENQUEUED;
+import static android.arch.background.workmanager.Work.STATUS_FAILED;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A class to manage the actual in-process (foreground) execution of work.
+ */
+class WorkExecutionManager {
+
+    private static final String TAG = "WorkExecMgr";
+
+    private Context mAppContext;
+    private WorkDatabase mWorkDatabase;
+    private ScheduledExecutorService mExecutor;
+
+    private Map<String, Future<?>> mFutures = new HashMap<>();
+    private final Object mLock = new Object();
+
+    WorkExecutionManager(
+            Context context,
+            WorkDatabase workDatabase,
+            ScheduledExecutorService executor) {
+        mAppContext = context.getApplicationContext();
+        mWorkDatabase = workDatabase;
+        mExecutor = executor;
+    }
+
+    void enqueue(String id, long delayMs) {
+        synchronized (mLock) {
+            InternalRunnable runnable = new InternalRunnable(id);
+            Future<?> future = mExecutor.schedule(runnable, delayMs, TimeUnit.MILLISECONDS);
+            mFutures.put(id, future);
+        }
+    }
+
+    boolean cancel(String id) {
+        synchronized (mLock) {
+            Future<?> future = mFutures.get(id);
+            if (future != null) {
+                boolean canceled = future.cancel(true);
+                mFutures.remove(id);
+                return canceled;
+            }
+        }
+        return false;
+    }
+
+    void shutdown() {
+        synchronized (mLock) {
+            for (Future future : mFutures.values()) {
+                if (future != null) {
+                    // TODO(sumir): Investigate if we should interrupt running tasks.
+                    // Also look at mExecutor.shutdown() vs. mExecutor.shutdownNow()
+                    future.cancel(true);
+                }
+            }
+            mFutures.clear();
+            mExecutor.shutdownNow();
+            mExecutor = null;
+        }
+    }
+
+    /**
+     * A callable that looks up the {@link WorkSpec} from the database for a given mId, instantiates
+     * its Worker, and then calls it.
+     */
+    private class InternalRunnable implements Runnable {
+
+        String mId;
+
+        InternalRunnable(String id) {
+            mId = id;
+        }
+
+        @Override
+        public void run() {
+            WorkSpecDao workSpecDao = mWorkDatabase.workSpecDao();
+            WorkSpec workSpec = workSpecDao.getWorkSpec(mId);
+            if (workSpec != null) {
+                int status = workSpec.mStatus;
+                if (status != STATUS_ENQUEUED) {
+                    Log.d(TAG, "Status for " + mId + " is not enqueued; not doing any work");
+                    return;
+                }
+
+                Worker worker = Worker.fromWorkSpec(mAppContext, mWorkDatabase, workSpec);
+                if (worker == null) {
+                    Log.e(TAG, "Could not create Worker " + workSpec.mWorkerClassName);
+                    workSpecDao.setWorkSpecStatus(mId, STATUS_FAILED);
+                    return;
+                }
+
+                synchronized (mLock) {
+                    if (mFutures.get(mId) == null) {
+                        Log.d(
+                                TAG,
+                                "InternalRunnable for id " + mId
+                                        + " was interrupted; not starting work");
+                        return;
+                    }
+                }
+
+                worker.call();
+                synchronized (mLock) {
+                    mFutures.remove(mId);
+                }
+            } else {
+                Log.e(TAG, "Didn't find WorkSpec for id " + mId);
+                synchronized (mLock) {
+                    mFutures.remove(mId);
+                }
+            }
+        }
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkManager.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkManager.java
new file mode 100644
index 0000000..4de587d
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkManager.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.arch.lifecycle.ProcessLifecycleOwner;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+/**
+ * WorkManager is a class used to enqueue persisted work that is guaranteed to run after its
+ * constraints are met.
+ */
+public final class WorkManager implements LifecycleObserver {
+
+    private static final String TAG = "WorkManager";
+
+    private Context mContext;
+    private String mName;
+    private ScheduledExecutorService mForegroundExecutor;
+    private ExecutorService mBackgroundExecutor;
+    private WorkDatabase mWorkDatabase;
+    private ExecutorService mEnqueueExecutor = Executors.newSingleThreadExecutor();
+    private WorkExecutionManager mForegroundWorkExecutionMgr;
+    private WorkSpecConverter<JobInfo> mWorkSpecConverter;
+
+    private WorkManager(
+            Context context,
+            String name,
+            ScheduledExecutorService foregroundExecutor,
+            ExecutorService backgroundExecutor) {
+        mContext = context.getApplicationContext();
+        mName = name;
+        mForegroundExecutor =
+                (foregroundExecutor == null)
+                        ? Executors.newScheduledThreadPool(4)   // TODO: Configure intelligently.
+                        : foregroundExecutor;
+        mBackgroundExecutor =
+                (backgroundExecutor == null)
+                        ? Executors.newSingleThreadExecutor()   // TODO: Configure intelligently.
+                        : backgroundExecutor;
+        mWorkDatabase = WorkDatabase.getInstance(mContext, mName);
+        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
+
+        // TODO(janclarin): Wrap JobScheduler logic behind another interface.
+        if (Build.VERSION.SDK_INT >= 21) {
+            mWorkSpecConverter = new JobSchedulerConverter(mContext);
+        }
+    }
+
+    /**
+     * Called when the process lifecycle is considered started.
+     */
+    @OnLifecycleEvent(Lifecycle.Event.ON_START)
+    public void onLifecycleStart() {
+        mForegroundWorkExecutionMgr = new WorkExecutionManager(
+                mContext,
+                mWorkDatabase,
+                mForegroundExecutor);
+    }
+
+    /**
+     * Called when the process lifecycle is considered stopped.
+     */
+    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+    public void onLifecycleStop() {
+        mForegroundWorkExecutionMgr.shutdown();
+        mForegroundWorkExecutionMgr = null;
+    }
+
+    /**
+     * Enqueues an item for background processing.
+     *
+     * @param work The {@link Work} to enqueue
+     * @return A {@link WorkContinuation} that allows further chaining
+     */
+    public WorkContinuation enqueue(Work work) {
+        return enqueue(work, null);
+    }
+
+    /**
+     * Enqueues an item for background processing.
+     *
+     * @param workBuilder The {@link Work.Builder} to enqueue; internally {@code build} is called
+     *                    on it
+     * @return A {@link WorkContinuation} that allows further chaining
+     */
+    public WorkContinuation enqueue(Work.Builder workBuilder) {
+        return enqueue(workBuilder.build(), null);
+    }
+
+    /**
+     * Enqueues an item for background processing.
+     *
+     * @param workerClass The {@link Worker} to enqueue; this is a convenience method that makes a
+     *                    {@link Work} object with default arguments using this Worker
+     * @return A {@link WorkContinuation} that allows further chaining
+     */
+    public WorkContinuation enqueue(Class<? extends Worker> workerClass) {
+        return enqueue(new Work.Builder(workerClass).build(), null);
+    }
+
+    WorkContinuation enqueue(Work work, String prerequisiteId) {
+        WorkContinuation workContinuation = new WorkContinuation(this, work.getId());
+        mEnqueueExecutor.execute(new EnqueueRunnable(work, prerequisiteId));
+        return workContinuation;
+    }
+
+    /**
+     * A Runnable to enqueue a {@link Work} in the database.
+     */
+    private class EnqueueRunnable implements Runnable {
+        private Work mWork;
+        private String mPrerequisiteId;
+
+        EnqueueRunnable(Work work, String prerequisiteId) {
+            mWork = work;
+            mPrerequisiteId = prerequisiteId;
+        }
+
+        @Override
+        public void run() {
+            mWorkDatabase.beginTransaction();
+            try {
+                mWorkDatabase.workSpecDao().insertWorkSpec(mWork.getWorkSpec());
+                if (mPrerequisiteId != null) {
+                    Dependency dep = new Dependency();
+                    dep.mPrerequisiteId = mPrerequisiteId;
+                    dep.mWorkSpecId = mWork.getId();
+                    mWorkDatabase.dependencyDao().insertDependency(dep);
+                } else {
+                    if (mForegroundWorkExecutionMgr != null) {
+                        mForegroundWorkExecutionMgr.enqueue(
+                                mWork.getId(),
+                                0L /* TODO: delay */);
+                        // TODO: Schedule dependent work.
+                    }
+
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        scheduleWorkWithJobScheduler();
+                        // TODO(janclarin): Schedule dependent work.
+                    }
+                }
+
+                mWorkDatabase.setTransactionSuccessful();
+            } finally {
+                mWorkDatabase.endTransaction();
+            }
+        }
+
+        @RequiresApi(api = 21)
+        private void scheduleWorkWithJobScheduler() {
+            JobScheduler jobScheduler =
+                    (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+            if (jobScheduler != null) {
+                JobInfo jobInfo = mWorkSpecConverter.convert(mWork.getWorkSpec());
+                jobScheduler.schedule(jobInfo);
+            }
+        }
+    }
+
+    /**
+     * A Builder for {@link WorkManager}.
+     */
+    public static class Builder {
+
+        private String mName;
+        private ScheduledExecutorService mForegroundExecutor;
+        private ExecutorService mBackgroundExecutor;
+
+        public Builder(String name) {
+            mName = name;
+        }
+
+        /**
+         * @param foregroundExecutor The ExecutorService to run in-process during active lifecycles
+         * @return The Builder
+         */
+        public Builder withForegroundExecutor(ScheduledExecutorService foregroundExecutor) {
+            mForegroundExecutor = foregroundExecutor;
+            return this;
+        }
+
+        /**
+         * @param backgroundExecutor The ExecutorService to run via OS-defined background execution
+         *                           such as {@link android.app.job.JobScheduler}
+         * @return The Builder
+         */
+        public Builder withBackgroundExecutor(ExecutorService backgroundExecutor) {
+            mBackgroundExecutor = backgroundExecutor;
+            return this;
+        }
+
+        /**
+         * Builds the {@link WorkManager}.
+         *
+         * @param context The context used for initialization (we will get the Application context)
+         * @return The {@link WorkManager}
+         */
+        public WorkManager build(Context context) {
+            return new WorkManager(context, mName, mForegroundExecutor, mBackgroundExecutor);
+        }
+    }
+}
+
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkService.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkService.java
new file mode 100644
index 0000000..48c5aac
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.util.Log;
+
+/**
+ * Service invoked by {@link android.app.job.JobScheduler} to run work tasks.
+ */
+@TargetApi(21)
+public class WorkService extends JobService {
+
+    private static final String TAG = "WorkService";
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        int jobId = params.getJobId();
+        Log.d(TAG, jobId + " scheduled on JobScheduler");
+        // TODO(janclarin): Schedule work with instance of WorkExecutionManager.
+        // TODO(janclarin): Call jobFinished after task is completed.
+        jobFinished(params, false);
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        int jobId = params.getJobId();
+        // TODO(janclarin): Cancel work with instance of WorkExecutionManager.
+        Log.d(TAG, jobId + " stopped");
+        return false;
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpec.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpec.java
new file mode 100644
index 0000000..17cf1f6
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpec.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.TypeConverters;
+
+/**
+ * Stores information about a logical unit of work.
+ */
+@Entity
+@TypeConverters(Arguments.class)
+public class WorkSpec {
+
+    @ColumnInfo(name = "id")
+    @PrimaryKey
+    String mId;
+
+    // TODO(xbhatnag)
+    @ColumnInfo(name = "repeat_duration")
+    long mRepeatDuration;
+
+    // TODO(xbhatnag)
+    @ColumnInfo(name = "flex_duration")
+    long mFlexDuration;
+
+    @ColumnInfo(name = "status")
+    @Work.WorkStatus
+    int mStatus = Work.STATUS_ENQUEUED;
+
+    @ColumnInfo(name = "worker_class_name")
+    String mWorkerClassName;
+
+    @Embedded
+    Constraints mConstraints = new Constraints.Builder().build();
+
+    Arguments mArguments = new Arguments();
+
+    String mTag;
+
+    // TODO(sumir): Should Backoff be disabled by default?
+    @ColumnInfo(name = "backoff_policy")
+    @Work.BackoffPolicy
+    int mBackoffPolicy = Work.BACKOFF_POLICY_EXPONENTIAL;
+
+    @ColumnInfo(name = "backoff_delay_duration")
+    long mBackoffDelayDuration = Work.DEFAULT_BACKOFF_DELAY_DURATION;
+
+    WorkSpec(String id) {
+        mId = id;
+    }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpecConverter.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpecConverter.java
new file mode 100644
index 0000000..2ed1b0c
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpecConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+/**
+ * Converts a {@link WorkSpec} into type T.
+ * @param <T> The type to convert to.
+ */
+interface WorkSpecConverter<T> {
+    /**
+     * Converts a {@link WorkSpec} into type T.
+     * @param workSpec The {@link WorkSpec} to convert to type T.
+     * @return The converted {@link WorkSpec} as type T.
+     */
+    T convert(WorkSpec workSpec);
+
+    /**
+     * Converts a {@link Constraints.NetworkType} into an appropriate int mapping.
+     * @param networkType The {@link Constraints.NetworkType} to convert to an int.
+     * @return The converted {@link Constraints.NetworkType} as an int.
+     */
+    int convertNetworkType(@Constraints.NetworkType int networkType);
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpecDao.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpecDao.java
new file mode 100644
index 0000000..ab4ec17
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkSpecDao.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static android.arch.persistence.room.OnConflictStrategy.FAIL;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.Query;
+
+/**
+ * The Data Access Object for {@link WorkSpec}s.
+ */
+@Dao
+public interface WorkSpecDao {
+
+    /**
+     * Attempts to insert a {@link WorkSpec} into the database.
+     *
+     * @param workSpec The WorkSpec to insert.
+     */
+    @Insert(onConflict = FAIL)
+    void insertWorkSpec(WorkSpec workSpec);
+
+    /**
+     * @param id The identifier
+     * @return The WorkSpec associated with that id
+     */
+    @Query("SELECT * FROM workspec WHERE id=:id")
+    WorkSpec getWorkSpec(String id);
+
+    /**
+     * Updates the status of a {@link WorkSpec}.
+     *
+     * @param id The identifier for the {@link WorkSpec}
+     * @param status The new status
+     * @return The number of rows that were updated (should be 0 or 1)
+     */
+    @Query("UPDATE workspec SET status=:status WHERE id=:id")
+    int setWorkSpecStatus(String id, int status);
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/Worker.java b/background/workmanager/src/main/java/android/arch/background/workmanager/Worker.java
new file mode 100644
index 0000000..eca0477
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/Worker.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static android.arch.background.workmanager.Work.STATUS_ENQUEUED;
+import static android.arch.background.workmanager.Work.STATUS_FAILED;
+import static android.arch.background.workmanager.Work.STATUS_RUNNING;
+import static android.arch.background.workmanager.Work.STATUS_SUCCEEDED;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.util.concurrent.Callable;
+
+/**
+ * The basic unit of work.
+ *
+ * @param <T> The payload type for this unit of work.
+ */
+public abstract class Worker<T> implements Callable<T> {
+
+    private static final String TAG = "Worker";
+
+    protected Context mAppContext;
+    private WorkDatabase mWorkDatabase;
+    private WorkSpec mWorkSpec;
+
+    public Worker(Context appContext, WorkDatabase workDatabase, WorkSpec workSpec) {
+        this.mAppContext = appContext;
+        this.mWorkDatabase = workDatabase;
+        this.mWorkSpec = workSpec;
+    }
+
+    /**
+     * Override this method to do your actual background processing.
+     *
+     * @return The result payload
+     */
+    public abstract T doWork();
+
+    @Override
+    public final T call() {
+        String id = mWorkSpec.mId;
+        Log.v(TAG, "Worker.call for " + id);
+        WorkSpecDao workSpecDao = mWorkDatabase.workSpecDao();
+        mWorkSpec.mStatus = STATUS_RUNNING;
+        workSpecDao.setWorkSpecStatus(id, STATUS_RUNNING);
+
+        T result = null;
+
+        try {
+            checkForInterruption();
+            result = doWork();
+            checkForInterruption();
+
+            Log.d(TAG, "Work succeeded for " + id);
+            mWorkSpec.mStatus = STATUS_SUCCEEDED;
+            workSpecDao.setWorkSpecStatus(id, STATUS_SUCCEEDED);
+        } catch (Exception e) {
+            // TODO: Retry policies.
+            if (e instanceof InterruptedException) {
+                Log.d(TAG, "Work interrupted for " + id);
+                mWorkSpec.mStatus = STATUS_ENQUEUED;
+                workSpecDao.setWorkSpecStatus(id, STATUS_ENQUEUED);
+            } else {
+                Log.d(TAG, "Work failed for " + id, e);
+                mWorkSpec.mStatus = STATUS_FAILED;
+                workSpecDao.setWorkSpecStatus(id, STATUS_FAILED);
+            }
+        }
+
+        return result;
+    }
+
+    static Worker fromWorkSpec(
+            final Context context,
+            final WorkDatabase workDatabase,
+            final WorkSpec workSpec) {
+        Context appContext = context.getApplicationContext();
+        String workerClassName = workSpec.mWorkerClassName;
+        try {
+            Class<?> clazz = Class.forName(workerClassName);
+            if (Worker.class.isAssignableFrom(clazz)) {
+                return (Worker) clazz
+                        .getConstructor(Context.class, WorkDatabase.class, WorkSpec.class)
+                        .newInstance(appContext, workDatabase, workSpec);
+            } else {
+                Log.e(TAG, "" + workerClassName + " is not of type Worker");
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Trouble instantiating " + workerClassName, e);
+        }
+        return null;
+    }
+
+    private void checkForInterruption() throws InterruptedException {
+        if (Thread.currentThread().isInterrupted()) {
+            throw new InterruptedException();
+        }
+    }
+}
diff --git a/background/workmanager/src/test/java/android/arch/background/workmanager/ArgumentsTest.java b/background/workmanager/src/test/java/android/arch/background/workmanager/ArgumentsTest.java
new file mode 100644
index 0000000..4e8823e
--- /dev/null
+++ b/background/workmanager/src/test/java/android/arch/background/workmanager/ArgumentsTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+@SmallTest
+public class ArgumentsTest {
+    private static final String KEY1 = "key1";
+    private static final String KEY2 = "key2";
+
+    @Test
+    public void empty() throws IOException, ClassNotFoundException {
+        Arguments args = new Arguments();
+
+        byte[] byteArray = Arguments.toByteArray(args);
+        Arguments restoredArgs = Arguments.fromByteArray(byteArray);
+
+        assertNotNull(restoredArgs);
+        assertEquals(0, restoredArgs.size());
+    }
+
+    @Test
+    public void serializeString() throws IOException, ClassNotFoundException {
+        Arguments args = new Arguments();
+        String expectedValue1 = "value1";
+        String expectedValue2 = "value2";
+        args.putString(KEY1, expectedValue1);
+        args.putString(KEY2, expectedValue2);
+
+        byte[] byteArray = Arguments.toByteArray(args);
+        Arguments restoredArgs = Arguments.fromByteArray(byteArray);
+
+        assertNotNull(restoredArgs);
+        assertEquals(2, restoredArgs.size());
+
+        String actualValue1 = restoredArgs.getString(KEY1, null);
+        assertNotNull(actualValue1);
+        assertEquals(expectedValue1, actualValue1);
+
+        String actualValue2 = restoredArgs.getString(KEY2, null);
+        assertNotNull(actualValue2);
+        assertEquals(expectedValue2, actualValue2);
+    }
+
+    @Test
+    public void serializeIntArray() throws IOException, ClassNotFoundException {
+        Arguments args = new Arguments();
+        int[] expectedValue1 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        int[] expectedValue2 = new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+        args.putIntArray(KEY1, expectedValue1);
+        args.putIntArray(KEY2, expectedValue2);
+
+        byte[] byteArray = Arguments.toByteArray(args);
+        Arguments restoredArgs = Arguments.fromByteArray(byteArray);
+
+        assertNotNull(restoredArgs);
+        assertEquals(2, restoredArgs.size());
+
+        int[] actualValue1 = restoredArgs.getIntArray(KEY1);
+        assertArrayEquals(expectedValue1, actualValue1);
+
+        int[] actualValue2 = restoredArgs.getIntArray(KEY2);
+        assertArrayEquals(expectedValue2, actualValue2);
+    }
+}
diff --git a/buildSrc/src/main/java/android/support/LibraryVersions.java b/buildSrc/src/main/java/android/support/LibraryVersions.java
index c994f6e..f1a2d79 100644
--- a/buildSrc/src/main/java/android/support/LibraryVersions.java
+++ b/buildSrc/src/main/java/android/support/LibraryVersions.java
@@ -69,4 +69,9 @@
      * Version code for shared testing code of flatfoot
      */
     public static final Version ARCH_CORE_TESTING = FLATFOOT_1_0_BATCH;
+
+    /**
+     * Version code for Background WorkManager
+     */
+    public static final Version WORKMANAGER = new Version("1.0.0-alpha1");
 }
diff --git a/v17/leanback/res/values-bn/strings.xml b/v17/leanback/res/values-bn/strings.xml
index d0e08c4..899c564 100644
--- a/v17/leanback/res/values-bn/strings.xml
+++ b/v17/leanback/res/values-bn/strings.xml
@@ -21,7 +21,7 @@
     <string name="orb_search_action" msgid="5651268540267663887">"অনুসন্ধান অ্যাকশন"</string>
     <string name="lb_search_bar_hint" msgid="8325490927970116252">"অনুসন্ধান"</string>
     <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"অনুসন্ধান করতে বলুন"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> খুঁজুন"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> অনুসন্ধান করুন"</string>
     <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> অনুসন্ধান করতে বলুন"</string>
     <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
     <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
@@ -33,11 +33,11 @@
     <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"পেছনের দিকে যান %1$dX"</string>
     <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"সরাসরি পরেরটিতে চলে যান"</string>
     <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"সরাসরি আগেরটিতে চলে যান"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"আরও অ্যাকশন"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"আরো অ্যাকশন"</string>
     <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"উপরের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন মুক্ত করুন"</string>
     <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"উপরের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচিত করুন"</string>
     <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন মুক্ত করুন"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন বেছে নিন"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"নীচের দিকে বুড়ো আঙ্গুল নির্দেশিত চিহ্ন নির্বাচন করুন"</string>
     <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"একটিরও পুনরাবৃত্তি করবেন না"</string>
     <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"সবগুলির পুনরাবৃত্তি করুন"</string>
     <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"একটির পুনরাবৃত্তি করুন"</string>
diff --git a/v17/leanback/res/values-hi/strings.xml b/v17/leanback/res/values-hi/strings.xml
index 00ef8ab..6287f14 100644
--- a/v17/leanback/res/values-hi/strings.xml
+++ b/v17/leanback/res/values-hi/strings.xml
@@ -41,11 +41,11 @@
     <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"कुछ भी न दोहराएं"</string>
     <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"सभी को दोहराएं"</string>
     <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"एक दोहराएं"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"शफ़ल करना चालू करें"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"फेर-बदल सक्षम करें"</string>
     <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"फेर-बदल अक्षम करें"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"अच्छी क्वालिटी में चलाएं"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"उच्च गुणवत्ता सक्षम करें"</string>
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करें"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"सबटाइटल चालू करें"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षक सक्षम करें"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षक अक्षम करें"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोड में चित्र डालें"</string>
     <string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
@@ -55,5 +55,5 @@
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी रखें"</string>
     <string name="lb_media_player_error" msgid="3650250994187305396">"मीडिया प्लेयर गड़बड़ी कोड %1$d कुछ और %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"शुरू करें"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"आगे बढ़ें"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"अगला"</string>
 </resources>
diff --git a/v17/leanback/res/values-pa/strings.xml b/v17/leanback/res/values-pa/strings.xml
index 57956ee..4b3c515 100644
--- a/v17/leanback/res/values-pa/strings.xml
+++ b/v17/leanback/res/values-pa/strings.xml
@@ -18,7 +18,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="lb_navigation_menu_contentDescription" msgid="6215811486591629025">"ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਮੀਨੂ"</string>
-    <string name="orb_search_action" msgid="5651268540267663887">"ਖੋਜ ਕਾਰਵਾਈ"</string>
+    <string name="orb_search_action" msgid="5651268540267663887">"ਖੋਜ ਕਿਰਿਆ"</string>
     <string name="lb_search_bar_hint" msgid="8325490927970116252">"ਖੋਜੋ"</string>
     <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"ਖੋਜਣ ਲਈ ਬੋਲੋ"</string>
     <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ਖੋਜੋ"</string>
@@ -27,12 +27,12 @@
     <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
     <string name="lb_playback_controls_play" msgid="731953341987346903">"ਪਲੇ ਕਰੋ"</string>
     <string name="lb_playback_controls_pause" msgid="6189521112079849518">"ਰੋਕੋ"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਭੇਜੋ"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX ਨੂੰ ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਭੇਜੋ"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"ਅੱਗੇ ਭੇਜੋ"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"%1$dX ਨੂੰ ਅੱਗੇ ਭੇਜੋ"</string>
     <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"ਰੀਵਾਈਂਡ"</string>
     <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"%1$dX ਨੂੰ ਰੀਵਾਈਂਡ ਕਰੋ"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ਅਗਲੇ ਨੂੰ ਛੱਡੋ"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ਪਿਛਲੇ ਨੂੰ ਛੱਡੋ"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"ਅਗਲਾ ਨੂੰ ਛੱਡੋ"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"ਪਿਛਲਾ ਨੂੰ ਛੱਡੋ"</string>
     <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"ਹੋਰ ਕਿਰਿਆਵਾਂ"</string>
     <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"ਥੰਬ ਅਪ ਨੂੰ ਅਚੋਣਵਾਂ ਕਰੋ"</string>
     <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ਥੰਬ ਅਪ ਨੂੰ ਚੁਣੋ"</string>
diff --git a/v7/appcompat/res/values-bn/strings.xml b/v7/appcompat/res/values-bn/strings.xml
index 2ea7591..5959799 100644
--- a/v7/appcompat/res/values-bn/strings.xml
+++ b/v7/appcompat/res/values-bn/strings.xml
@@ -19,11 +19,11 @@
     <string name="abc_action_mode_done" msgid="4076576682505996667">"সম্পন্ন হয়েছে"</string>
     <string name="abc_action_bar_home_description" msgid="4600421777120114993">"হোম এ নেভিগেট করুন"</string>
     <string name="abc_action_bar_up_description" msgid="1594238315039666878">"উপরের দিকে নেভিগেট করুন"</string>
-    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"আরও বিকল্প"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"আরো বিকল্প"</string>
     <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"সঙ্কুচিত করুন"</string>
-    <string name="abc_searchview_description_search" msgid="8264924765203268293">"খুঁজুন"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"অনুসন্ধান করুন"</string>
     <string name="abc_search_hint" msgid="7723749260725869598">"অনুসন্ধান..."</string>
-    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ক্যোয়ারী খুঁজুন"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ক্যোয়ারী অনুসন্ধান করুন"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ক্যোয়ারী সাফ করুন"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ক্যোয়ারী জমা দিন"</string>
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"ভয়েস অনুসন্ধান"</string>
@@ -33,5 +33,5 @@
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"এর সাথে শেয়ার করুন"</string>
     <string name="abc_capital_on" msgid="3405795526292276155">"চালু"</string>
     <string name="abc_capital_off" msgid="121134116657445385">"বন্ধ"</string>
-    <string name="search_menu_title" msgid="146198913615257606">"খুঁজুন"</string>
+    <string name="search_menu_title" msgid="146198913615257606">"অনুসন্ধান করুন"</string>
 </resources>
diff --git a/v7/appcompat/res/values-hi/strings.xml b/v7/appcompat/res/values-hi/strings.xml
index 3a393c7..0d90e55 100644
--- a/v7/appcompat/res/values-hi/strings.xml
+++ b/v7/appcompat/res/values-hi/strings.xml
@@ -16,11 +16,11 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_action_mode_done" msgid="4076576682505996667">"हो गया"</string>
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"पूर्ण"</string>
     <string name="abc_action_bar_home_description" msgid="4600421777120114993">"होम पेज पर जाएं"</string>
     <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ऊपर जाएं"</string>
     <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"ज़्यादा विकल्प"</string>
-    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"छोटा करें"</string>
+    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"संक्षिप्त करें"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"सर्च करें"</string>
     <string name="abc_search_hint" msgid="7723749260725869598">"खोजा जा रहा है…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"सर्च क्वेरी"</string>
diff --git a/v7/appcompat/res/values-pa/strings.xml b/v7/appcompat/res/values-pa/strings.xml
index 7f28ac8..bc2e6ea 100644
--- a/v7/appcompat/res/values-pa/strings.xml
+++ b/v7/appcompat/res/values-pa/strings.xml
@@ -17,13 +17,13 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="abc_action_mode_done" msgid="4076576682505996667">"ਹੋ ਗਿਆ"</string>
-    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
-    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ਉੱਪਰ ਜਾਓ"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"ਹੋਮ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ਉੱਪਰ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
     <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"ਹੋਰ ਚੋਣਾਂ"</string>
     <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"ਨਸ਼ਟ ਕਰੋ"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ਖੋਜੋ"</string>
     <string name="abc_search_hint" msgid="7723749260725869598">"ਖੋਜ…"</string>
-    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ਖੋਜ ਪੁੱਛਗਿੱਛ"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ਸਵਾਲ ਖੋਜੋ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ਸਵਾਲ ਹਟਾਓ"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ਸਵਾਲ ਪ੍ਰਸਤੁਤ ਕਰੋ"</string>
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"ਵੌਇਸ ਖੋਜ"</string>
@@ -33,5 +33,5 @@
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ਇਸ ਨਾਲ ਸਾਂਝਾ ਕਰੋ"</string>
     <string name="abc_capital_on" msgid="3405795526292276155">"ਤੇ"</string>
     <string name="abc_capital_off" msgid="121134116657445385">"ਬੰਦ"</string>
-    <string name="search_menu_title" msgid="146198913615257606">"ਖੋਜੋ"</string>
+    <string name="search_menu_title" msgid="146198913615257606">"ਖੋਜ"</string>
 </resources>
diff --git a/v7/appcompat/res/values-ta/strings.xml b/v7/appcompat/res/values-ta/strings.xml
index 4a2ad2f..7daeaaf 100644
--- a/v7/appcompat/res/values-ta/strings.xml
+++ b/v7/appcompat/res/values-ta/strings.xml
@@ -31,7 +31,7 @@
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"எல்லாம் காட்டு"</string>
     <string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> மூலம் பகிர்"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"இதனுடன் பகிர்"</string>
-    <string name="abc_capital_on" msgid="3405795526292276155">"ஆன்"</string>
-    <string name="abc_capital_off" msgid="121134116657445385">"ஆஃப்"</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"இயக்கு"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"முடக்கு"</string>
     <string name="search_menu_title" msgid="146198913615257606">"தேடு"</string>
 </resources>
diff --git a/v7/mediarouter/res/values-hi/strings.xml b/v7/mediarouter/res/values-hi/strings.xml
index f9ac43b..adf3e88 100644
--- a/v7/mediarouter/res/values-hi/strings.xml
+++ b/v7/mediarouter/res/values-hi/strings.xml
@@ -30,8 +30,8 @@
     <string name="mr_controller_play" msgid="683634565969987458">"चलाएं"</string>
     <string name="mr_controller_pause" msgid="5451884435510905406">"रोकें"</string>
     <string name="mr_controller_stop" msgid="735874641921425123">"बंद करें"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तार करें"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"छोटा करें"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तृत करें"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"संक्षिप्त करें"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"एल्बम आर्ट"</string>
     <string name="mr_controller_volume_slider" msgid="2361785992211841709">"वॉल्यूम स्लाइडर"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"कोई मीडिया चयनित नहीं है"</string>
diff --git a/v7/mediarouter/res/values-ta/strings.xml b/v7/mediarouter/res/values-ta/strings.xml
index 99c6172..59dac88 100644
--- a/v7/mediarouter/res/values-ta/strings.xml
+++ b/v7/mediarouter/res/values-ta/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"சிஸ்டம்"</string>
+    <string name="mr_system_route_name" msgid="5441529851481176817">"அமைப்பு"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"சாதனங்கள்"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"திரையிடு பட்டன்"</string>
     <string name="mr_cast_button_disconnected" msgid="816305490427819240">"அனுப்புதல் பொத்தான். துண்டிக்கப்பட்டது"</string>