diff --git a/.gitignore b/.gitignore index 3133b75a..0c77a124 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ /app/google-services.json /app/build/ /app/src/main/res/values/secrets.xml -/app/fastaccess-key -/jobdispatcher/build/ +/app/fastaccess-key \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index e964c639..0b373ce2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,8 +29,8 @@ android { applicationId "com.fastaccess.github" minSdkVersion 21 targetSdkVersion 26 - versionCode 430 - versionName "4.3.0" + versionCode 440 + versionName "4.4.0" buildConfigString "GITHUB_CLIENT_ID", (buildProperties.secrets['github_client_id'] | buildProperties.notThere['github_client_id']).string buildConfigString "GITHUB_SECRET", (buildProperties.secrets['github_secret'] | buildProperties.notThere['github_secret']).string buildConfigString "IMGUR_CLIENT_ID", (buildProperties.secrets['imgur_client_id'] | buildProperties.notThere['imgur_client_id']).string @@ -167,7 +167,7 @@ dependencies { implementation 'com.jaredrummler:android-device-names:1.1.4' implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:2.1.0' implementation 'com.airbnb.android:lottie:2.2.0' - implementation project(path: ':jobdispatcher') + implementation 'com.firebase:firebase-jobdispatcher:0.8.2' compileOnly "org.projectlombok:lombok:${lombokVersion}" kapt "org.projectlombok:lombok:${lombokVersion}" kapt "com.evernote:android-state-processor:${state_version}" diff --git a/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java b/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java index 28f5641a..6e5d01de 100644 --- a/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java +++ b/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java @@ -4,6 +4,7 @@ import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; import android.text.SpannableStringBuilder; +import com.fastaccess.helper.Logger; import com.fastaccess.ui.widgets.SpannableBuilder; import net.nightwhistler.htmlspanner.TagNodeHandler; @@ -42,10 +43,11 @@ import lombok.NoArgsConstructor; return node.getParent() == null ? null : node.getParent().getName(); } - @Override public void beforeChildren(TagNode node, SpannableStringBuilder builder) { + @Override public void beforeChildren(TagNode node, SpannableStringBuilder builder) { TodoItems todoItem = null; if (node.getChildTags() != null && node.getChildTags().length > 0) { for (TagNode tagNode : node.getChildTags()) { + Logger.e(tagNode.getName(), tagNode.getText()); if (tagNode.getName() != null && tagNode.getName().equals("input")) { todoItem = new TodoItems(); todoItem.isChecked = tagNode.getAttributeByName("checked") != null; diff --git a/app/src/main/java/com/fastaccess/provider/timeline/handler/MarginHandler.java b/app/src/main/java/com/fastaccess/provider/timeline/handler/MarginHandler.java index f5435a08..3fec4aa4 100644 --- a/app/src/main/java/com/fastaccess/provider/timeline/handler/MarginHandler.java +++ b/app/src/main/java/com/fastaccess/provider/timeline/handler/MarginHandler.java @@ -20,10 +20,8 @@ public class MarginHandler extends TagNodeHandler { } } - public void handleTagNode(TagNode node, SpannableStringBuilder builder, int start, int end) { builder.setSpan(new LeadingMarginSpan.Standard(30), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); this.appendNewLine(builder); - this.appendNewLine(builder); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt index 86cee4f2..9801b314 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt @@ -16,7 +16,6 @@ import com.fastaccess.BuildConfig import com.fastaccess.R import com.fastaccess.helper.AppHelper import com.fastaccess.helper.InputHelper -import com.fastaccess.helper.PrefGetter import com.fastaccess.helper.ViewHelper import com.fastaccess.provider.fabric.FabricProvider import com.fastaccess.ui.base.BaseActivity @@ -76,7 +75,7 @@ class PremiumActivity : BaseActivity(), Premi return true } - @OnClick(R.id.close) fun onClose(): Unit = finish() + @OnClick(R.id.close) fun onClose() = finish() override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) @@ -97,8 +96,6 @@ class PremiumActivity : BaseActivity(), Premi override fun onAnimationRepeat(p0: Animator?) {} override fun onAnimationEnd(p0: Animator?) { FabricProvider.logPurchase(InputHelper.toString(editText)) - PrefGetter.setProItems() - PrefGetter.setEnterpriseItem() showMessage(R.string.success, R.string.success) successResult() } diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt index ab1cb8f8..ac73d355 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt @@ -1,6 +1,6 @@ package com.fastaccess.ui.modules.main.premium -import com.fastaccess.helper.Logger +import com.fastaccess.helper.PrefGetter import com.fastaccess.helper.RxHelper import com.fastaccess.ui.base.mvp.presenter.BasePresenter import com.github.b3er.rxfirebase.database.data @@ -26,13 +26,20 @@ class PremiumPresenter : BasePresenter(), PremiumMvp.Presenter val map = it.getValue(gti) exists = map?.contains(promo) } - Logger.e(it.children, it.childrenCount, exists) return@flatMap Observable.just(exists) } .doOnComplete { sendToView { it.hideProgress() } } .subscribe({ when (it) { - true -> sendToView { it.onSuccessfullyActivated() } + true -> sendToView { + if (promo.contains("student")) { + PrefGetter.setProItems() + } else { + PrefGetter.setProItems() + PrefGetter.setEnterpriseItem() + } + it.onSuccessfullyActivated() + } else -> sendToView { it.onNoMatch() } } }, ::println)) diff --git a/app/src/main/res/raw/changelog.html b/app/src/main/res/raw/changelog.html index 5bf6dd1f..14695aae 100644 --- a/app/src/main/res/raw/changelog.html +++ b/app/src/main/res/raw/changelog.html @@ -8,28 +8,22 @@

FastHub changelog

-

Version 4.3.0 (Project Columns and Cards) +

Version 4.4.0 (Org Project Columns and Cards)

-

Reporting Issues or Feature Requests in Google Play review section, will be ignored or might even get your account to be blocked from - FastHub. You are using an app for GitHub, which provides a proper way to report issues. -
- Please report the issues in FastHub repo instead, by opening the Drawer Menu and clicking on “Report an Issue” - PLEASE USE IT. +

Please report the issues in FastHub repo instead, by opening the Drawer Menu and clicking on “Report an Issue” + PLEASE USE IT.

-

Bugs , Enhancements & new Features (4.3.0) +

Bugs , Enhancements & new Features (4.4.0)

    -
  • Project Columns & Cards (Edit, Create & Delete)
  • -
  • (New) Repo Collaborators now can Edit, delete & create files.
  • -
  • (New) Repo Collaborators now can Edit, delete & Comments.
  • -
  • (New) Long press in Feeds to navigate directly to Repo.
  • -
  • (Enhancement) Made Search to have minimum 2 chars.
  • -
  • (Enhancement) Adding blockable progress when adding new comment.
  • -
  • (Fix) Crash in PRs & Issues comments due to Table rendering!.
  • -
  • (Fix) ReadMe scrolling when Device Animation is turned off.
  • -
  • (Fix) Closed/Opened Issue pagination where most of the issues in the page are PRs (GitHub API!!!!).
  • +
  • (New) Org Project Columns & Cards (Edit, Create & Delete)
  • +
  • (New) Displaying Labels under Issue/Pr description.
  • +
  • (Enhancement) Removal of PR review limit.
  • +
  • (Enhancement) Selected text will be taken in consideration when adding Image/Link.
  • +
  • (Enhancement) Removed Loading background.
  • +
  • (Enhancement) More markdown enhancement.
  • (Fix) Lots of bug fixes.
  • There are more stuff are not mentioned, find them out :stuck_out_tongue:
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 012dd54c..1a5c453f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -592,6 +592,19 @@
• I\'m having this issue or I want this & that!!

Head to https://github.com/k0shk0sh/FastHub/issues/new and create new issue for bugs or feature requests, I really do encourage you to search before opening a ticket. Any duplicate request will result in it being closed immediately.

+ +
• How do I get PROMO CODE?
+

If you are a student, you\'ll have to provide me via Email that you are student, you will need below documents:

+
    +
  • Your university identity card & your identity card (that shows your name & your face to compare it!)
  • +
  • Your university start & end date
  • +
  • Rate FastHub in the Play Store
  • +
+

If you aren\'t a student and you can\'t afford to pay for PRO, you\'ll need:

+
    +
  • Write an article about FastHub in social media such as (Medium)
  • +
  • Rate FastHub in the Play Store
  • +
]]> FAQ Comments added successfully diff --git a/jobdispatcher/build.gradle b/jobdispatcher/build.gradle deleted file mode 100644 index d326ddf2..00000000 --- a/jobdispatcher/build.gradle +++ /dev/null @@ -1,79 +0,0 @@ -apply plugin: "com.android.library" - -android { - compileSdkVersion 26 - buildToolsVersion "26.0.1" - - defaultConfig { - minSdkVersion 16 - targetSdkVersion 26 - versionCode 1 - versionName "0.8.0" - testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' - } - - defaultPublishConfig "release" - publishNonDefault true - - buildTypes { - debug { - testCoverageEnabled true - } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt') - } - } - - sourceSets { - // A set of testing helpers that are shared across test types - testLib { java.srcDir("src/main") } - test { java.srcDir("src/testLib") } // Robolectric tests - androidTest { java.srcDir("src/testLib") } // Android (e2e) tests - } -} - -dependencies { - // The main library only depends on the Android support lib - compile "com.android.support:support-v4:26.0.1" - - def junit = 'junit:junit:4.12' - def robolectric = 'org.robolectric:robolectric:3.3.2' - - // The common test library uses JUnit - testLibCompile junit - - // The unit tests are written using JUnit, Robolectric, and Mockito - testCompile junit - testCompile robolectric - testCompile 'org.mockito:mockito-core:2.2.5' - - // The Android (e2e) tests are written using JUnit and the test support lib - androidTestCompile junit - androidTestCompile 'com.android.support.test:runner:0.5' -} - -task javadocs(type: Javadoc) { - description "Generate Javadocs" - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - classpath += configurations.compile - failOnError false -} - -task javadocsJar(type: Jar, dependsOn: javadocs) { - description "Package Javadocs into a jar" - classifier = "javadoc" - from javadocs.destinationDir -} - -task sourcesJar(type: Jar) { - description "Package sources into a jar" - classifier = "sources" - from android.sourceSets.main.java.sourceFiles -} - -task aar(dependsOn: "assembleRelease") { - group "artifact" - description "Builds the library AARs" -} diff --git a/jobdispatcher/coverage.gradle b/jobdispatcher/coverage.gradle deleted file mode 100644 index 204f2dc2..00000000 --- a/jobdispatcher/coverage.gradle +++ /dev/null @@ -1,40 +0,0 @@ -apply plugin: "jacoco" - -jacoco { - // see https://github.com/jacoco/jacoco/pull/288 and the top build.gradle - toolVersion "0.7.6.201602180812" -} - -android { - testOptions { - unitTests.all { - systemProperty "robolectric.logging.enabled", true - systemProperty "robolectric.logging", "stdout" - - jacoco { - includeNoLocationClasses = true - } - } - } -} - -// ignore these when generating coverage -def ignoredPrefixes = ['R$', 'R.class', 'BuildConfig.class'] - -task coverage(type: JacocoReport, dependsOn: ["testDebugUnitTest"]) { - group = "Reports" - description = "Generate a coverage report" - - classDirectories = fileTree( - dir: "${project.buildDir}/intermediates/classes/debug/com/firebase/", - exclude: { d -> ignoredPrefixes.any { p -> d.file.name.startsWith(p) } } - ) - sourceDirectories = files(["src/main/java/com/firebase/"]) - executionData = files("${project.buildDir}/jacoco/testDebugUnitTest.exec") - - reports { - xml.enabled = true - html.enabled = true - html.destination "${buildDir}/coverage_html" - } -} diff --git a/jobdispatcher/src/androidTest/AndroidManifest.xml b/jobdispatcher/src/androidTest/AndroidManifest.xml deleted file mode 100644 index c87f27fc..00000000 --- a/jobdispatcher/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/jobdispatcher/src/androidTest/java/com/firebase/jobdispatcher/EndToEndTest.java b/jobdispatcher/src/androidTest/java/com/firebase/jobdispatcher/EndToEndTest.java deleted file mode 100644 index 48b1048b..00000000 --- a/jobdispatcher/src/androidTest/java/com/firebase/jobdispatcher/EndToEndTest.java +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Basic end to end test for the JobDispatcher. Requires Google Play services be installed and - * available. - */ -@RunWith(AndroidJUnit4.class) -public final class EndToEndTest { - private Context appContext; - private FirebaseJobDispatcher dispatcher; - - @Before public void setUp() { - appContext = InstrumentationRegistry.getTargetContext(); - dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(appContext)); - TestJobService.reset(); - } - - @Test public void basicImmediateJob() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - TestJobService.setProxy(new TestJobService.JobServiceProxy() { - @Override - public boolean onStartJob(JobParameters params) { - latch.countDown(); - return false; - } - - @Override - public boolean onStopJob(JobParameters params) { - return false; - } - }); - - dispatcher.mustSchedule( - dispatcher.newJobBuilder() - .setService(TestJobService.class) - .setTrigger(Trigger.NOW) - .setTag("basic-immediate-job") - .build()); - - assertTrue("Latch wasn't counted down as expected", latch.await(120, TimeUnit.SECONDS)); - } -} diff --git a/jobdispatcher/src/main/AndroidManifest.xml b/jobdispatcher/src/main/AndroidManifest.xml deleted file mode 100644 index 71205bd8..00000000 --- a/jobdispatcher/src/main/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/BundleProtocol.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/BundleProtocol.java deleted file mode 100644 index 774a8340..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/BundleProtocol.java +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -final class BundleProtocol { - static final String PACKED_PARAM_BUNDLE_PREFIX = "com.firebase.jobdispatcher."; - - // PACKED_PARAM values are only read on the client side, so as long as the - // extraction process gets the same changes then it's fine. - static final String PACKED_PARAM_CONSTRAINTS = "constraints"; - static final String PACKED_PARAM_LIFETIME = "persistent"; - static final String PACKED_PARAM_RECURRING = "recurring"; - static final String PACKED_PARAM_SERVICE = "service"; - static final String PACKED_PARAM_TAG = "tag"; - static final String PACKED_PARAM_EXTRAS = "extras"; - static final String PACKED_PARAM_TRIGGER_TYPE = "trigger_type"; - static final String PACKED_PARAM_TRIGGER_WINDOW_END = "window_end"; - static final String PACKED_PARAM_TRIGGER_WINDOW_START = "window_start"; - static final int TRIGGER_TYPE_EXECUTION_WINDOW = 1; - static final int TRIGGER_TYPE_IMMEDIATE = 2; - static final int TRIGGER_TYPE_CONTENT_URI = 3; - static final String PACKED_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS = - "initial_backoff_seconds"; - static final String PACKED_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS = - "maximum_backoff_seconds"; - static final String PACKED_PARAM_RETRY_STRATEGY_POLICY = "retry_policy"; - static final String PACKED_PARAM_REPLACE_CURRENT = "replace_current"; - static final String PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY = "content_uri_flags_array"; - static final String PACKED_PARAM_CONTENT_URI_ARRAY = "content_uri_array"; - static final String PACKED_PARAM_TRIGGERED_URIS = "triggered_uris"; - static final String PACKED_PARAM_OBSERVED_URI = "observed_uris"; - - BundleProtocol() { - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Constraint.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Constraint.java deleted file mode 100644 index ff413fb2..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Constraint.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.IntDef; -import android.support.annotation.VisibleForTesting; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * A Constraint is a runtime requirement for a job. A job only becomes eligible to run once its - * trigger has been activated and all constraints are satisfied. - */ -public final class Constraint { - /** - * Only run the job when an unmetered network is available. - */ - public static final int ON_UNMETERED_NETWORK = 1; - - /** - * Only run the job when a network connection is available. If both this and - * {@link #ON_UNMETERED_NETWORK} is provided, {@link #ON_UNMETERED_NETWORK} will take - * precedence. - */ - public static final int ON_ANY_NETWORK = 1 << 1; - - /** - * Only run the job when the device is currently charging. - */ - public static final int DEVICE_CHARGING = 1 << 2; - - /** - * Only run the job when the device is idle. This is ignored for devices that don't expose the - * concept of an idle state. - */ - public static final int DEVICE_IDLE = 1 << 3; - - @VisibleForTesting - static final int[] ALL_CONSTRAINTS = { - ON_ANY_NETWORK, ON_UNMETERED_NETWORK, DEVICE_CHARGING, DEVICE_IDLE}; - - /** Constraint shouldn't ever be instantiated. */ - private Constraint() {} - - /** - * A tooling type-hint for any of the valid constraint values. - */ - @IntDef(flag = true, value = { - ON_ANY_NETWORK, - ON_UNMETERED_NETWORK, - DEVICE_CHARGING, - DEVICE_IDLE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface JobConstraint {} - - /** - * Compact a provided array of constraints into a single int. - * - * @see #uncompact(int) - */ - static int compact(@JobConstraint int[] constraints) { - int result = 0; - if (constraints == null) { - return result; - } - for (int c : constraints) { - result |= c; - } - return result; - } - - /** - * Unpack a single int into an array of constraints. - * - * @see #compact(int[]) - */ - static int[] uncompact(int compactConstraints) { - int length = 0; - for (int c : ALL_CONSTRAINTS) { - length += (compactConstraints & c) == c ? 1 : 0; - } - int[] list = new int[length]; - - int i = 0; - for (int c : ALL_CONSTRAINTS) { - if ((compactConstraints & c) == c) { - list[i++] = c; - } - } - - return list; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/DefaultJobValidator.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/DefaultJobValidator.java deleted file mode 100644 index 4804804e..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/DefaultJobValidator.java +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.RetryStrategy.RETRY_POLICY_EXPONENTIAL; -import static com.firebase.jobdispatcher.RetryStrategy.RETRY_POLICY_LINEAR; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Bundle; -import android.os.Parcel; -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -/** - * Validates Jobs according to some safe standards. - *

- * Custom JobValidators should typically extend from this. - */ -public class DefaultJobValidator implements JobValidator { - - /** - * The maximum length of a tag, in characters (i.e. String.length()). Strings longer than this - * will cause validation to fail. - */ - public static final int MAX_TAG_LENGTH = 100; - - /** - * The maximum size, in bytes, that the provided extras bundle can be. Corresponds to - * {@link Parcel#dataSize()}. - */ - public final static int MAX_EXTRAS_SIZE_BYTES = 10 * 1024; - - /** Private ref to the Context. Necessary to check that the manifest is configured correctly. */ - private final Context context; - - public DefaultJobValidator(Context context) { - this.context = context; - } - - /** @see {@link #MAX_EXTRAS_SIZE_BYTES}. */ - private static int measureBundleSize(Bundle extras) { - Parcel p = Parcel.obtain(); - extras.writeToParcel(p, 0); - int sizeInBytes = p.dataSize(); - p.recycle(); - - return sizeInBytes; - } - - /** Combines two {@literal Lists} together. */ - @Nullable - private static List mergeErrorLists(@Nullable List errors, - @Nullable List newErrors) { - if (errors == null) { - return newErrors; - } - if (newErrors == null) { - return errors; - } - - errors.addAll(newErrors); - return errors; - } - - @Nullable - private static List addError(@Nullable List errors, String newError) { - if (newError == null) { - return errors; - } - if (errors == null) { - return getMutableSingletonList(newError); - } - - Collections.addAll(errors, newError); - - return errors; - } - - @Nullable - private static List addErrorsIf(boolean condition, List errors, String newErr) { - if (condition) { - return addError(errors, newErr); - } - - return errors; - } - - /** - * Attempts to validate the provided {@code JobParameters}. If the JobParameters is valid, null will be - * returned. If the JobParameters has errors, a list of those errors will be returned. - */ - @Nullable - @Override - @CallSuper - public List validate(JobParameters job) { - List errors = null; - - errors = mergeErrorLists(errors, validate(job.getTrigger())); - errors = mergeErrorLists(errors, validate(job.getRetryStrategy())); - - if (job.isRecurring() && job.getTrigger() == Trigger.NOW) { - errors = addError(errors, "ImmediateTriggers can't be used with recurring jobs"); - } - - errors = mergeErrorLists(errors, validateForTransport(job.getExtras())); - if (job.getLifetime() > Lifetime.UNTIL_NEXT_BOOT) { - //noinspection ConstantConditions - errors = mergeErrorLists(errors, validateForPersistence(job.getExtras())); - } - - errors = mergeErrorLists(errors, validateTag(job.getTag())); - errors = mergeErrorLists(errors, validateService(job.getService())); - - return errors; - } - - /** - * Attempts to validate the provided Trigger. If valid, null is returned. Otherwise a list of - * errors will be returned. - *

- * Note that a Trigger that passes validation here is not necessarily valid in all permutations - * of a JobParameters. For example, an Immediate is never valid for a recurring job. - * @param trigger - */ - @Nullable - @Override - @CallSuper - public List validate(JobTrigger trigger) { - if (trigger != Trigger.NOW - && !(trigger instanceof JobTrigger.ExecutionWindowTrigger) - && !(trigger instanceof JobTrigger.ContentUriTrigger)) { - return getMutableSingletonList("Unknown trigger provided"); - } - - return null; - } - - /** - * Attempts to validate the provided RetryStrategy. If valid, null is returned. Otherwise a list - * of errors will be returned. - */ - @Nullable - @Override - @CallSuper - public List validate(RetryStrategy retryStrategy) { - List errors = null; - - int policy = retryStrategy.getPolicy(); - int initial = retryStrategy.getInitialBackoff(); - int maximum = retryStrategy.getMaximumBackoff(); - - errors = addErrorsIf(policy != RETRY_POLICY_EXPONENTIAL && policy != RETRY_POLICY_LINEAR, - errors, "Unknown retry policy provided"); - errors = addErrorsIf(maximum < initial, - errors, "Maximum backoff must be greater than or equal to initial backoff"); - errors = addErrorsIf(300 > maximum, - errors, "Maximum backoff must be greater than 300s (5 minutes)"); - errors = addErrorsIf(initial < 30, - errors, "Initial backoff must be at least 30s"); - - return errors; - } - - @Nullable - private List validateForPersistence(Bundle extras) { - List errors = null; - - if (extras != null) { - // check the types to make sure they're persistable - for (String k : extras.keySet()) { - errors = addError(errors, validateExtrasType(extras, k)); - } - } - - return errors; - } - - @Nullable - private List validateForTransport(Bundle extras) { - if (extras == null) { - return null; - } - - int bundleSizeInBytes = measureBundleSize(extras); - if (bundleSizeInBytes > MAX_EXTRAS_SIZE_BYTES) { - return getMutableSingletonList(String.format(Locale.US, - "Extras too large: %d bytes is > the max (%d bytes)", - bundleSizeInBytes, MAX_EXTRAS_SIZE_BYTES)); - } - - return null; - } - - @Nullable - private String validateExtrasType(Bundle extras, String key) { - Object o = extras.get(key); - - if (o == null - || o instanceof Integer - || o instanceof Long - || o instanceof Double - || o instanceof String - || o instanceof Boolean) { - return null; - } - - return String.format(Locale.US, - "Received value of type '%s' for key '%s', but only the" - + " following extra parameter types are supported:" - + " Integer, Long, Double, String, and Boolean", - o == null ? null : o.getClass(), key); - } - - private List validateService(String service) { - if (service == null || service.isEmpty()) { - return getMutableSingletonList("Service can't be empty"); - } - - if (context == null) { - return getMutableSingletonList("Context is null, can't query PackageManager"); - } - - PackageManager pm = context.getPackageManager(); - if (pm == null) { - return getMutableSingletonList("PackageManager is null, can't validate service"); - } - - final String msg = "Couldn't find a registered service with the name " + service - + ". Is it declared in the manifest with the right intent-filter?"; - - Intent executeIntent = new Intent(JobService.ACTION_EXECUTE); - executeIntent.setClassName(context, service); - List intentServices = pm.queryIntentServices(executeIntent, 0); - if (intentServices == null || intentServices.isEmpty()) { - return getMutableSingletonList(msg); - } - - for (ResolveInfo info : intentServices) { - if (info.serviceInfo != null && info.serviceInfo.enabled) { - // found a match! - return null; - } - } - - return getMutableSingletonList(msg); - } - - private List validateTag(String tag) { - if (tag == null) { - return getMutableSingletonList("Tag can't be null"); - } - - if (tag.length() > MAX_TAG_LENGTH) { - return getMutableSingletonList("Tag must be shorter than " + MAX_TAG_LENGTH); - } - - return null; - } - - @NonNull - private static List getMutableSingletonList(String msg) { - ArrayList strings = new ArrayList<>(); - strings.add(msg); - return strings; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Driver.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Driver.java deleted file mode 100644 index fe832721..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Driver.java +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.FirebaseJobDispatcher.CancelResult; -import com.firebase.jobdispatcher.FirebaseJobDispatcher.ScheduleResult; - -/** - * Driver represents a component that understands how to schedule, validate, and execute jobs. - */ -public interface Driver { - - /** - * Schedules the provided Job. - * - * @return one of the SCHEDULE_RESULT_ constants - */ - @ScheduleResult - int schedule(@NonNull Job job); - - /** - * Cancels the job with the provided tag and class. - * - * @return one of the CANCEL_RESULT_ constants. - */ - @CancelResult - int cancel(@NonNull String tag); - - /** - * Cancels all jobs registered with this Driver. - * - * @return one of the CANCEL_RESULT_ constants. - */ - @CancelResult - int cancelAll(); - - /** - * Returns a JobValidator configured for this backend. - */ - @NonNull - JobValidator getValidator(); - - /** - * Indicates whether the backend is available. - */ - boolean isAvailable(); -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ExecutionDelegator.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ExecutionDelegator.java deleted file mode 100644 index 1dbacb3c..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ExecutionDelegator.java +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static android.content.Context.BIND_AUTO_CREATE; - -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; -import android.support.v4.util.SimpleArrayMap; -import android.util.Log; -import com.firebase.jobdispatcher.JobService.JobResult; -import java.lang.ref.WeakReference; - -/** - * ExecutionDelegator tracks local Binder connections to client JobServices and handles - * communication with those services. - */ -/* package */ class ExecutionDelegator { - @VisibleForTesting - static final int JOB_FINISHED = 1; - - static final String TAG = "FJD.ExternalReceiver"; - - interface JobFinishedCallback { - void onJobFinished(@NonNull JobInvocation jobInvocation, @JobResult int result); - } - - /** - * A mapping of {@link JobInvocation} to (local) binder connections. - * Synchronized by itself. - */ - private final SimpleArrayMap serviceConnections = - new SimpleArrayMap<>(); - private final ResponseHandler responseHandler = - new ResponseHandler(Looper.getMainLooper(), new WeakReference<>(this)); - private final Context context; - private final JobFinishedCallback jobFinishedCallback; - - ExecutionDelegator(Context context, JobFinishedCallback jobFinishedCallback) { - this.context = context; - this.jobFinishedCallback = jobFinishedCallback; - } - - /** - * Executes the provided {@code jobInvocation} by kicking off the creation of a new Binder - * connection to the Service. - * - * @return true if the service was bound successfully. - */ - boolean executeJob(JobInvocation jobInvocation) { - if (jobInvocation == null) { - return false; - } - - JobServiceConnection conn = new JobServiceConnection(jobInvocation, - responseHandler.obtainMessage(JOB_FINISHED)); - - synchronized (serviceConnections) { - JobServiceConnection oldConnection = serviceConnections.put(jobInvocation, conn); - if (oldConnection != null) { - Log.e(TAG, "Received execution request for already running job"); - } - return context.bindService(createBindIntent(jobInvocation), conn, BIND_AUTO_CREATE); - } - } - - @NonNull - private Intent createBindIntent(JobParameters jobParameters) { - Intent execReq = new Intent(JobService.ACTION_EXECUTE); - execReq.setClassName(context, jobParameters.getService()); - return execReq; - } - - void stopJob(JobInvocation job) { - synchronized (serviceConnections) { - JobServiceConnection jobServiceConnection = serviceConnections.remove(job); - if (jobServiceConnection != null) { - jobServiceConnection.onStop(); - safeUnbindService(jobServiceConnection); - } - } - } - - private void safeUnbindService(JobServiceConnection connection) { - if (connection != null && connection.isBound()) { - try { - context.unbindService(connection); - } catch (IllegalArgumentException e) { - Log.w(TAG, "Error unbinding service: " + e.getMessage()); - } - } - } - - private void onJobFinishedMessage(JobInvocation jobInvocation, int result) { - synchronized (serviceConnections) { - JobServiceConnection connection = serviceConnections.remove(jobInvocation); - safeUnbindService(connection); - } - - jobFinishedCallback.onJobFinished(jobInvocation, result); - } - - private static class ResponseHandler extends Handler { - - /** - * We hold a WeakReference to the ExecutionDelegator because it holds a reference to a - * Service Context and Handlers are often kept in memory longer than you'd expect because - * any pending Messages can maintain references to them. - */ - private final WeakReference executionDelegatorReference; - - ResponseHandler(Looper looper, WeakReference executionDelegator) { - super(looper); - this.executionDelegatorReference = executionDelegator; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case JOB_FINISHED: - if (msg.obj instanceof JobInvocation) { - ExecutionDelegator delegator = this.executionDelegatorReference.get(); - if (delegator == null) { - Log.wtf(TAG, "handleMessage: service was unexpectedly GC'd" - + ", can't send job result"); - return; - } - - delegator.onJobFinishedMessage((JobInvocation) msg.obj, msg.arg1); - return; - } - - Log.wtf(TAG, "handleMessage: unknown obj returned"); - return; - - default: - Log.wtf(TAG, "handleMessage: unknown message type received: " + msg.what); - } - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/FirebaseJobDispatcher.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/FirebaseJobDispatcher.java deleted file mode 100644 index 1a76093f..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/FirebaseJobDispatcher.java +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.RetryStrategy.RetryPolicy; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * The FirebaseJobDispatcher provides a driver-agnostic API for scheduling and cancelling Jobs. - * - * @see #FirebaseJobDispatcher(Driver) - * @see Driver - * @see JobParameters - */ -public final class FirebaseJobDispatcher { - /** - * Indicates the schedule request seems to have been successful. - */ - public final static int SCHEDULE_RESULT_SUCCESS = 0; - - /** - * Indicates the schedule request encountered an unknown error. - */ - public final static int SCHEDULE_RESULT_UNKNOWN_ERROR = 1; - - /** - * Indicates the schedule request failed because the driver was unavailable. - */ - public final static int SCHEDULE_RESULT_NO_DRIVER_AVAILABLE = 2; - - /** - * Indicates the schedule request failed because the Trigger was unsupported. - */ - public final static int SCHEDULE_RESULT_UNSUPPORTED_TRIGGER = 3; - - /** - * Indicates the schedule request failed because the service is not exposed or configured - * correctly. - */ - public final static int SCHEDULE_RESULT_BAD_SERVICE = 4; - - /** - * Indicates the cancel request seems to have been successful. - */ - public final static int CANCEL_RESULT_SUCCESS = 0; - /** - * Indicates the cancel request encountered an unknown error. - */ - public final static int CANCEL_RESULT_UNKNOWN_ERROR = 1; - /** - * Indicates the cancel request failed because the driver was unavailable. - */ - public final static int CANCEL_RESULT_NO_DRIVER_AVAILABLE = 2; - /** - * The backing Driver for this instance. - */ - private final Driver mDriver; - /** - * The ValidationEnforcer configured for the current Driver. - */ - private final ValidationEnforcer mValidator; - /** - * Single instance of a RetryStrategy.Builder, configured with the current driver's validation - * settings. We can do this because the RetryStrategy.Builder is stateless. - */ - private RetryStrategy.Builder mRetryStrategyBuilder; - - /** - * Instantiates a new FirebaseJobDispatcher using the provided Driver. - */ - public FirebaseJobDispatcher(Driver driver) { - mDriver = driver; - mValidator = new ValidationEnforcer(mDriver.getValidator()); - mRetryStrategyBuilder = new RetryStrategy.Builder(mValidator); - } - - /** - * Attempts to schedule the provided Job. - *

- * Returns one of the SCHEDULE_RESULT_ constants. - */ - @ScheduleResult - public int schedule(@NonNull Job job) { - if (!mDriver.isAvailable()) { - return SCHEDULE_RESULT_NO_DRIVER_AVAILABLE; - } - - return mDriver.schedule(job); - } - - /** - * Attempts to cancel the Job that matches the provided tag and endpoint. - *

- * Returns one of the CANCEL_RESULT_ constants. - */ - @CancelResult - public int cancel(@NonNull String tag) { - if (!mDriver.isAvailable()) { - return CANCEL_RESULT_NO_DRIVER_AVAILABLE; - } - - return mDriver.cancel(tag); - } - - /** - * Attempts to cancel all Jobs registered for this package. - *

- * Returns one of the CANCEL_RESULT_ constants. - */ - @CancelResult - public int cancelAll() { - if (!mDriver.isAvailable()) { - return CANCEL_RESULT_NO_DRIVER_AVAILABLE; - } - - return mDriver.cancelAll(); - } - - /** - * Attempts to schedule the provided Job, throwing an exception if it fails. - * - * @throws ScheduleFailedException - */ - public void mustSchedule(Job job) { - if (schedule(job) != SCHEDULE_RESULT_SUCCESS) { - throw new ScheduleFailedException(); - } - } - - /** - * Returns a ValidationEnforcer configured for the current Driver. - */ - public ValidationEnforcer getValidator() { - return mValidator; - } - - /** - * Creates a new Job.Builder, configured with the current driver's validation settings. - */ - @NonNull - public Job.Builder newJobBuilder() { - return new Job.Builder(mValidator); - } - - /** - * Creates a new RetryStrategy from the provided parameters, validated with the current driver's - * {@link JobValidator}. - * - * @param policy the backoff policy to use. One of the {@link RetryPolicy} constants. - * @param initialBackoff the initial backoff, in seconds. - * @param maximumBackoff the maximum backoff, in seconds. - * @throws ValidationEnforcer.ValidationException - * @see RetryStrategy - */ - public RetryStrategy newRetryStrategy(@RetryPolicy int policy, int initialBackoff, - int maximumBackoff) { - - return mRetryStrategyBuilder.build(policy, initialBackoff, maximumBackoff); - } - - /** - * Results that can legally be returned from {@link #schedule(Job)} calls. - */ - @IntDef({ - SCHEDULE_RESULT_SUCCESS, - SCHEDULE_RESULT_UNKNOWN_ERROR, - SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, - SCHEDULE_RESULT_UNSUPPORTED_TRIGGER, - SCHEDULE_RESULT_BAD_SERVICE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ScheduleResult { - } - - /** - * Results that can legally be returned from {@link #cancel(String)} or {@link #cancelAll()} - * calls. - */ - @IntDef({ - CANCEL_RESULT_SUCCESS, - CANCEL_RESULT_UNKNOWN_ERROR, - CANCEL_RESULT_NO_DRIVER_AVAILABLE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CancelResult { - } - - /** - * Thrown when a {@link FirebaseJobDispatcher#schedule(com.firebase.jobdispatcher.Job)} call - * fails. - */ - public final static class ScheduleFailedException extends RuntimeException { - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractor.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractor.java deleted file mode 100644 index 03fca073..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractor.java +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.Nullable; -import android.util.Log; -import android.util.Pair; -import java.util.ArrayList; - -/** - * Responsible for extracting a JobCallback from a given Bundle. - * - *

Google Play services will send the Binder packed inside a simple strong Binder wrapper ({@link - * #PENDING_CALLBACK_CLASS}) under the key {@link #BUNDLE_KEY_CALLBACK "callback"}. - */ -/* package */ final class GooglePlayCallbackExtractor { - - private static final String TAG = GooglePlayReceiver.TAG; - private static final String ERROR_NULL_CALLBACK = "No callback received, terminating"; - private static final String ERROR_INVALID_CALLBACK = "Bad callback received, terminating"; - - /** The Parcelable class that wraps the Binder we need to access. */ - private static final String PENDING_CALLBACK_CLASS = - "com.google.android.gms.gcm.PendingCallback"; - /** The key for the wrapped Binder. */ - private static final String BUNDLE_KEY_CALLBACK = "callback"; - /** A magic number that indicates the following bytes belong to a Bundle. */ - private static final int BUNDLE_MAGIC = 0x4C444E42; - /** A magic number that indicates the following value is a Parcelable. */ - private static final int VAL_PARCELABLE = 4; - - // GuardedBy("GooglePlayCallbackExtractor.class") - private static Boolean shouldReadKeysAsStringsCached = null; - - public Pair extractCallback(@Nullable Bundle data) { - if (data == null) { - Log.e(TAG, ERROR_NULL_CALLBACK); - return null; - } - - return extractWrappedBinderFromParcel(data); - } - - /** - * Bundles are written out in the following format: - * A header, which consists of: - *

    - *
  1. length (int)
  2. - *
  3. magic number ({@link #BUNDLE_MAGIC}) (int)
  4. - *
  5. number of entries (int)
  6. - *
- *

- * Then the map values, each of which looks like this: - *

    - *
  1. string key
  2. - *
  3. int type marker
  4. - *
  5. (any) parceled value
  6. - *
- *

- * We're just going to iterate over the map looking for the right key (BUNDLE_KEY_CALLBACK) - * and try and read the IBinder straight from the parcelled data. This is entirely dependent - * on the implementation of Parcel, but these specific parts of Parcel / Bundle haven't - * changed since 2008 and newer versions of Android will ship with newer versions of Google - * Play services which embed the IBinder directly into the Bundle (no need to deal with the - * Parcelable issues). - */ - @Nullable - @SuppressLint("ParcelClassLoader") - private Pair extractWrappedBinderFromParcel(Bundle data) { - Bundle cleanBundle = new Bundle(); - Parcel serialized = toParcel(data); - JobCallback callback = null; - - try { - int length = serialized.readInt(); - if (length <= 0) { - // Empty Bundle - Log.w(TAG, ERROR_NULL_CALLBACK); - return null; - } - - int magic = serialized.readInt(); - if (magic != BUNDLE_MAGIC) { - // Not a Bundle - Log.w(TAG, ERROR_NULL_CALLBACK); - return null; - } - - int numEntries = serialized.readInt(); - for (int i = 0; i < numEntries; i++) { - String entryKey = readKey(serialized); - if (entryKey == null) { - continue; - } - - if (!(callback == null && BUNDLE_KEY_CALLBACK.equals(entryKey))) { - // If it's not the 'callback' key, we can just read it using the standard - // mechanisms because we're not afraid of rogue BadParcelableExceptions. - Object value = serialized.readValue(null /* class loader */); - if (value instanceof String) { - cleanBundle.putString(entryKey, (String) value); - } else if (value instanceof Boolean) { - cleanBundle.putBoolean(entryKey, (boolean) value); - } else if (value instanceof Integer) { - cleanBundle.putInt(entryKey, (int) value); - } else if (value instanceof ArrayList) { - // The only acceptable ArrayList in a Bundle is one that consists entirely - // of Parcelables, so this cast is safe. - @SuppressWarnings("unchecked") // safe by specification - ArrayList arrayList = (ArrayList) value; - cleanBundle.putParcelableArrayList(entryKey, arrayList); - } else if (value instanceof Bundle) { - cleanBundle.putBundle(entryKey, (Bundle) value); - } else if (value instanceof Parcelable) { - cleanBundle.putParcelable(entryKey, (Parcelable) value); - } - - // Move to the next key - continue; - } - - int typeTag = serialized.readInt(); - if (typeTag != VAL_PARCELABLE) { - // If the key is correct ("callback"), but it's not a Parcelable then something - // went wrong and we should bail. - Log.w(TAG, ERROR_INVALID_CALLBACK); - return null; - } - - String clsname = serialized.readString(); - if (!PENDING_CALLBACK_CLASS.equals(clsname)) { - // If it's a Parcelable, but not one we recognize then we should not try and - // unpack it. - Log.w(TAG, ERROR_INVALID_CALLBACK); - return null; - } - - // Instead of trying to instantiate clsname, we'll just read its single member. - IBinder remote = serialized.readStrongBinder(); - callback = new GooglePlayJobCallback(remote); - } - - if (callback == null) { - Log.w(TAG, ERROR_NULL_CALLBACK); - return null; - } - return Pair.create(callback, cleanBundle); - } finally { - serialized.recycle(); - } - } - - private static Parcel toParcel(Bundle data) { - Parcel serialized = Parcel.obtain(); - data.writeToParcel(serialized, 0); - serialized.setDataPosition(0); - return serialized; - } - - /** - * Reads the next key (String) from the provided {@code serialized} Parcel. - * - *

Naively using {@link Parcel#readString()} fails on versions of Android older than L, - * whereas {@link Parcel#readValue(ClassLoader)} works on older versions but fails on anything L - * or newer. - */ - private String readKey(Parcel serialized) { - if (shouldReadKeysAsStrings()) { - return serialized.readString(); - } - - // Older platforms require readValue - Object entryKeyObj = serialized.readValue(null /* Use the system ClassLoader */); - if (!(entryKeyObj instanceof String)) { - // Should never happen (Bundle keys are always Strings) - Log.w(TAG, ERROR_INVALID_CALLBACK); - return null; - } - - return (String) entryKeyObj; - } - - /** - * Checks whether {@link Parcel#readString()} or {@link Parcel#readValue()} should be used to - * access Bundle keys from a serialized Parcel. Commit {@link - * https://android.googlesource.com/platform/frameworks/base/+/9c3e74f - * I57bda9eb79ceaaa9c1b94ad49d9e462b52102149} (which only officially landed in Lollipop) changed - * from using writeValue to writeString for Bundle keys. Some OEMs have pulled this change into - * their KitKat fork, so we can't trust the SDK version check. Instead, we'll write a dummy - * Bundle to a Parcel and figure it out using that. - * - * The check is cached because the result can't change during runtime. - */ - private static synchronized boolean shouldReadKeysAsStrings() { - // We're pretty sure that readString() should always be used on L+, but if we shortcircuit - // this check then we have no evidence that this code is functioning correctly on KitKat - // devices that have the corresponding writeString() change. - if (shouldReadKeysAsStringsCached == null) { - final String expectedKey = "key"; - Bundle testBundle = new Bundle(); - testBundle.putString(expectedKey, "value"); - Parcel testParcel = toParcel(testBundle); - try { - // length - checkCondition(testParcel.readInt() > 0); - // magic - checkCondition(testParcel.readInt() == BUNDLE_MAGIC); - // num entries - checkCondition(testParcel.readInt() == 1); - - shouldReadKeysAsStringsCached = expectedKey.equals(testParcel.readString()); - } catch (RuntimeException e) { - shouldReadKeysAsStringsCached = Boolean.FALSE; - } finally { - testParcel.recycle(); - } - } - - return shouldReadKeysAsStringsCached; - } - - /** Throws an {@code IllegalStateException} if {@code condition} is false. */ - private static void checkCondition(boolean condition) { - if (!condition) { - throw new IllegalStateException(); - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayDriver.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayDriver.java deleted file mode 100644 index 58314821..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayDriver.java +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.support.annotation.NonNull; - -import com.firebase.jobdispatcher.FirebaseJobDispatcher.ScheduleResult; - -/** - * GooglePlayDriver provides an implementation of Driver for devices with Google Play - * services installed. This backend does not do any availability checks and any uses should be - * guarded with a call to {@code GoogleApiAvailability#isGooglePlayServicesAvailable(android.content.Context)} - * - * @see - * GoogleApiAvailability - */ -public final class GooglePlayDriver implements Driver { - static final String BACKEND_PACKAGE = "com.google.android.gms"; - private final static String ACTION_SCHEDULE = "com.google.android.gms.gcm.ACTION_SCHEDULE"; - - private final static String BUNDLE_PARAM_SCHEDULER_ACTION = "scheduler_action"; - private final static String BUNDLE_PARAM_TAG = "tag"; - private final static String BUNDLE_PARAM_TOKEN = "app"; - private final static String BUNDLE_PARAM_COMPONENT = "component"; - - private final static String SCHEDULER_ACTION_SCHEDULE_TASK = "SCHEDULE_TASK"; - private final static String SCHEDULER_ACTION_CANCEL_TASK = "CANCEL_TASK"; - private final static String SCHEDULER_ACTION_CANCEL_ALL = "CANCEL_ALL"; - private static final String INTENT_PARAM_SOURCE = "source"; - private static final String INTENT_PARAM_SOURCE_VERSION = "source_version"; - - private static final int JOB_DISPATCHER_SOURCE_CODE = 1 << 3; - private static final int JOB_DISPATCHER_SOURCE_VERSION_CODE = 1; - - private final JobValidator mValidator; - /** - * The application Context. Used to send broadcasts. - */ - private final Context mContext; - /** - * A PendingIntent from this package. Passed inside the broadcast so the receiver can verify the - * sender's package. - */ - private final PendingIntent mToken; - /** - * Turns Jobs into Bundles. - */ - private final GooglePlayJobWriter mWriter; - - /** - * Instantiates a new GooglePlayDriver. - */ - public GooglePlayDriver(Context context) { - mContext = context; - mToken = PendingIntent.getBroadcast(context, 0, new Intent(), 0); - mWriter = new GooglePlayJobWriter(); - mValidator = new DefaultJobValidator(context); - } - - @Override - public boolean isAvailable() { - ApplicationInfo applicationInfo = null; - try { - applicationInfo = mContext.getPackageManager().getApplicationInfo(BACKEND_PACKAGE, 0); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - return applicationInfo != null && applicationInfo.enabled; - } - - - /** - * Schedules the provided Job. - */ - @Override - @ScheduleResult - public int schedule(@NonNull Job job) { - mContext.sendBroadcast(createScheduleRequest(job)); - - return FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS; - } - - @Override - public int cancel(@NonNull String tag) { - mContext.sendBroadcast(createCancelRequest(tag)); - - return FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS; - } - - @Override - public int cancelAll() { - mContext.sendBroadcast(createBatchCancelRequest()); - - return FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS; - } - - @NonNull - protected Intent createCancelRequest(@NonNull String tag) { - Intent cancelReq = createSchedulerIntent(SCHEDULER_ACTION_CANCEL_TASK); - cancelReq.putExtra(BUNDLE_PARAM_TAG, tag); - cancelReq.putExtra(BUNDLE_PARAM_COMPONENT, new ComponentName(mContext, getReceiverClass())); - return cancelReq; - } - - @NonNull - protected Intent createBatchCancelRequest() { - Intent cancelReq = createSchedulerIntent(SCHEDULER_ACTION_CANCEL_ALL); - cancelReq.putExtra(BUNDLE_PARAM_COMPONENT, new ComponentName(mContext, getReceiverClass())); - return cancelReq; - } - - @NonNull - protected Class getReceiverClass() { - return GooglePlayReceiver.class; - } - - @NonNull - @Override - public JobValidator getValidator() { - return mValidator; - } - - @NonNull - private Intent createScheduleRequest(JobParameters job) { - Intent scheduleReq = createSchedulerIntent(SCHEDULER_ACTION_SCHEDULE_TASK); - scheduleReq.putExtras(mWriter.writeToBundle(job, scheduleReq.getExtras())); - return scheduleReq; - } - - @NonNull - private Intent createSchedulerIntent(String schedulerAction) { - Intent scheduleReq = new Intent(ACTION_SCHEDULE); - - scheduleReq.setPackage(BACKEND_PACKAGE); - scheduleReq.putExtra(BUNDLE_PARAM_SCHEDULER_ACTION, schedulerAction); - scheduleReq.putExtra(BUNDLE_PARAM_TOKEN, mToken); - scheduleReq.putExtra(INTENT_PARAM_SOURCE, JOB_DISPATCHER_SOURCE_CODE); - scheduleReq.putExtra(INTENT_PARAM_SOURCE_VERSION, JOB_DISPATCHER_SOURCE_VERSION_CODE); - - return scheduleReq; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobCallback.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobCallback.java deleted file mode 100644 index 7f47fe59..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobCallback.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.os.IBinder; -import android.os.Parcel; -import android.os.RemoteException; - -/** - * Wraps the GooglePlay-specific callback class in a JobCallback-compatible interface. - */ -/* package */ final class GooglePlayJobCallback implements JobCallback { - - private static final String DESCRIPTOR = "com.google.android.gms.gcm.INetworkTaskCallback"; - /** The only supported transaction ID. */ - private static final int TRANSACTION_TASK_FINISHED = IBinder.FIRST_CALL_TRANSACTION + 1; - - private final IBinder mRemote; - - public GooglePlayJobCallback(IBinder binder) { - mRemote = binder; - } - - @Override - public void jobFinished(@JobService.JobResult int status) { - Parcel request = Parcel.obtain(); - Parcel response = Parcel.obtain(); - try { - request.writeInterfaceToken(DESCRIPTOR); - request.writeInt(status); - - mRemote.transact(TRANSACTION_TASK_FINISHED, request, response, 0); - - response.readException(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } finally { - request.recycle(); - response.recycle(); - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobWriter.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobWriter.java deleted file mode 100644 index 0389f90f..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobWriter.java +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.IntDef; -import android.support.annotation.VisibleForTesting; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import com.firebase.jobdispatcher.RetryStrategy.RetryPolicy; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/* package */ final class GooglePlayJobWriter { - - static final String REQUEST_PARAM_UPDATE_CURRENT = "update_current"; - static final String REQUEST_PARAM_EXTRAS = "extras"; - static final String REQUEST_PARAM_PERSISTED = "persisted"; - static final String REQUEST_PARAM_REQUIRED_NETWORK = "requiredNetwork"; - static final String REQUEST_PARAM_REQUIRES_CHARGING = "requiresCharging"; - static final String REQUEST_PARAM_REQUIRES_IDLE = "requiresIdle"; - static final String REQUEST_PARAM_RETRY_STRATEGY = "retryStrategy"; - static final String REQUEST_PARAM_SERVICE = "service"; - static final String REQUEST_PARAM_TAG = "tag"; - - static final String REQUEST_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS = - "initial_backoff_seconds"; - static final String REQUEST_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS = - "maximum_backoff_seconds"; - static final String REQUEST_PARAM_RETRY_STRATEGY_POLICY = "retry_policy"; - - static final String REQUEST_PARAM_TRIGGER_TYPE = "trigger_type"; - static final String REQUEST_PARAM_TRIGGER_WINDOW_END = "window_end"; - static final String REQUEST_PARAM_TRIGGER_WINDOW_FLEX = "period_flex"; - static final String REQUEST_PARAM_TRIGGER_WINDOW_PERIOD = "period"; - static final String REQUEST_PARAM_TRIGGER_WINDOW_START = "window_start"; - - @VisibleForTesting - /* package */ static final int LEGACY_RETRY_POLICY_EXPONENTIAL = 0; - @VisibleForTesting - /* package */ static final int LEGACY_RETRY_POLICY_LINEAR = 1; - @VisibleForTesting - /* package */ final static int LEGACY_NETWORK_UNMETERED = 1; - @VisibleForTesting - /* package */ final static int LEGACY_NETWORK_CONNECTED = 0; - @VisibleForTesting - /* package */ final static int LEGACY_NETWORK_ANY = 2; - - private JobCoder jobCoder = new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, false); - - private static void writeExecutionWindowTriggerToBundle(JobParameters job, Bundle b, - JobTrigger.ExecutionWindowTrigger trigger) { - - b.putInt(REQUEST_PARAM_TRIGGER_TYPE, BundleProtocol.TRIGGER_TYPE_EXECUTION_WINDOW); - - if (job.isRecurring()) { - b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_PERIOD, - trigger.getWindowEnd()); - b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_FLEX, - trigger.getWindowEnd() - trigger.getWindowStart()); - } else { - b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_START, - trigger.getWindowStart()); - b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_END, - trigger.getWindowEnd()); - } - } - - private static void writeImmediateTriggerToBundle(Bundle b) { - b.putInt(REQUEST_PARAM_TRIGGER_TYPE, BundleProtocol.TRIGGER_TYPE_IMMEDIATE); - b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_START, 0); - b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_END, 30); - } - - private void writeContentUriTriggerToBundle(Bundle data, ContentUriTrigger uriTrigger) { - data.putInt(BundleProtocol.PACKED_PARAM_TRIGGER_TYPE, - BundleProtocol.TRIGGER_TYPE_CONTENT_URI); - - int size = uriTrigger.getUris().size(); - int[] flagsArray = new int[size]; - Uri[] uriArray = new Uri[size]; - for (int i = 0; i < size; i++) { - ObservedUri uri = uriTrigger.getUris().get(i); - flagsArray[i] = uri.getFlags(); - uriArray[i] = uri.getUri(); - } - data.putIntArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY, flagsArray); - data.putParcelableArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_ARRAY, uriArray); - } - - public Bundle writeToBundle(JobParameters job, Bundle b) { - b.putString(REQUEST_PARAM_TAG, job.getTag()); - b.putBoolean(REQUEST_PARAM_UPDATE_CURRENT, job.shouldReplaceCurrent()); - - boolean persisted = job.getLifetime() == Lifetime.FOREVER; - b.putBoolean(REQUEST_PARAM_PERSISTED, persisted); - b.putString(REQUEST_PARAM_SERVICE, GooglePlayReceiver.class.getName()); - - writeTriggerToBundle(job, b); - writeConstraintsToBundle(job, b); - writeRetryStrategyToBundle(job, b); - - // Embed the job spec (minus extras) into the extras (under a prefix) - Bundle extras = job.getExtras(); - if (extras == null) { - extras = new Bundle(); - } - b.putBundle(REQUEST_PARAM_EXTRAS, jobCoder.encode(job, extras)); - - return b; - } - - private void writeRetryStrategyToBundle(JobParameters job, Bundle b) { - RetryStrategy strategy = job.getRetryStrategy(); - - Bundle rb = new Bundle(); - rb.putInt(REQUEST_PARAM_RETRY_STRATEGY_POLICY, - convertRetryPolicyToLegacyVersion(strategy.getPolicy())); - rb.putInt(REQUEST_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS, - strategy.getInitialBackoff()); - rb.putInt(REQUEST_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS, - strategy.getMaximumBackoff()); - - b.putBundle(REQUEST_PARAM_RETRY_STRATEGY, rb); - } - - private int convertRetryPolicyToLegacyVersion(@RetryPolicy int policy) { - switch (policy) { - case RetryStrategy.RETRY_POLICY_LINEAR: - return LEGACY_RETRY_POLICY_LINEAR; - - case RetryStrategy.RETRY_POLICY_EXPONENTIAL: - // fallthrough - default: - return LEGACY_RETRY_POLICY_EXPONENTIAL; - } - } - - private void writeTriggerToBundle(JobParameters job, Bundle b) { - final JobTrigger trigger = job.getTrigger(); - - if (trigger == Trigger.NOW) { - writeImmediateTriggerToBundle(b); - } else if (trigger instanceof JobTrigger.ExecutionWindowTrigger) { - writeExecutionWindowTriggerToBundle(job, b, (JobTrigger.ExecutionWindowTrigger) trigger); - } else if (trigger instanceof JobTrigger.ContentUriTrigger) { - writeContentUriTriggerToBundle(b, (JobTrigger.ContentUriTrigger) trigger); - } else { - throw new IllegalArgumentException("Unknown trigger: " + trigger.getClass()); - } - } - - private void writeConstraintsToBundle(JobParameters job, Bundle b) { - int c = Constraint.compact(job.getConstraints()); - - b.putBoolean(REQUEST_PARAM_REQUIRES_CHARGING, - (c & Constraint.DEVICE_CHARGING) == Constraint.DEVICE_CHARGING); - b.putBoolean(REQUEST_PARAM_REQUIRES_IDLE, - (c & Constraint.DEVICE_IDLE) == Constraint.DEVICE_IDLE); - b.putInt(REQUEST_PARAM_REQUIRED_NETWORK, convertConstraintsToLegacyNetConstant(c)); - } - - /** - * Converts a bitmap of Constraint values into a LegacyNetworkConstraint constant (int). - */ - @LegacyNetworkConstant - private int convertConstraintsToLegacyNetConstant(int constraintMap) { - int reqNet = LEGACY_NETWORK_ANY; - - reqNet = (constraintMap & Constraint.ON_ANY_NETWORK) == Constraint.ON_ANY_NETWORK - ? LEGACY_NETWORK_CONNECTED - : reqNet; - - reqNet = (constraintMap & Constraint.ON_UNMETERED_NETWORK) == Constraint.ON_UNMETERED_NETWORK - ? LEGACY_NETWORK_UNMETERED - : reqNet; - - return reqNet; - } - - @IntDef({LEGACY_NETWORK_ANY, LEGACY_NETWORK_CONNECTED, LEGACY_NETWORK_UNMETERED}) - @Retention(RetentionPolicy.SOURCE) - private @interface LegacyNetworkConstant {} -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessageHandler.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessageHandler.java deleted file mode 100644 index 664805f6..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessageHandler.java +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG; -import static com.firebase.jobdispatcher.GooglePlayReceiver.TAG; - -import android.annotation.TargetApi; -import android.app.AppOpsManager; -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.util.Log; -import com.firebase.jobdispatcher.JobInvocation.Builder; - -/** - * A messenger for communication with GCM Network Scheduler. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -class GooglePlayMessageHandler extends Handler { - - static final int MSG_START_EXEC = 1; - static final int MSG_STOP_EXEC = 2; - static final int MSG_RESULT = 3; - private static final int MSG_INIT = 4; - private final GooglePlayReceiver googlePlayReceiver; - - public GooglePlayMessageHandler(Looper mainLooper, GooglePlayReceiver googlePlayReceiver) { - super(mainLooper); - this.googlePlayReceiver = googlePlayReceiver; - } - - @Override - public void handleMessage(Message message) { - if (message == null) { - return; - } - - AppOpsManager appOpsManager = (AppOpsManager) googlePlayReceiver.getApplicationContext() - .getSystemService(Context.APP_OPS_SERVICE); - try { - appOpsManager.checkPackage(message.sendingUid, GooglePlayDriver.BACKEND_PACKAGE); - } catch (SecurityException e) { - Log.e(TAG, "Message was not sent from GCM."); - return; - } - - switch (message.what) { - case MSG_START_EXEC: - handleStartMessage(message); - break; - - case MSG_STOP_EXEC: - handleStopMessage(message); - break; - - case MSG_INIT: - // Not implemented. - break; - - default: - Log.e(TAG, "Unrecognized message received: " + message); - break; - } - } - - private void handleStartMessage(Message message) { - final Bundle data = message.getData(); - - final Messenger replyTo = message.replyTo; - String tag = data.getString(REQUEST_PARAM_TAG); - if (replyTo == null || tag == null) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Invalid start execution message."); - } - return; - } - - GooglePlayMessengerCallback messengerCallback = - new GooglePlayMessengerCallback(replyTo, tag); - JobInvocation jobInvocation = googlePlayReceiver.prepareJob(messengerCallback, data); - googlePlayReceiver.getExecutionDelegator().executeJob(jobInvocation); - } - - private void handleStopMessage(Message message) { - Builder builder = GooglePlayReceiver.getJobCoder().decode(message.getData()); - if (builder == null) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Invalid stop execution message."); - } - return; - } - JobInvocation job = builder.build(); - googlePlayReceiver.getExecutionDelegator().stopJob(job); - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessengerCallback.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessengerCallback.java deleted file mode 100644 index 06fb153f..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessengerCallback.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG; - -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.JobService.JobResult; - -/** - * Wraps the GooglePlay messenger in a JobCallback-compatible interface. - */ -class GooglePlayMessengerCallback implements JobCallback { - - private final Messenger messenger; - private final String tag; - - GooglePlayMessengerCallback(Messenger messenger, String tag) { - this.messenger = messenger; - this.tag = tag; - } - - @Override - public void jobFinished(@JobResult int status) { - try { - messenger.send(createResultMessage(status)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - @NonNull - private Message createResultMessage(int result) { - final Message msg = Message.obtain(); - msg.what = GooglePlayMessageHandler.MSG_RESULT; - msg.arg1 = result; - - Bundle b = new Bundle(); - b.putString(REQUEST_PARAM_TAG, tag); - msg.setData(b); - return msg; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayReceiver.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayReceiver.java deleted file mode 100644 index e8dc6060..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayReceiver.java +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.app.Service; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Looper; -import android.os.Messenger; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.support.v4.util.SimpleArrayMap; -import android.util.Log; -import android.util.Pair; -import com.firebase.jobdispatcher.Job.Builder; -import com.firebase.jobdispatcher.JobService.JobResult; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; - -/** - * Handles incoming execute requests from the GooglePlay driver and forwards them to your Service. - */ -public class GooglePlayReceiver extends Service implements ExecutionDelegator.JobFinishedCallback { - /** - * Logging tag. - */ - /* package */ static final String TAG = "FJD.GooglePlayReceiver"; - /** - * The action sent by Google Play services that triggers job execution. - */ - @VisibleForTesting - static final String ACTION_EXECUTE = "com.google.android.gms.gcm.ACTION_TASK_READY"; - - /** Action sent by Google Play services when your app has been updated. */ - @VisibleForTesting - static final String ACTION_INITIALIZE = "com.google.android.gms.gcm.SERVICE_ACTION_INITIALIZE"; - - private static final String ERROR_NULL_INTENT = "Null Intent passed, terminating"; - private static final String ERROR_UNKNOWN_ACTION = "Unknown action received, terminating"; - private static final String ERROR_NO_DATA = "No data provided, terminating"; - - private static final JobCoder prefixedCoder = - new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, true); - - private final GooglePlayCallbackExtractor callbackExtractor = new GooglePlayCallbackExtractor(); - - /** - * The single Messenger that's returned from valid onBind requests. Guarded by intrinsic lock. - */ - @VisibleForTesting - Messenger serviceMessenger; - - /** - * Driver for rescheduling jobs. Guarded by intrinsic lock. - */ - @VisibleForTesting - Driver driver; - - /** - * Guarded by intrinsic lock. - */ - @VisibleForTesting - ValidationEnforcer validationEnforcer; - - /** - * The ExecutionDelegator used to communicate with client JobServices. - * Guarded by intrinsic lock. - */ - private ExecutionDelegator executionDelegator; - - /** - * The most recent startId passed to onStartCommand. - * Guarded by intrinsic lock. - */ - private int latestStartId; - - /** - * Endpoint (String) -> Tag (String) -> JobCallback - */ - private SimpleArrayMap> callbacks = - new SimpleArrayMap<>(1); - - private static void sendResultSafely(JobCallback callback, int result) { - try { - callback.jobFinished(result); - } catch (Throwable e) { - Log.e(TAG, "Encountered error running callback", e.getCause()); - } - } - - @Override - public final int onStartCommand(Intent intent, int flags, int startId) { - try { - super.onStartCommand(intent, flags, startId); - - if (intent == null) { - Log.w(TAG, ERROR_NULL_INTENT); - return START_NOT_STICKY; - } - - String action = intent.getAction(); - if (ACTION_EXECUTE.equals(action)) { - getExecutionDelegator().executeJob(prepareJob(intent)); - return START_NOT_STICKY; - } else if (ACTION_INITIALIZE.equals(action)) { - return START_NOT_STICKY; - } - - Log.e(TAG, ERROR_UNKNOWN_ACTION); - return START_NOT_STICKY; - } finally { - synchronized (this) { - latestStartId = startId; - if (callbacks.isEmpty()) { - stopSelf(latestStartId); - } - } - } - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - // Only Lollipop+ supports UID checking messages, so we can't trust this system on older - // platforms. - if (intent == null - || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP - || !ACTION_EXECUTE.equals(intent.getAction())) { - return null; - } - return getServiceMessenger().getBinder(); - } - - private synchronized Messenger getServiceMessenger() { - if (serviceMessenger == null) { - serviceMessenger = - new Messenger(new GooglePlayMessageHandler(Looper.getMainLooper(), this)); - } - return serviceMessenger; - } - - /* package */ synchronized ExecutionDelegator getExecutionDelegator() { - if (executionDelegator == null) { - executionDelegator = new ExecutionDelegator(this, this); - } - return executionDelegator; - } - - @NonNull - private synchronized Driver getGooglePlayDriver() { - if (driver == null) { - driver = new GooglePlayDriver(getApplicationContext()); - } - return driver; - } - - @NonNull - private synchronized ValidationEnforcer getValidationEnforcer() { - if (validationEnforcer == null) { - validationEnforcer = new ValidationEnforcer(getGooglePlayDriver().getValidator()); - } - return validationEnforcer; - } - - @Nullable - @VisibleForTesting - JobInvocation prepareJob(Intent intent) { - Bundle intentExtras = intent.getExtras(); - if (intentExtras == null) { - Log.e(TAG, ERROR_NO_DATA); - return null; - } - - // get the callback first. If we don't have this we can't talk back to the backend. - Pair extraction = callbackExtractor.extractCallback(intentExtras); - if (extraction == null) { - Log.i(TAG, "no callback found"); - return null; - } - return prepareJob(extraction.first, extraction.second); - } - - @Nullable - synchronized JobInvocation prepareJob(JobCallback callback, Bundle bundle) { - JobInvocation job = prefixedCoder.decodeIntentBundle(bundle); - if (job == null) { - Log.e(TAG, "unable to decode job"); - sendResultSafely(callback, JobService.RESULT_FAIL_NORETRY); - return null; - } - SimpleArrayMap map = callbacks.get(job.getService()); - if (map == null) { - map = new SimpleArrayMap<>(1); - callbacks.put(job.getService(), map); - } - - map.put(job.getTag(), callback); - - return job; - } - - @Override - public synchronized void onJobFinished(@NonNull JobInvocation js, @JobResult int result) { - try { - SimpleArrayMap map = callbacks.get(js.getService()); - if (map == null) { - return; - } - JobCallback callback = map.remove(js.getTag()); - if (callback == null) { - return; - } - if (map.isEmpty()) { - callbacks.remove(js.getService()); - } - - if (needsToBeRescheduled(js, result)) { - reschedule(js); - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "sending jobFinished for " + js.getTag() + " = " + result); - } - sendResultSafely(callback, result); - } - } finally { - if (callbacks.isEmpty()) { - // Safe to call stopSelf, even if we're being bound to - stopSelf(latestStartId); - } - } - } - - private void reschedule(JobInvocation jobInvocation) { - Job job = new Builder(getValidationEnforcer(), jobInvocation) - .setReplaceCurrent(true) - .build(); - - getGooglePlayDriver().schedule(job); - } - - /** - * Recurring content URI triggered jobs need to be rescheduled when execution is finished. - * - *

GooglePlay does not support recurring content URI triggered jobs. - * - *

{@link JobService#RESULT_FAIL_RETRY} needs to be sent or current triggered URIs will be - * lost. - */ - private static boolean needsToBeRescheduled(JobParameters job, int result) { - return job.isRecurring() - && job.getTrigger() instanceof ContentUriTrigger - && result != JobService.RESULT_FAIL_RETRY; - } - - static JobCoder getJobCoder() { - return prefixedCoder; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Job.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Job.java deleted file mode 100644 index 692404fe..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Job.java +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.firebase.jobdispatcher.Constraint.JobConstraint; - -/** - * Job is the embodiment of a unit of work and an associated set of triggers, settings, and runtime - * constraints. - */ -public final class Job implements JobParameters { - private final String mService; - private final String mTag; - private final JobTrigger mTrigger; - private final RetryStrategy mRetryStrategy; - private final int mLifetime; - private final boolean mRecurring; - private final int[] mConstraints; - private final boolean mReplaceCurrent; - private Bundle mExtras; - - private Job(Builder builder) { - mService = builder.mServiceClassName; - mExtras = builder.mExtras; - mTag = builder.mTag; - mTrigger = builder.mTrigger; - mRetryStrategy = builder.mRetryStrategy; - mLifetime = builder.mLifetime; - mRecurring = builder.mRecurring; - mConstraints = builder.mConstraints != null ? builder.mConstraints : new int[0]; - mReplaceCurrent = builder.mReplaceCurrent; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public int[] getConstraints() { - return mConstraints; - } - - /** - * {@inheritDoc} - */ - @Nullable - @Override - public Bundle getExtras() { - return mExtras; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public RetryStrategy getRetryStrategy() { - return mRetryStrategy; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean shouldReplaceCurrent() { - return mReplaceCurrent; - } - - @Nullable - @Override - public TriggerReason getTriggerReason() { - return null; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public String getTag() { - return mTag; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public JobTrigger getTrigger() { - return mTrigger; - } - - /** - * {@inheritDoc} - */ - @Override - public int getLifetime() { - return mLifetime; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRecurring() { - return mRecurring; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public String getService() { - return mService; - } - - /** - * A class that understands how to build a {@link Job}. Retrieved by calling - * {@link FirebaseJobDispatcher#newJobBuilder()}. - */ - public final static class Builder implements JobParameters { - private final ValidationEnforcer mValidator; - - private String mServiceClassName; - private Bundle mExtras; - private String mTag; - private JobTrigger mTrigger = Trigger.NOW; - private int mLifetime = Lifetime.UNTIL_NEXT_BOOT; - private int[] mConstraints; - - private RetryStrategy mRetryStrategy = RetryStrategy.DEFAULT_EXPONENTIAL; - private boolean mReplaceCurrent = false; - private boolean mRecurring = false; - - Builder(ValidationEnforcer validator) { - mValidator = validator; - } - - Builder(ValidationEnforcer validator, JobParameters job) { - mValidator = validator; - - mTag = job.getTag(); - mServiceClassName = job.getService(); - mTrigger = job.getTrigger(); - mRecurring = job.isRecurring(); - mLifetime = job.getLifetime(); - mConstraints = job.getConstraints(); - mExtras = job.getExtras(); - mRetryStrategy = job.getRetryStrategy(); - } - - /** - * Adds the provided constraint to the current list of runtime constraints. - */ - public Builder addConstraint(@JobConstraint int constraint) { - // Create a new, longer constraints array - int[] newConstraints = new int[mConstraints == null ? 1 : mConstraints.length + 1]; - - if (mConstraints != null && mConstraints.length != 0) { - // Copy all the old values over - System.arraycopy(mConstraints, 0, newConstraints, 0, mConstraints.length); - } - - // add the new value - newConstraints[newConstraints.length - 1] = constraint; - // update the pointer - mConstraints = newConstraints; - - return this; - } - - /** - * Sets whether this Job should replace pre-existing Jobs with the same tag. - */ - public Builder setReplaceCurrent(boolean replaceCurrent) { - mReplaceCurrent = replaceCurrent; - - return this; - } - - /** - * Builds the Job, using the settings provided so far. - * - * @throws ValidationEnforcer.ValidationException - */ - public Job build() { - mValidator.ensureValid(this); - - return new Job(this); - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public String getService() { - return mServiceClassName; - } - - /** - * Sets the backing JobService class for the Job. See {@link #getService()}. - */ - public Builder setService(Class serviceClass) { - mServiceClassName = serviceClass == null ? null : serviceClass.getName(); - - return this; - } - - /** - * Sets the backing JobService class name for the Job. See {@link #getService()}. - * - *

Should not be exposed, for internal use only. - */ - Builder setServiceName(String serviceClassName) { - mServiceClassName = serviceClassName; - - return this; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public String getTag() { - return mTag; - } - - /** - * Sets the unique String tag used to identify the Job. See {@link #getTag()}. - */ - public Builder setTag(String tag) { - mTag = tag; - - return this; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public JobTrigger getTrigger() { - return mTrigger; - } - - /** - * Sets the Trigger used for the Job. See {@link #getTrigger()}. - */ - public Builder setTrigger(JobTrigger trigger) { - mTrigger = trigger; - - return this; - } - - /** - * {@inheritDoc} - */ - @Override - @Lifetime.LifetimeConstant - public int getLifetime() { - return mLifetime; - } - - /** - * Sets the Job's lifetime, or how long it should persist. See {@link #getLifetime()}. - */ - public Builder setLifetime(@Lifetime.LifetimeConstant int lifetime) { - mLifetime = lifetime; - - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRecurring() { - return mRecurring; - } - - /** - * Sets whether the job should recur. The default is false. - */ - public Builder setRecurring(boolean recurring) { - mRecurring = recurring; - - return this; - } - - /** - * {@inheritDoc} - */ - @Override - @JobConstraint - public int[] getConstraints() { - return mConstraints == null ? new int[]{} : mConstraints; - } - - /** - * Sets the Job's runtime constraints. See {@link #getConstraints()}. - */ - public Builder setConstraints(@JobConstraint int... constraints) { - mConstraints = constraints; - - return this; - } - - /** - * {@inheritDoc} - */ - @Nullable - @Override - public Bundle getExtras() { - return mExtras; - } - - /** - * Sets the user-defined extras associated with the Job. See {@link #getExtras()}. - */ - public Builder setExtras(Bundle extras) { - mExtras = extras; - - return this; - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public RetryStrategy getRetryStrategy() { - return mRetryStrategy; - } - - /** - * Set the RetryStrategy used for the Job. See {@link #getRetryStrategy()}. - */ - public Builder setRetryStrategy(RetryStrategy retryStrategy) { - mRetryStrategy = retryStrategy; - - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean shouldReplaceCurrent() { - return mReplaceCurrent; - } - - @Nullable - @Override - public TriggerReason getTriggerReason() { - return null; - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCallback.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCallback.java deleted file mode 100644 index adeb82ea..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCallback.java +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -/** - * JobCallback describes an object that knows how to send a JobResult back to the underlying - * execution driver. - */ -public interface JobCallback { - /** - * @throws RuntimeException - */ - void jobFinished(@JobService.JobResult int status); -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCoder.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCoder.java deleted file mode 100644 index e5523587..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCoder.java +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.Constraint.compact; -import static com.firebase.jobdispatcher.Constraint.uncompact; -import static com.firebase.jobdispatcher.ExecutionDelegator.TAG; - -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * JobCoder is a tool to encode and decode JobSpecs from Bundles. - */ -/* package */ final class JobCoder { - private final boolean includeExtras; - private final String prefix; - - private static final String JSON_URI_FLAGS = "uri_flags"; - private static final String JSON_URIS = "uris"; - - JobCoder(String prefix, boolean includeExtras) { - this.includeExtras = includeExtras; - this.prefix = prefix; - } - - @NonNull - Bundle encode(@NonNull JobParameters jobParameters, @NonNull Bundle data) { - if (data == null) { - throw new IllegalArgumentException("Unexpected null Bundle provided"); - } - - data.putInt(prefix + BundleProtocol.PACKED_PARAM_LIFETIME, - jobParameters.getLifetime()); - data.putBoolean(prefix + BundleProtocol.PACKED_PARAM_RECURRING, - jobParameters.isRecurring()); - data.putBoolean(prefix + BundleProtocol.PACKED_PARAM_REPLACE_CURRENT, - jobParameters.shouldReplaceCurrent()); - data.putString(prefix + BundleProtocol.PACKED_PARAM_TAG, - jobParameters.getTag()); - data.putString(prefix + BundleProtocol.PACKED_PARAM_SERVICE, - jobParameters.getService()); - data.putInt(prefix + BundleProtocol.PACKED_PARAM_CONSTRAINTS, - compact(jobParameters.getConstraints())); - - if (includeExtras) { - data.putBundle(prefix + BundleProtocol.PACKED_PARAM_EXTRAS, - jobParameters.getExtras()); - } - - encodeTrigger(jobParameters.getTrigger(), data); - encodeRetryStrategy(jobParameters.getRetryStrategy(), data); - - return data; - } - - JobInvocation decodeIntentBundle(@NonNull Bundle bundle) { - if (bundle == null) { - Log.e(TAG, "Unexpected null Bundle provided"); - return null; - } - - Bundle taskExtras = bundle.getBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS); - if (taskExtras == null) { - return null; - } - - JobInvocation.Builder builder = decode(taskExtras); - - List triggeredContentUris = - bundle.getParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS); - if (triggeredContentUris != null) { - builder.setTriggerReason(new TriggerReason(triggeredContentUris)); - } - return builder.build(); - } - - @Nullable - public JobInvocation.Builder decode(@NonNull Bundle data) { - if (data == null) { - throw new IllegalArgumentException("Unexpected null Bundle provided"); - } - - boolean recur = data.getBoolean(prefix + BundleProtocol.PACKED_PARAM_RECURRING); - boolean replaceCur = data.getBoolean(prefix + BundleProtocol.PACKED_PARAM_REPLACE_CURRENT); - int lifetime = data.getInt(prefix + BundleProtocol.PACKED_PARAM_LIFETIME); - int[] constraints = uncompact(data.getInt(prefix + BundleProtocol.PACKED_PARAM_CONSTRAINTS)); - - JobTrigger trigger = decodeTrigger(data); - RetryStrategy retryStrategy = decodeRetryStrategy(data); - - String tag = data.getString(prefix + BundleProtocol.PACKED_PARAM_TAG); - String service = data.getString(prefix + BundleProtocol.PACKED_PARAM_SERVICE); - - if (tag == null || service == null || trigger == null || retryStrategy == null) { - return null; - } - - JobInvocation.Builder builder = new JobInvocation.Builder(); - builder.setTag(tag); - builder.setService(service); - builder.setTrigger(trigger); - builder.setRetryStrategy(retryStrategy); - builder.setRecurring(recur); - //noinspection WrongConstant - builder.setLifetime(lifetime); - //noinspection WrongConstant - builder.setConstraints(constraints); - builder.setReplaceCurrent(replaceCur); - - // repack the taskExtras - builder.addExtras(data); - return builder; - } - - @NonNull - private JobTrigger decodeTrigger(Bundle data) { - switch (data.getInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE)) { - case BundleProtocol.TRIGGER_TYPE_IMMEDIATE: - return Trigger.NOW; - - case BundleProtocol.TRIGGER_TYPE_EXECUTION_WINDOW: - return Trigger.executionWindow( - data.getInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_START), - data.getInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_END)); - - case BundleProtocol.TRIGGER_TYPE_CONTENT_URI: - String uris = data.getString(prefix + BundleProtocol.PACKED_PARAM_OBSERVED_URI); - List observedUris = convertJsonToObservedUris(uris); - return Trigger.contentUriTrigger(Collections.unmodifiableList(observedUris)); - - default: - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Unsupported trigger."); - } - return null; - } - } - - private void encodeTrigger(JobTrigger trigger, Bundle data) { - if (trigger == Trigger.NOW) { - data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE, - BundleProtocol.TRIGGER_TYPE_IMMEDIATE); - } else if (trigger instanceof JobTrigger.ExecutionWindowTrigger) { - JobTrigger.ExecutionWindowTrigger t = (JobTrigger.ExecutionWindowTrigger) trigger; - - data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE, - BundleProtocol.TRIGGER_TYPE_EXECUTION_WINDOW); - data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_START, - t.getWindowStart()); - data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_END, - t.getWindowEnd()); - } else if (trigger instanceof JobTrigger.ContentUriTrigger) { - data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE, - BundleProtocol.TRIGGER_TYPE_CONTENT_URI); - ContentUriTrigger uriTrigger = (ContentUriTrigger) trigger; - String jsonTrigger = convertObservedUrisToJsonString(uriTrigger.getUris()); - data.putString(prefix + BundleProtocol.PACKED_PARAM_OBSERVED_URI, jsonTrigger); - } else { - throw new IllegalArgumentException("Unsupported trigger."); - } - } - - private RetryStrategy decodeRetryStrategy(Bundle data) { - int policy = data.getInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_POLICY); - if (policy != RetryStrategy.RETRY_POLICY_EXPONENTIAL - && policy != RetryStrategy.RETRY_POLICY_LINEAR) { - - return RetryStrategy.DEFAULT_EXPONENTIAL; - } - - //noinspection WrongConstant - return new RetryStrategy( - policy, - data.getInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS), - data.getInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS)); - } - - private void encodeRetryStrategy(RetryStrategy retryStrategy, Bundle data) { - if (retryStrategy == null) { - retryStrategy = RetryStrategy.DEFAULT_EXPONENTIAL; - } - - data.putInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_POLICY, - retryStrategy.getPolicy()); - data.putInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS, - retryStrategy.getInitialBackoff()); - data.putInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS, - retryStrategy.getMaximumBackoff()); - } - - @NonNull - private String convertObservedUrisToJsonString(@NonNull List uris) { - JSONObject contentUris = new JSONObject(); - JSONArray jsonFlags = new JSONArray(); - JSONArray jsonUris = new JSONArray(); - for (ObservedUri uri : uris) { - jsonFlags.put(uri.getFlags()); - jsonUris.put(uri.getUri()); - } - try { - contentUris.put(JSON_URI_FLAGS, jsonFlags); - contentUris.put(JSON_URIS, jsonUris); - } catch (JSONException e) { - throw new RuntimeException(e); - } - return contentUris.toString(); - } - - @NonNull - private List convertJsonToObservedUris(@NonNull String contentUrisJson) { - List uris = new ArrayList<>(); - try { - JSONObject json = new JSONObject(contentUrisJson); - JSONArray jsonFlags = json.getJSONArray(JSON_URI_FLAGS); - JSONArray jsonUris = json.getJSONArray(JSON_URIS); - int length = jsonFlags.length(); - - for (int i = 0; i < length; i++) { - int flags = jsonFlags.getInt(i); - String uri = jsonUris.getString(i); - uris.add(new ObservedUri(Uri.parse(uri), flags)); - } - } catch (JSONException e) { - throw new RuntimeException(e); - } - return uris; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobInvocation.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobInvocation.java deleted file mode 100644 index 08263e84..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobInvocation.java +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.Constraint.JobConstraint; - -/** - * An internal non-Job implementation of JobParameters. Passed to JobService invocations. - */ -/* package */ final class JobInvocation implements JobParameters { - - @NonNull - private final String mTag; - - @NonNull - private final String mService; - - @NonNull - private final JobTrigger mTrigger; - - private final boolean mRecurring; - - private final int mLifetime; - - @NonNull - @JobConstraint - private final int[] mConstraints; - - @NonNull - private final Bundle mExtras; - - private final RetryStrategy mRetryStrategy; - - private final boolean mReplaceCurrent; - - private final TriggerReason mTriggerReason; - - private JobInvocation(Builder builder) { - mTag = builder.mTag; - mService = builder.mService; - mTrigger = builder.mTrigger; - mRetryStrategy = builder.mRetryStrategy; - mRecurring = builder.mRecurring; - mLifetime = builder.mLifetime; - mConstraints = builder.mConstraints; - mExtras = builder.mExtras; - mReplaceCurrent = builder.mReplaceCurrent; - mTriggerReason = builder.mTriggerReason; - } - - @NonNull - @Override - public String getService() { - return mService; - } - - @NonNull - @Override - public String getTag() { - return mTag; - } - - @NonNull - @Override - public JobTrigger getTrigger() { - return mTrigger; - } - - @Override - public int getLifetime() { - return mLifetime; - } - - @Override - public boolean isRecurring() { - return mRecurring; - } - - @NonNull - @Override - public int[] getConstraints() { - return mConstraints; - } - - @NonNull - @Override - public Bundle getExtras() { - return mExtras; - } - - @NonNull - @Override - public RetryStrategy getRetryStrategy() { - return mRetryStrategy; - } - - @Override - public boolean shouldReplaceCurrent() { - return mReplaceCurrent; - } - - @Override - public TriggerReason getTriggerReason() { - return mTriggerReason; - } - - static final class Builder { - - @NonNull - private String mTag; - - @NonNull - private String mService; - - @NonNull - private JobTrigger mTrigger; - - private boolean mRecurring; - - private int mLifetime; - - @NonNull - @JobConstraint - private int[] mConstraints; - - @NonNull - private final Bundle mExtras = new Bundle(); - - private RetryStrategy mRetryStrategy; - - private boolean mReplaceCurrent; - - private TriggerReason mTriggerReason; - - JobInvocation build() { - if (mTag == null || mService == null || mTrigger == null) { - throw new IllegalArgumentException("Required fields were not populated."); - } - return new JobInvocation(this); - } - - public Builder setTag(@NonNull String mTag) { - this.mTag = mTag; - return this; - } - - public Builder setService(@NonNull String mService) { - this.mService = mService; - return this; - } - - public Builder setTrigger(@NonNull JobTrigger mTrigger) { - this.mTrigger = mTrigger; - return this; - } - - public Builder setRecurring(boolean mRecurring) { - this.mRecurring = mRecurring; - return this; - } - - public Builder setLifetime(@Lifetime.LifetimeConstant int mLifetime) { - this.mLifetime = mLifetime; - return this; - } - - public Builder setConstraints(@JobConstraint @NonNull int[] mConstraints) { - this.mConstraints = mConstraints; - return this; - } - - public Builder addExtras(@NonNull Bundle bundle) { - if (bundle != null) { - mExtras.putAll(bundle); - } - return this; - } - - public Builder setRetryStrategy(RetryStrategy mRetryStrategy) { - this.mRetryStrategy = mRetryStrategy; - return this; - } - - public Builder setReplaceCurrent(boolean mReplaceCurrent) { - this.mReplaceCurrent = mReplaceCurrent; - return this; - } - - public Builder setTriggerReason(TriggerReason triggerReason) { - this.mTriggerReason = triggerReason; - return this; - } - } - - /** - * @return true if the tag and the service of provided {@link JobInvocation} have the same - * values. - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || !getClass().equals(o.getClass())) { - return false; - } - - JobInvocation jobInvocation = (JobInvocation) o; - - return mTag.equals(jobInvocation.mTag) - && mService.equals(jobInvocation.mService); - } - - @Override - public int hashCode() { - int result = mTag.hashCode(); - result = 31 * result + mService.hashCode(); - return result; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobParameters.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobParameters.java deleted file mode 100644 index 24f0844e..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobParameters.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.firebase.jobdispatcher.Constraint.JobConstraint; - -/** - * JobParameters represents anything that can describe itself in terms of Job components. - */ -public interface JobParameters { - - /** - * Returns the name of the backing JobService class. - */ - @NonNull - String getService(); - - /** - * Returns a string identifier for the Job. Used when cancelling Jobs and displaying debug - * messages. - */ - @NonNull - String getTag(); - - /** - * The Job's Trigger, which decides when the Job is ready to run. - */ - @NonNull - JobTrigger getTrigger(); - - /** - * The Job's lifetime; how long it should persist for. - */ - @Lifetime.LifetimeConstant - int getLifetime(); - - /** - * Whether the Job should repeat. - */ - boolean isRecurring(); - - /** - * The runtime constraints applied to this Job. A Job is not run until the trigger is activated - * and all the runtime constraints are satisfied. - */ - @JobConstraint - int[] getConstraints(); - - /** - * The optional set of user-supplied extras associated with this Job. - */ - @Nullable - Bundle getExtras(); - - /** - * The RetryStrategy for the Job. Used to determine how to handle failures. - */ - @NonNull - RetryStrategy getRetryStrategy(); - - /** - * Whether the Job should replace a pre-existing Job with the same tag. - */ - boolean shouldReplaceCurrent(); - - /** @return A {@link TriggerReason} that - if non null - describes why the job was triggered. */ - @Nullable - TriggerReason getTriggerReason(); -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobService.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobService.java deleted file mode 100644 index 41076e76..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobService.java +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.app.Service; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Binder; -import android.os.IBinder; -import android.os.Message; -import android.support.annotation.IntDef; -import android.support.annotation.MainThread; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.support.v4.util.SimpleArrayMap; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Locale; - -/** - * JobService is the fundamental unit of work used in the JobDispatcher. - *

- * Users will need to override {@link #onStartJob(JobParameters)}, which is where any asynchronous - * execution should start. This method, like most lifecycle methods, runs on the main thread; you - * must offload execution to another thread (or {@link android.os.AsyncTask}, or - * {@link android.os.Handler}, or your favorite flavor of concurrency). - *

- * Once any asynchronous work is complete {@link #jobFinished(JobParameters, boolean)} should be - * called to inform the backing driver of the result. - *

- * Implementations should also override {@link #onStopJob(JobParameters)}, which will be called if - * the scheduling engine wishes to interrupt your work (most likely because the runtime constraints - * that are associated with the job in question are no longer met). - */ -public abstract class JobService extends Service { - /** - * Returned to indicate the job was executed successfully. If the job is not recurring (i.e. a - * one-off) it will be dequeued and forgotten. If it is recurring the trigger will be reset and - * the job will be requeued. - */ - public static final int RESULT_SUCCESS = 0; - - /** - * Returned to indicate the job encountered an error during execution and should be retried after - * a backoff period. - */ - public static final int RESULT_FAIL_RETRY = 1; - - /** - * Returned to indicate the job encountered an error during execution but should not be retried. - * If the job is not recurring (i.e. a one-off) it will be dequeued and forgotten. If it is - * recurring the trigger will be reset and the job will be requeued. - */ - public static final int RESULT_FAIL_NORETRY = 2; - - static final String TAG = "FJD.JobService"; - - @VisibleForTesting - static final String ACTION_EXECUTE = "com.firebase.jobdispatcher.ACTION_EXECUTE"; - - /** - * Correlates job tags (unique strings) with Messages, which are used to signal the completion - * of a job. - */ - private final SimpleArrayMap runningJobs = new SimpleArrayMap<>(1); - private LocalBinder binder = new LocalBinder(); - - /** - * The entry point to your Job. Implementations should offload work to another thread of - * execution as soon as possible because this runs on the main thread. If work was offloaded, - * call {@link JobService#jobFinished(JobParameters, boolean)} to notify the scheduling service - * that the work is completed. - *

- * In order to reschedule use {@link JobService#jobFinished(JobParameters, boolean)}. - * - * @return {@code true} if there is more work remaining in the worker thread, {@code false} if the job was completed. - */ - @MainThread - public abstract boolean onStartJob(JobParameters job); - - /** - * Called when the scheduling engine has decided to interrupt the execution of a running job, - * most likely because the runtime constraints associated with the job are no longer satisfied. - * The job must stop execution. - * - * @return true if the job should be retried - * @see com.firebase.jobdispatcher.JobInvocation.Builder#setRetryStrategy(RetryStrategy) - * @see RetryStrategy - */ - @MainThread - public abstract boolean onStopJob(JobParameters job); - - @MainThread - void start(JobParameters job, Message msg) { - synchronized (runningJobs) { - if (runningJobs.containsKey(job.getTag())) { - Log.w(TAG, String - .format(Locale.US, "Job with tag = %s was already running.", job.getTag())); - return; - } - runningJobs.put(job.getTag(), new JobCallback(msg)); - - boolean moreWork = onStartJob(job); - if (!moreWork) { - JobCallback callback = runningJobs.remove(job.getTag()); - if (callback != null) { - callback.sendResult(RESULT_SUCCESS); - } - } - } - } - - @MainThread - void stop(JobInvocation job) { - synchronized (runningJobs) { - JobCallback jobCallback = runningJobs.remove(job.getTag()); - - if (jobCallback == null) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Provided job has already been executed."); - } - return; - } - boolean shouldRetry = onStopJob(job); - jobCallback.sendResult(shouldRetry ? RESULT_FAIL_RETRY : RESULT_SUCCESS); - } - } - - /** - * Callback to inform the scheduling driver that you've finished executing. Can be called from - * any thread. When the system receives this message, it will release the wakelock being held. - * - * @param job - * @param needsReschedule - * whether the job should be rescheduled - * @see com.firebase.jobdispatcher.JobInvocation.Builder#setRetryStrategy(RetryStrategy) - */ - public final void jobFinished(@NonNull JobParameters job, boolean needsReschedule) { - if (job == null) { - Log.e(TAG, "jobFinished called with a null JobParameters"); - return; - } - - synchronized (runningJobs) { - JobCallback jobCallback = runningJobs.remove(job.getTag()); - - if (jobCallback != null) { - jobCallback.sendResult(needsReschedule ? RESULT_FAIL_RETRY : RESULT_SUCCESS); - } - } - } - - @Override - public final int onStartCommand(Intent intent, int flags, int startId) { - stopSelf(startId); - - return START_NOT_STICKY; - } - - @Nullable - @Override - public final IBinder onBind(Intent intent) { - return binder; - } - - @Override - public final boolean onUnbind(Intent intent) { - synchronized (runningJobs) { - for (int i = runningJobs.size() - 1; i >= 0; i--) { - JobCallback callback = runningJobs.get(runningJobs.keyAt(i)); - if (callback != null && callback.message != null) { - if (callback.message.obj instanceof JobParameters) { - callback.sendResult(onStopJob((JobParameters) callback.message.obj) - // returned true, would like to be rescheduled - ? RESULT_FAIL_RETRY - // returned false, but was interrupted so consider it a fail - : RESULT_FAIL_NORETRY); - } - } - } - } - - return super.onUnbind(intent); - } - - @Override - public final void onRebind(Intent intent) { - super.onRebind(intent); - } - - @Override - public final void onStart(Intent intent, int startId) { - } - - @Override - protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - super.dump(fd, writer, args); - } - - @Override - public final void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - } - - @Override - public final void onTaskRemoved(Intent rootIntent) { - super.onTaskRemoved(rootIntent); - } - - /** - * The result returned from a job execution. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({RESULT_SUCCESS, RESULT_FAIL_RETRY, RESULT_FAIL_NORETRY}) - public @interface JobResult { - } - - private final static class JobCallback { - public final Message message; - - private JobCallback(Message message) { - this.message = message; - } - - void sendResult(@JobResult int result) { - try { - if (message != null) { - message.arg1 = result; - message.sendToTarget(); - } - } catch (Exception ignored) {}//catch this freaking crash!!!! - } - } - - class LocalBinder extends Binder { - JobService getService() { - return JobService.this; - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobServiceConnection.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobServiceConnection.java deleted file mode 100644 index c96e9f68..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobServiceConnection.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.ExecutionDelegator.TAG; - -import android.content.ComponentName; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.Message; -import android.support.annotation.VisibleForTesting; -import android.util.Log; - -/** - * ServiceConnection for job execution. - */ -@VisibleForTesting -class JobServiceConnection implements ServiceConnection { - - private final JobInvocation jobInvocation; - // Should be sent only once. Can't be reused. - private final Message jobFinishedMessage; - private boolean wasMessageUsed = false; - - //Guarded by "this". Can be updated from main and binder threads. - private JobService.LocalBinder binder; - - JobServiceConnection(JobInvocation jobInvocation, Message jobFinishedMessage) { - this.jobFinishedMessage = jobFinishedMessage; - this.jobInvocation = jobInvocation; - this.jobFinishedMessage.obj = this.jobInvocation; - } - - @Override - public synchronized void onServiceConnected(ComponentName name, IBinder service) { - if (!(service instanceof JobService.LocalBinder)) { - Log.w(TAG, "Unknown service connected"); - return; - } - if (wasMessageUsed) { - Log.w(TAG, "onServiceConnected Duplicate calls. Ignored."); - return; - } else { - wasMessageUsed = true; - } - - binder = (JobService.LocalBinder) service; - - JobService jobService = binder.getService(); - - jobService.start(jobInvocation, jobFinishedMessage); - } - - @Override - public synchronized void onServiceDisconnected(ComponentName name) { - binder = null; - } - - synchronized boolean isBound() { - return binder != null; - } - - synchronized void onStop() { - if (isBound()) { - binder.getService().stop(jobInvocation); - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobTrigger.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobTrigger.java deleted file mode 100644 index b1c510c7..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobTrigger.java +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import java.util.List; - -/** - * Contains all supported triggers. - */ -public class JobTrigger { - - /** - * ImmediateTrigger is a Trigger that's immediately available. The Job will be run as soon as - * the runtime constraints are satisfied. - */ - public static final class ImmediateTrigger extends JobTrigger { - /* package */ ImmediateTrigger() {} - } - - /** - * ExecutionWindow represents a Job trigger that becomes eligible once - * the current elapsed time exceeds the scheduled time + the {@code windowStart} - * value. The scheduler backend is encouraged to use the windowEnd value as a - * signal that the job should be run, but this is not an enforced behavior. - */ - public static final class ExecutionWindowTrigger extends JobTrigger { - private final int mWindowStart; - private final int mWindowEnd; - - /* package */ ExecutionWindowTrigger(int windowStart, int windowEnd) { - this.mWindowStart = windowStart; - this.mWindowEnd = windowEnd; - } - - public int getWindowStart() { - return mWindowStart; - } - - public int getWindowEnd() { - return mWindowEnd; - } - } - - /** A trigger that will be triggered on content update for any of provided uris. */ - public static final class ContentUriTrigger extends JobTrigger { - private final List uris; - - /* package */ ContentUriTrigger(List uris) { - this.uris = uris; - } - - public List getUris() { - return uris; - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobValidator.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobValidator.java deleted file mode 100644 index c97198f4..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobValidator.java +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.Nullable; -import java.util.List; - -/** - * A JobValidator is an object that knows how to validate Jobs and some of their composite - * components. - */ -public interface JobValidator { - /** - * Returns a List of error messages, or null if the JobParameters is - * valid. - */ - @Nullable - List validate(JobParameters job); - - /** - * Returns a List of error messages, or null if the Trigger is - * valid. - * @param trigger - */ - @Nullable - List validate(JobTrigger trigger); - - /** - * Returns a List of error messages, or null if the RetryStrategy - * is valid. - */ - @Nullable - List validate(RetryStrategy retryStrategy); - -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Lifetime.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Lifetime.java deleted file mode 100644 index 456bc221..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Lifetime.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.IntDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Lifetime represents how long a Job should last. - */ -public final class Lifetime { - /** - * The Job should be preserved until the next boot. This is the default. - */ - public final static int UNTIL_NEXT_BOOT = 1; - - /** - * The Job should be preserved "forever." - */ - public final static int FOREVER = 2; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({FOREVER, UNTIL_NEXT_BOOT}) - @interface LifetimeConstant {} -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ObservedUri.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ObservedUri.java deleted file mode 100644 index b22c15e0..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ObservedUri.java +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.net.Uri; -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Represents a single observed URI and any associated flags. */ -public final class ObservedUri { - - private final Uri uri; - - private final int flags; - - /** Flag enforcement. */ - @IntDef(flag = true, value = Flags.FLAG_NOTIFY_FOR_DESCENDANTS) - @Retention(RetentionPolicy.SOURCE) - public @interface Flags { - - /** - * Triggers if any descendants of the given URI change. Corresponds to the {@code - * notifyForDescendants} of {@link android.content.ContentResolver#registerContentObserver}. - */ - int FLAG_NOTIFY_FOR_DESCENDANTS = 1 << 0; - } - - /** - * Create a new ObservedUri. - * - * @param uri The URI to observe. - * @param flags Any {@link Flags} associated with the URI. - */ - public ObservedUri(@NonNull Uri uri, @Flags int flags) { - if (uri == null) { - throw new IllegalArgumentException("URI must not be null."); - } - this.uri = uri; - this.flags = flags; - } - - public Uri getUri() { - return uri; - } - - public int getFlags() { - return flags; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ObservedUri)) { - return false; - } - - ObservedUri otherUri = (ObservedUri) o; - return flags == otherUri.flags && uri.equals(otherUri.uri); - } - - @Override - public int hashCode() { - return uri.hashCode() ^ flags; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/RetryStrategy.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/RetryStrategy.java deleted file mode 100644 index 7d9b3190..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/RetryStrategy.java +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.IntDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * RetryStrategy represents an approach to handling job execution failures. Jobs will have a - * time-based backoff enforced, based on the chosen policy (one of {@code RETRY_POLICY_EXPONENTIAL} - * or {@code RETRY_POLICY_LINEAR}. - */ -public final class RetryStrategy { - /** - * Increase the backoff time exponentially. - *

- * Calculated using {@code initial_backoff * 2 ^ (num_failures - 1)}. - */ - public final static int RETRY_POLICY_EXPONENTIAL = 1; - - /** - * Increase the backoff time linearly. - *

- * Calculated using {@code initial_backoff * num_failures}. - */ - public final static int RETRY_POLICY_LINEAR = 2; - - /** - * Expected schedule is: [30s, 60s, 120s, 240s, ..., 3600s] - */ - public final static RetryStrategy DEFAULT_EXPONENTIAL = - new RetryStrategy(RETRY_POLICY_EXPONENTIAL, 30, 3600); - - /** - * Expected schedule is: [30s, 60s, 90s, 120s, ..., 3600s] - */ - public final static RetryStrategy DEFAULT_LINEAR = - new RetryStrategy(RETRY_POLICY_LINEAR, 30, 3600); - - @RetryPolicy - private final int mPolicy; - private final int mInitialBackoff; - private final int mMaximumBackoff; - - /* package */ RetryStrategy(@RetryPolicy int policy, int initialBackoff, int maximumBackoff) { - mPolicy = policy; - mInitialBackoff = initialBackoff; - mMaximumBackoff = maximumBackoff; - } - - /** - * Returns the backoff policy in place. - */ - @RetryPolicy - public int getPolicy() { - return mPolicy; - } - - /** - * Returns the initial backoff (i.e. when # of failures == 1), in seconds. - */ - public int getInitialBackoff() { - return mInitialBackoff; - } - - /** - * Returns the maximum backoff duration in seconds. - */ - public int getMaximumBackoff() { - return mMaximumBackoff; - } - - @IntDef({RETRY_POLICY_LINEAR, RETRY_POLICY_EXPONENTIAL}) - @Retention(RetentionPolicy.SOURCE) - public @interface RetryPolicy { - } - - /* package */ final static class Builder { - private final ValidationEnforcer mValidator; - - Builder(ValidationEnforcer validator) { - mValidator = validator; - } - - public RetryStrategy build(@RetryPolicy int policy, int initialBackoff, int maxBackoff) { - RetryStrategy rs = new RetryStrategy(policy, initialBackoff, maxBackoff); - mValidator.ensureValid(rs); - return rs; - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/SimpleJobService.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/SimpleJobService.java deleted file mode 100644 index 8cfbe5f4..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/SimpleJobService.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.os.AsyncTask; -import android.support.annotation.CallSuper; -import android.support.v4.util.SimpleArrayMap; - -/** - * SimpleJobService provides a simple way of doing background work in a JobService. - * - * Users should override onRunJob and return one of the {@link JobResult} ints. - */ -public abstract class SimpleJobService extends JobService { - private final SimpleArrayMap runningJobs = - new SimpleArrayMap<>(); - - @CallSuper - @Override - public boolean onStartJob(JobParameters job) { - AsyncJobTask async = new AsyncJobTask(this, job); - - synchronized (runningJobs) { - runningJobs.put(job, async); - } - - async.execute(); - - return true; // more work to do - } - - @CallSuper - @Override - public boolean onStopJob(JobParameters job) { - synchronized (runningJobs) { - AsyncJobTask async = runningJobs.remove(job); - if (async != null) { - async.cancel(true); - return true; - } - } - - return false; - } - - private void onJobFinished(JobParameters jobParameters, boolean b) { - synchronized (runningJobs) { - runningJobs.remove(jobParameters); - } - - jobFinished(jobParameters, b); - } - - @JobResult - public abstract int onRunJob(JobParameters job); - - private static class AsyncJobTask extends AsyncTask { - private final SimpleJobService jobService; - private final JobParameters jobParameters; - - private AsyncJobTask(SimpleJobService jobService, JobParameters jobParameters) { - this.jobService = jobService; - this.jobParameters = jobParameters; - } - - @Override - protected Integer doInBackground(Void... params) { - return jobService.onRunJob(jobParameters); - } - - @Override - protected void onPostExecute(Integer integer) { - jobService.onJobFinished(jobParameters, integer == JobService.RESULT_FAIL_RETRY); - } - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Trigger.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Trigger.java deleted file mode 100644 index 3e01ae9c..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Trigger.java +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.NonNull; -import java.util.List; - -/** - * Generally, a Trigger is an object that can answer the question, "is this job ready to run?" - *

- * More specifically, a Trigger is an opaque, abstract class used to root the type hierarchy. - */ -public final class Trigger { - - /** - * Immediate is a Trigger that's immediately available. The Job will be run as soon as the - * runtime constraints are satisfied. - *

- * It is invalid to schedule an Immediate with a recurring Job. - */ - public final static JobTrigger.ImmediateTrigger NOW = new JobTrigger.ImmediateTrigger(); - - /** - * Creates a new ExecutionWindow based on the provided time interval. - * - * @param windowStart The earliest time (in seconds) the job should be - * considered eligible to run. Calculated from when the - * job was scheduled (for new jobs) or last run (for - * recurring jobs). - * @param windowEnd The latest time (in seconds) the job should be run in - * an ideal world. Calculated in the same way as - * {@code windowStart}. - * @throws IllegalArgumentException if the provided parameters are too - * restrictive. - */ - public static JobTrigger.ExecutionWindowTrigger executionWindow(int windowStart, int windowEnd) { - if (windowStart < 0) { - throw new IllegalArgumentException("Window start can't be less than 0"); - } else if (windowEnd < windowStart) { - throw new IllegalArgumentException("Window end can't be less than window start"); - } - - return new JobTrigger.ExecutionWindowTrigger(windowStart, windowEnd); - } - - /** - * Creates a new ContentUriTrigger based on the provided list of {@link ObservedUri}. - * - * @param uris The list of URIs to observe. The trigger will be available if a piece of content, - * corresponding to any of provided URIs, is updated. - * @throws IllegalArgumentException if provided list of URIs is null or empty. - */ - public static JobTrigger.ContentUriTrigger contentUriTrigger(@NonNull List uris) { - if (uris == null || uris.isEmpty()) { - throw new IllegalArgumentException("Uris must not be null or empty."); - } - return new JobTrigger.ContentUriTrigger(uris); - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/TriggerReason.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/TriggerReason.java deleted file mode 100644 index 00fc053b..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/TriggerReason.java +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.net.Uri; -import java.util.List; - -/** The class contains a summary of the events which caused the job to be executed. */ -public class TriggerReason { - private final List mTriggeredContentUris; - - TriggerReason(List mTriggeredContentUris) { - this.mTriggeredContentUris = mTriggeredContentUris; - } - - public List getTriggeredContentUris() { - return mTriggeredContentUris; - } -} diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ValidationEnforcer.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ValidationEnforcer.java deleted file mode 100644 index d9cfbe02..00000000 --- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ValidationEnforcer.java +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import java.util.List; - -/** - * Wraps a JobValidator and provides helpful validation utilities. - */ -public class ValidationEnforcer implements JobValidator { - private final JobValidator mValidator; - - public ValidationEnforcer(JobValidator validator) { - mValidator = validator; - } - - /** - * {@inheritDoc} - */ - @Nullable - @Override - public List validate(JobParameters job) { - return mValidator.validate(job); - } - - /** - * {@inheritDoc} - * @param trigger - */ - @Nullable - @Override - public List validate(JobTrigger trigger) { - return mValidator.validate(trigger); - } - - /** - * {@inheritDoc} - */ - @Nullable - @Override - public List validate(RetryStrategy retryStrategy) { - return mValidator.validate(retryStrategy); - } - - /** - * Indicates whether the provided JobParameters is valid. - */ - public final boolean isValid(JobParameters job) { - return validate(job) == null; - } - - /** - * Indicates whether the provided JobTrigger is valid. - */ - public final boolean isValid(JobTrigger trigger) { - return validate(trigger) == null; - } - - /** - * Indicates whether the provided RetryStrategy is valid. - */ - public final boolean isValid(RetryStrategy retryStrategy) { - return validate(retryStrategy) == null; - } - - /** - * Throws a RuntimeException if the provided JobParameters is invalid. - * - * @throws ValidationException - */ - public final void ensureValid(JobParameters job) { - ensureNoErrors(validate(job)); - } - - /** - * Throws a RuntimeException if the provided JobTrigger is invalid. - * - * @throws ValidationException - */ - public final void ensureValid(JobTrigger trigger) { - ensureNoErrors(validate(trigger)); - } - - /** - * Throws a RuntimeException if the provided RetryStrategy is - * invalid. - * - * @throws ValidationException - */ - public final void ensureValid(RetryStrategy retryStrategy) { - ensureNoErrors(validate(retryStrategy)); - } - - private void ensureNoErrors(List errors) { - if (errors != null) { - throw new ValidationException("JobParameters is invalid", errors); - } - } - - /** - * An Exception thrown when a validation error is encountered. - */ - public final static class ValidationException extends RuntimeException { - private final List mErrors; - - public ValidationException(String msg, @NonNull List errors) { - super(msg + ": " + TextUtils.join("\n - ", errors)); - mErrors = errors; - } - - public List getErrors() { - return mErrors; - } - } -} diff --git a/jobdispatcher/src/test/java/android/net/http/AndroidHttpClient.java b/jobdispatcher/src/test/java/android/net/http/AndroidHttpClient.java deleted file mode 100644 index 0720a56f..00000000 --- a/jobdispatcher/src/test/java/android/net/http/AndroidHttpClient.java +++ /dev/null @@ -1,10 +0,0 @@ -package android.net.http; - -/** - * Robolectric requires this class be available in the classpath, otherwise {@link - * org.robolectric.Shadows#shadowOf(android.os.Looper)} fails. We don't use AndroidHttpClient, so - * include a stub to make Robolectric happy. - * - * @see https://github.com/robolectric/robolectric/issues/1862 - */ -public class AndroidHttpClient {} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ConstraintTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ConstraintTest.java deleted file mode 100644 index 808d7a3c..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ConstraintTest.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; - -import android.text.TextUtils; -import java.util.Arrays; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class ConstraintTest { - - /** - * Just to get 100% coverage. - */ - @Test - public void testPrivateConstructor() throws Exception { - TestUtil.assertHasSinglePrivateConstructor(Constraint.class); - } - - @Test - public void testCompactAndUnCompact() { - for (List combo : TestUtil.getAllConstraintCombinations()) { - int[] input = TestUtil.toIntArray(combo); - Arrays.sort(input); - - int[] output = Constraint.uncompact(Constraint.compact(input)); - Arrays.sort(output); - - for (int i = 0; i < combo.size(); i++) { - assertEquals("Combination = " + TextUtils.join(", ", combo), - input[i], - output[i]); - } - - assertEquals("Expected length of two arrays to be the same", - input.length, - output.length); - } - } - - @Test - public void compactNull() { - assertEquals(0, Constraint.compact(null)); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ContentUriTriggerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ContentUriTriggerTest.java deleted file mode 100644 index 5ea200af..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ContentUriTriggerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static junit.framework.Assert.assertEquals; - -import android.provider.ContactsContract; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -/** Test for {@link ContentUriTrigger}. */ -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21) -public class ContentUriTriggerTest { - - @Test(expected = IllegalArgumentException.class) - public void constrains_null() throws Exception { - Trigger.contentUriTrigger(null); - } - - @Test(expected = IllegalArgumentException.class) - public void constrains_emptyList() throws Exception { - Trigger.contentUriTrigger(Collections.emptyList()); - } - - @Test - public void constrains_valid() throws Exception { - List uris = Arrays.asList(new ObservedUri(ContactsContract.AUTHORITY_URI, 0)); - ContentUriTrigger uriTrigger = Trigger.contentUriTrigger(uris); - assertEquals(uris, uriTrigger.getUris()); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/DefaultJobValidatorTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/DefaultJobValidatorTest.java deleted file mode 100644 index 2d526417..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/DefaultJobValidatorTest.java +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static java.util.Collections.singletonList; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.provider.ContactsContract; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import com.firebase.jobdispatcher.ObservedUri.Flags; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class DefaultJobValidatorTest { - - @Mock - private Context mMockContext; - - private DefaultJobValidator mValidator; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mValidator = new DefaultJobValidator(mMockContext); - } - - @SuppressWarnings("WrongConstant") - @Test - public void testValidate_retryStrategy() throws Exception { - Map> testCases = new HashMap<>(); - testCases.put( - new RetryStrategy(0 /* bad policy */, 30, 3600), - singletonList("Unknown retry policy provided")); - testCases.put( - new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 15, 3600), - singletonList("Initial backoff must be at least 30s")); - testCases.put( - new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 15, 3600), - singletonList("Initial backoff must be at least 30s")); - testCases.put( - new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 30, 60), - singletonList("Maximum backoff must be greater than 300s (5 minutes)")); - testCases.put( - new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 30, 60), - singletonList("Maximum backoff must be greater than 300s (5 minutes)")); - testCases.put( - new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 301, 300), - singletonList("Maximum backoff must be greater than or equal to initial backoff")); - testCases.put( - new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 301, 300), - singletonList("Maximum backoff must be greater than or equal to initial backoff")); - - for (Entry> testCase : testCases.entrySet()) { - List validationErrors = mValidator.validate(testCase.getKey()); - assertNotNull("Expected validation errors, but got null", validationErrors); - - for (String expected : testCase.getValue()) { - assertTrue( - "Expected validation errors to contain \"" + expected + "\"", - validationErrors.contains(expected)); - } - } - } - - @Test - public void testValidate_trigger() throws Exception { - Map testCases = new HashMap<>(); - - testCases.put(Trigger.NOW, null); - testCases.put(Trigger.executionWindow(0, 100), null); - ContentUriTrigger contentUriTrigger = - Trigger.contentUriTrigger( - Arrays.asList( - new ObservedUri( - ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS))); - testCases.put(contentUriTrigger, null); - - for (Entry testCase : testCases.entrySet()) { - List validationErrors = mValidator.validate(testCase.getKey()); - if (testCase.getValue() == null) { - assertNull("Expected no validation errors for trigger", validationErrors); - } else { - assertTrue( - "Expected validation errors to contain \"" + testCase.getValue() + "\"", - validationErrors.contains(testCase.getValue())); - } - } - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionDelegatorTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionDelegatorTest.java deleted file mode 100644 index a523c465..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionDelegatorTest.java +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.TestUtil.getContentUriTrigger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.JobService.JobResult; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -@SuppressWarnings("WrongConstant") -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21) -public class ExecutionDelegatorTest { - - private Context mMockContext; - private TestJobReceiver mReceiver; - private ExecutionDelegator mExecutionDelegator; - - @Before - public void setUp() { - mMockContext = spy(RuntimeEnvironment.application); - doReturn("com.example.foo").when(mMockContext).getPackageName(); - - mReceiver = new TestJobReceiver(); - mExecutionDelegator = new ExecutionDelegator(mMockContext, mReceiver); - } - - @Test - public void testExecuteJob_sendsBroadcastWithJobAndMessage() throws Exception { - for (JobInvocation input : TestUtil.getJobInvocationCombinations()) { - verifyExecuteJob(input); - } - } - - private void verifyExecuteJob(JobInvocation input) throws Exception { - reset(mMockContext); - mReceiver.lastResult = -1; - - mReceiver.setLatch(new CountDownLatch(1)); - - mExecutionDelegator.executeJob(input); - - final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); - final ArgumentCaptor connCaptor = - ArgumentCaptor.forClass(ServiceConnection.class); - verify(mMockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt()); - - final Intent result = intentCaptor.getValue(); - // verify the intent was sent to the right place - assertEquals(input.getService(), result.getComponent().getClassName()); - assertEquals(JobService.ACTION_EXECUTE, result.getAction()); - - final ServiceConnection connection = connCaptor.getValue(); - - ComponentName cname = mock(ComponentName.class); - JobService.LocalBinder mockLocalBinder = mock(JobService.LocalBinder.class); - final JobParameters[] out = new JobParameters[1]; - - JobService mockJobService = new JobService() { - @Override - public boolean onStartJob(JobParameters job) { - out[0] = job; - return false; - } - - @Override - public boolean onStopJob(JobParameters job) { - return false; - } - }; - - when(mockLocalBinder.getService()).thenReturn(mockJobService); - - connection.onServiceConnected(cname, mockLocalBinder); - - TestUtil.assertJobsEqual(input, out[0]); - - // make sure the countdownlatch was decremented - assertTrue(mReceiver.mLatch.await(1, TimeUnit.SECONDS)); - - // verify the lastResult was set correctly - assertEquals(JobService.RESULT_SUCCESS, mReceiver.lastResult); - } - - @Test - public void testExecuteJob_handlesNull() { - assertFalse("Expected calling triggerExecution on null to fail and return false", - mExecutionDelegator.executeJob(null)); - } - - @Test - public void testHandleMessage_doesntCrashOnBadJobData() { - JobInvocation j = new JobInvocation.Builder() - .setService(TestJobService.class.getName()) - .setTag("tag") - .setTrigger(Trigger.NOW) - .build(); - - mExecutionDelegator.executeJob(j); - - ArgumentCaptor intentCaptor = - ArgumentCaptor.forClass(Intent.class); - ArgumentCaptor connCaptor = - ArgumentCaptor.forClass(ServiceConnection.class); - - //noinspection WrongConstant - verify(mMockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt()); - - Intent executeReq = intentCaptor.getValue(); - assertEquals(JobService.ACTION_EXECUTE, executeReq.getAction()); - } - - @Test - public void onStop_mock() throws InterruptedException { - JobInvocation job = new JobInvocation.Builder() - .setTag("TAG") - .setTrigger(getContentUriTrigger()) - .setService(TestJobService.class.getName()) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .build(); - - reset(mMockContext); - mReceiver.lastResult = -1; - - mExecutionDelegator.executeJob(job); - - final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); - final ArgumentCaptor connCaptor = - ArgumentCaptor.forClass(ServiceConnection.class); - verify(mMockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt()); - - final Intent result = intentCaptor.getValue(); - // verify the intent was sent to the right place - assertEquals(job.getService(), result.getComponent().getClassName()); - assertEquals(JobService.ACTION_EXECUTE, result.getAction()); - - final JobParameters[] out = new JobParameters[2]; - - JobService mockJobService = new JobService() { - @Override - public boolean onStartJob(JobParameters job) { - out[0] = job; - return true; - } - - @Override - public boolean onStopJob(JobParameters job) { - out[1] = job; - return false; - } - }; - - JobService.LocalBinder mockLocalBinder = mock(JobService.LocalBinder.class); - when(mockLocalBinder.getService()).thenReturn(mockJobService); - - ComponentName componentName = mock(ComponentName.class); - final ServiceConnection connection = connCaptor.getValue(); - connection.onServiceConnected(componentName, mockLocalBinder); - - mExecutionDelegator.stopJob(job); - - TestUtil.assertJobsEqual(job, out[0]); - TestUtil.assertJobsEqual(job, out[1]); - } - - private final static class TestJobReceiver implements ExecutionDelegator.JobFinishedCallback { - int lastResult; - - private CountDownLatch mLatch; - - @Override - public void onJobFinished(@NonNull JobInvocation js, @JobResult int result) { - lastResult = result; - - if (mLatch != null) { - mLatch.countDown(); - } - } - - /** - * Convenience method for tests. - */ - public void setLatch(CountDownLatch latch) { - mLatch = latch; - } - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionWindowTriggerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionWindowTriggerTest.java deleted file mode 100644 index 6dfe8577..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionWindowTriggerTest.java +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class ExecutionWindowTriggerTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Test - public void testNewInstance_withValidWindow() throws Exception { - JobTrigger.ExecutionWindowTrigger trigger = Trigger.executionWindow(0, 60); - - assertEquals(0, trigger.getWindowStart()); - assertEquals(60, trigger.getWindowEnd()); - } - - @Test - public void testNewInstance_withNegativeStart() throws Exception { - expectedException.expect(IllegalArgumentException.class); - - Trigger.executionWindow(-10, 60); - } - - @Test - public void testNewInstance_withNegativeEnd() throws Exception { - expectedException.expect(IllegalArgumentException.class); - - Trigger.executionWindow(0, -1); - } - - @Test - public void testNewInstance_withReversedValues() throws Exception { - expectedException.expect(IllegalArgumentException.class); - - Trigger.executionWindow(60, 0); - } - - @Test - public void testNewInstance_withTooSmallWindow_now() throws Exception { - expectedException.expect(IllegalArgumentException.class); - - Trigger.executionWindow(60, 59); - } - - @Test - public void testNewInstance_withTooSmallWindow_inFuture() throws Exception { - expectedException.expect(IllegalArgumentException.class); - - Trigger.executionWindow(200, 100); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExtendedShadowParcel.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExtendedShadowParcel.java deleted file mode 100644 index e5ecf104..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExtendedShadowParcel.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.firebase.jobdispatcher; - -import android.os.IBinder; -import android.os.Parcel; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.annotation.RealObject; -import org.robolectric.shadows.ShadowParcel; - -/** - * ShadowParcel doesn't correctly handle {@link Parcel#writeStrongBinder(IBinder)} or {@link - * Parcel#readStrongBinder()}, so we shim a simple implementation that uses an in-memory map to read - * and write Binder objects. - */ -@Implements(Parcel.class) -public class ExtendedShadowParcel extends ShadowParcel { - @RealObject private Parcel realObject; - - // Map each IBinder to an integer, and use the super's int-writing capability to fake Binder - // read/writes. - private final AtomicInteger nextBinderId = new AtomicInteger(1); - private final Map binderMap = - Collections.synchronizedMap(new HashMap()); - - @Implementation - public void writeStrongBinder(IBinder binder) { - int id = nextBinderId.getAndIncrement(); - binderMap.put(id, binder); - realObject.writeInt(id); - } - - @Implementation - public IBinder readStrongBinder() { - return binderMap.get(realObject.readInt()); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/FirebaseJobDispatcherTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/FirebaseJobDispatcherTest.java deleted file mode 100644 index dd0b22ee..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/FirebaseJobDispatcherTest.java +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.firebase.jobdispatcher.FirebaseJobDispatcher.ScheduleFailedException; -import java.util.Arrays; -import java.util.List; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class FirebaseJobDispatcherTest { - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Mock - private Driver mDriver; - - @Mock - private JobValidator mValidator; - - private FirebaseJobDispatcher mDispatcher; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mDriver.getValidator()).thenReturn(mValidator); - - mDispatcher = new FirebaseJobDispatcher(mDriver); - setDriverAvailability(true); - } - - @Test - public void testSchedule_passThrough() throws Exception { - final int[] possibleResults = { - FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS, - FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, - FirebaseJobDispatcher.SCHEDULE_RESULT_BAD_SERVICE, - FirebaseJobDispatcher.SCHEDULE_RESULT_UNKNOWN_ERROR, - FirebaseJobDispatcher.SCHEDULE_RESULT_UNSUPPORTED_TRIGGER}; - - for (int result : possibleResults) { - when(mDriver.schedule(null)).thenReturn(result); - assertEquals(result, mDispatcher.schedule(null)); - } - - verify(mDriver, times(possibleResults.length)).schedule(null); - } - - @Test - public void testSchedule_unavailable() throws Exception { - setDriverAvailability(false); - assertEquals( - FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, - mDispatcher.schedule(null)); - verify(mDriver, never()).schedule(null); - } - - @Test - public void testCancelJob() throws Exception { - final String tag = "foo"; - - // simulate success - when(mDriver.cancel(tag)) - .thenReturn(FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS); - - assertEquals( - "Expected dispatcher to pass the result of Driver#cancel(String, Class) through", - FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS, - mDispatcher.cancel(tag)); - - // verify the driver was indeed called - verify(mDriver).cancel(tag); - } - - @Test - public void testCancelJob_unavailable() throws Exception { - setDriverAvailability(false); // driver is unavailable - - assertEquals( - FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE, - mDispatcher.cancel("foo")); - - // verify the driver was never even consulted - verify(mDriver, never()).cancel("foo"); - } - - @Test - public void testCancelAllJobs() throws Exception { - when(mDriver.cancelAll()).thenReturn(FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS); - - assertEquals(FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS, mDispatcher.cancelAll()); - verify(mDriver).cancelAll(); - } - - @Test - public void testCancelAllJobs_unavailable() throws Exception { - setDriverAvailability(false); // driver is unavailable - - assertEquals( - FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE, - mDispatcher.cancelAll()); - - verify(mDriver, never()).cancelAll(); - } - - @Test - public void testMustSchedule_success() throws Exception { - when(mDriver.schedule(null)).thenReturn(FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS); - - /* assert no exception is thrown */ - mDispatcher.mustSchedule(null); - } - - @Test - public void testMustSchedule_unavailable() throws Exception { - setDriverAvailability(false); // driver is unavailable - expectedException.expect(FirebaseJobDispatcher.ScheduleFailedException.class); - - mDispatcher.mustSchedule(null); - } - - @Test - public void testMustSchedule_failure() throws Exception { - final int[] possibleErrors = { - FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, - FirebaseJobDispatcher.SCHEDULE_RESULT_BAD_SERVICE, - FirebaseJobDispatcher.SCHEDULE_RESULT_UNKNOWN_ERROR, - FirebaseJobDispatcher.SCHEDULE_RESULT_UNSUPPORTED_TRIGGER}; - - for (int scheduleError : possibleErrors) { - when(mDriver.schedule(null)).thenReturn(scheduleError); - - try { - mDispatcher.mustSchedule(null); - - fail("Expected mustSchedule() with error code " + scheduleError + " to fail"); - } catch (ScheduleFailedException expected) { /* expected */ } - } - - verify(mDriver, times(possibleErrors.length)).schedule(null); - } - - @Test - public void testNewRetryStrategyBuilder() { - // custom validator that only approves strategies where initialbackoff == 30s - when(mValidator.validate(any(RetryStrategy.class))).thenAnswer(new Answer>() { - @Override - public List answer(InvocationOnMock invocation) throws Throwable { - RetryStrategy rs = (RetryStrategy) invocation.getArguments()[0]; - // only succeed if initialBackoff == 30s - return rs.getInitialBackoff() == 30 ? null : Arrays.asList("foo", "bar"); - } - }); - - try { - mDispatcher.newRetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 0, 30); - fail("Expected initial backoff != 30s to fail using custom validator"); - } catch (Exception unused) { /* unused */ } - - try { - mDispatcher.newRetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 30, 30); - } catch (Exception unused) { - fail("Expected initial backoff == 30s not to fail using custom validator"); - } - } - - public void setDriverAvailability(boolean driverAvailability) { - when(mDriver.isAvailable()).thenReturn(driverAvailability); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractorTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractorTest.java deleted file mode 100644 index f6591850..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractorTest.java +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Pair; -import com.firebase.jobdispatcher.TestUtil.InspectableBinder; -import com.firebase.jobdispatcher.TestUtil.TransactionArguments; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config( - constants = BuildConfig.class, - manifest = Config.NONE, - sdk = 23, - shadows = {ExtendedShadowParcel.class} -) -public final class GooglePlayCallbackExtractorTest { - @Mock - private IBinder mBinder; - - private GooglePlayCallbackExtractor mExtractor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mExtractor = new GooglePlayCallbackExtractor(); - } - - @Test - public void testExtractCallback_nullBundle() { - assertNull(mExtractor.extractCallback(null)); - } - - @Test - public void testExtractCallback_nullParcelable() { - Bundle emptyBundle = new Bundle(); - assertNull(extractCallback(emptyBundle)); - } - - @Test - public void testExtractCallback_badParcelable() { - Bundle misconfiguredBundle = new Bundle(); - misconfiguredBundle.putParcelable("callback", new BadParcelable(1)); - - assertNull(extractCallback(misconfiguredBundle)); - } - - @Test - public void testExtractCallback_goodParcelable() { - InspectableBinder binder = new InspectableBinder(); - Bundle validBundle = new Bundle(); - validBundle.putParcelable("callback", binder.toPendingCallback()); - - Pair extraction = extractCallback(validBundle); - assertNotNull(extraction); - assertEquals("should have stripped the 'callback' entry from the extracted bundle", - 0, extraction.second.keySet().size()); - extraction.first.jobFinished(JobService.RESULT_SUCCESS); - - // Check our homemade Binder is doing the right things: - TransactionArguments args = binder.getArguments().get(0); - // Should have set the transaction code: - assertEquals("transaction code", IBinder.FIRST_CALL_TRANSACTION + 1, args.code); - - // strong mode bit - args.data.readInt(); - // interface token - assertEquals("com.google.android.gms.gcm.INetworkTaskCallback", args.data.readString()); - // result - assertEquals("result", JobService.RESULT_SUCCESS, args.data.readInt()); - } - - @Test - public void testExtractCallback_extraMapValues() { - Bundle validBundle = new Bundle(); - validBundle.putString("foo", "bar"); - validBundle.putInt("bar", 3); - validBundle.putParcelable("parcelable", new Bundle()); - validBundle.putParcelable("callback", new InspectableBinder().toPendingCallback()); - - Pair extraction = extractCallback(validBundle); - assertNotNull(extraction); - assertEquals("should have stripped the 'callback' entry from the extracted bundle", - 3, extraction.second.keySet().size()); - } - - private Pair extractCallback(Bundle bundle) { - return mExtractor.extractCallback(bundle); - } - - private static final class BadParcelable implements Parcelable { - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - @Override - public BadParcelable createFromParcel(Parcel in) { - return new BadParcelable(in); - } - - @Override - public BadParcelable[] newArray(int size) { - return new BadParcelable[size]; - } - }; - private final int mNum; - - public BadParcelable(int i) { - mNum = i; - } - - private BadParcelable(Parcel in) { - mNum = in.readInt(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dst, int flags) { - dst.writeInt(mNum); - } - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayDriverTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayDriverTest.java deleted file mode 100644 index 822a8f1d..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayDriverTest.java +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.os.Parcelable; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class GooglePlayDriverTest { - @Mock - public Context mMockContext; - - private TestJobDriver mDriver; - private FirebaseJobDispatcher mDispatcher; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mDriver = new TestJobDriver(new GooglePlayDriver(mMockContext)); - mDispatcher = new FirebaseJobDispatcher(mDriver); - - when(mMockContext.getPackageName()).thenReturn("foo.bar.whatever"); - } - - @Test - public void testSchedule_failsWhenPlayServicesIsUnavailable() throws Exception { - markBackendUnavailable(); - mockPackageManagerInfo(); - - Job job = null; - try { - job = mDispatcher.newJobBuilder() - .setService(TestJobService.class) - .setTag("foobar") - .setConstraints(Constraint.DEVICE_CHARGING) - .setTrigger(Trigger.executionWindow(0, 60)) - .build(); - } catch (ValidationEnforcer.ValidationException ve) { - fail(TextUtils.join("\n", ve.getErrors())); - } - - assertEquals("Expected schedule() request to fail when backend is unavailable", - FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, - mDispatcher.schedule(job)); - } - - @Test - public void testCancelJobs_backendUnavailable() throws Exception { - markBackendUnavailable(); - - assertEquals("Expected cancelAll() request to fail when backend is unavailable", - FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE, - mDispatcher.cancelAll()); - } - - @Test - public void testSchedule_sendsAppropriateBroadcast() { - ArgumentCaptor pmQueryIntentCaptor = mockPackageManagerInfo(); - - Job job = mDispatcher.newJobBuilder() - .setConstraints(Constraint.DEVICE_CHARGING) - .setService(TestJobService.class) - .setTrigger(Trigger.executionWindow(0, 60)) - .setRecurring(false) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .setTag("foobar") - .build(); - - Intent pmQueryIntent = pmQueryIntentCaptor.getValue(); - assertEquals(JobService.ACTION_EXECUTE, pmQueryIntent.getAction()); - assertEquals(TestJobService.class.getName(), pmQueryIntent.getComponent().getClassName()); - - assertEquals("Expected schedule() request to succeed", - FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS, - mDispatcher.schedule(job)); - - final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); - verify(mMockContext).sendBroadcast(captor.capture()); - - Intent broadcast = captor.getValue(); - - assertNotNull(broadcast); - assertEquals("com.google.android.gms.gcm.ACTION_SCHEDULE", broadcast.getAction()); - assertEquals("SCHEDULE_TASK", broadcast.getStringExtra("scheduler_action")); - assertEquals("com.google.android.gms", broadcast.getPackage()); - assertEquals(8, broadcast.getIntExtra("source", -1)); - assertEquals(1, broadcast.getIntExtra("source_version", -1)); - - final Parcelable parcelablePendingIntent = broadcast.getParcelableExtra("app"); - assertTrue("Expected 'app' value to be a PendingIntent", - parcelablePendingIntent instanceof PendingIntent); - } - - private ArgumentCaptor mockPackageManagerInfo() { - PackageManager packageManager = mock(PackageManager.class); - when(mMockContext.getPackageManager()).thenReturn(packageManager); - ArgumentCaptor intentArgCaptor = ArgumentCaptor.forClass(Intent.class); - - ResolveInfo info = new ResolveInfo(); - info.serviceInfo = new ServiceInfo(); - info.serviceInfo.enabled = true; - - //noinspection WrongConstant - when(packageManager.queryIntentServices(intentArgCaptor.capture(), eq(0))) - .thenReturn(Arrays.asList(info)); - - return intentArgCaptor; - } - - @Test - public void testCancel_sendsAppropriateBroadcast() { - mDispatcher.cancel("foobar"); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); - verify(mMockContext).sendBroadcast(captor.capture()); - - Intent broadcast = captor.getValue(); - - assertNotNull(broadcast); - assertEquals("foobar", broadcast.getStringExtra("tag")); - } - - private void markBackendUnavailable() { - mDriver.available = false; - } - - public final static class TestJobDriver implements Driver { - public boolean available = true; - - private final Driver wrappedDriver; - - public TestJobDriver(Driver wrappedDriver) { - this.wrappedDriver = wrappedDriver; - } - - @Override - public int schedule(@NonNull Job job) { - return this.wrappedDriver.schedule(job); - } - - @Override - public int cancel(@NonNull String tag) { - return this.wrappedDriver.cancel(tag); - } - - @Override - public int cancelAll() { - return this.wrappedDriver.cancelAll(); - } - - @NonNull - @Override - public JobValidator getValidator() { - return this.wrappedDriver.getValidator(); - } - - @Override - public boolean isAvailable() { - return available; - } - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayJobWriterTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayJobWriterTest.java deleted file mode 100644 index c56ab0d0..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayJobWriterTest.java +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import com.firebase.jobdispatcher.Job.Builder; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import com.firebase.jobdispatcher.ObservedUri.Flags; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class GooglePlayJobWriterTest { - - private static final boolean[] ALL_BOOLEANS = {true, false}; - - private GooglePlayJobWriter mWriter; - - private static Builder initializeDefaultBuilder() { - return TestUtil.getBuilderWithNoopValidator() - .setConstraints(Constraint.DEVICE_CHARGING) - .setExtras(null) - .setLifetime(Lifetime.FOREVER) - .setRecurring(false) - .setReplaceCurrent(false) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .setService(TestJobService.class) - .setTag("tag") - .setTrigger(Trigger.NOW); - } - - @Before - public void setUp() throws Exception { - mWriter = new GooglePlayJobWriter(); - } - - @Test - public void testWriteToBundle_tags() { - for (String tag : Arrays.asList("foo", "bar", "foobar", "this is a tag")) { - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setTag(tag).build(), - new Bundle()); - - assertEquals("tag", tag, b.getString("tag")); - } - } - - @Test - public void testWriteToBundle_updateCurrent() { - for (boolean replaceCurrent : ALL_BOOLEANS) { - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setReplaceCurrent(replaceCurrent).build(), - new Bundle()); - - assertEquals("update_current", replaceCurrent, b.getBoolean("update_current")); - } - } - - @Test - public void testWriteToBundle_persisted() { - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setLifetime(Lifetime.FOREVER).build(), - new Bundle()); - - assertTrue("persisted", b.getBoolean("persisted")); - - for (int lifetime : new int[]{Lifetime.UNTIL_NEXT_BOOT}) { - b = mWriter.writeToBundle( - initializeDefaultBuilder().setLifetime(lifetime).build(), - new Bundle()); - - assertFalse("persisted", b.getBoolean("persisted")); - } - } - - @Test - public void testWriteToBundle_service() { - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setService(TestJobService.class).build(), - new Bundle()); - - assertEquals("service", GooglePlayReceiver.class.getName(), b.getString("service")); - } - - @Test - public void testWriteToBundle_requiredNetwork() { - Map mapping = new HashMap<>(); - mapping.put(Constraint.ON_ANY_NETWORK, GooglePlayJobWriter.LEGACY_NETWORK_CONNECTED); - mapping.put(Constraint.ON_UNMETERED_NETWORK, GooglePlayJobWriter.LEGACY_NETWORK_UNMETERED); - mapping.put(0, GooglePlayJobWriter.LEGACY_NETWORK_ANY); - - for (Entry testCase : mapping.entrySet()) { - @SuppressWarnings("WrongConstant") - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setConstraints(testCase.getKey()).build(), - new Bundle()); - - assertEquals("requiredNetwork", (int) testCase.getValue(), b.getInt("requiredNetwork")); - } - } - - @Test - public void testWriteToBundle_unmeteredConstraintShouldTakePrecendence() { - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder() - .setConstraints(Constraint.ON_ANY_NETWORK, Constraint.ON_UNMETERED_NETWORK) - .build(), - new Bundle()); - - assertEquals("expected ON_UNMETERED_NETWORK to take precendence over ON_ANY_NETWORK", - GooglePlayJobWriter.LEGACY_NETWORK_UNMETERED, b.getInt("requiredNetwork")); - } - - @Test - public void testWriteToBundle_requiresCharging() { - assertTrue("requiresCharging", mWriter.writeToBundle( - initializeDefaultBuilder().setConstraints(Constraint.DEVICE_CHARGING).build(), - new Bundle()).getBoolean("requiresCharging")); - - for (Integer constraint : Arrays.asList( - Constraint.ON_ANY_NETWORK, - Constraint.ON_UNMETERED_NETWORK)) { - - assertFalse("requiresCharging", mWriter.writeToBundle( - initializeDefaultBuilder().setConstraints(constraint).build(), - new Bundle()).getBoolean("requiresCharging")); - } - } - - @Test - public void testWriteToBundle_requiresIdle() { - assertTrue("requiresIdle", mWriter.writeToBundle( - initializeDefaultBuilder().setConstraints(Constraint.DEVICE_IDLE).build(), - new Bundle()).getBoolean("requiresIdle")); - - for (Integer constraint : Arrays.asList( - Constraint.ON_ANY_NETWORK, - Constraint.ON_UNMETERED_NETWORK)) { - - assertFalse("requiresIdle", mWriter.writeToBundle( - initializeDefaultBuilder().setConstraints(constraint).build(), - new Bundle()).getBoolean("requiresIdle")); - } - } - - @Test - public void testWriteToBundle_retryPolicy() { - assertEquals("retry_policy", - GooglePlayJobWriter.LEGACY_RETRY_POLICY_EXPONENTIAL, - mWriter.writeToBundle( - initializeDefaultBuilder() - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .build(), - new Bundle()).getBundle("retryStrategy").getInt("retry_policy")); - - assertEquals("retry_policy", - GooglePlayJobWriter.LEGACY_RETRY_POLICY_LINEAR, - mWriter.writeToBundle( - initializeDefaultBuilder() - .setRetryStrategy(RetryStrategy.DEFAULT_LINEAR) - .build(), - new Bundle()).getBundle("retryStrategy").getInt("retry_policy")); - } - - @Test - public void testWriteToBundle_backoffSeconds() { - for (RetryStrategy retryStrategy : Arrays - .asList(RetryStrategy.DEFAULT_EXPONENTIAL, RetryStrategy.DEFAULT_LINEAR)) { - - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setRetryStrategy(retryStrategy).build(), - new Bundle()).getBundle("retryStrategy"); - - assertEquals("initial_backoff_seconds", - retryStrategy.getInitialBackoff(), - b.getInt("initial_backoff_seconds")); - - assertEquals("maximum_backoff_seconds", - retryStrategy.getMaximumBackoff(), - b.getInt("maximum_backoff_seconds")); - - } - } - - @Test - public void testWriteToBundle_triggers() { - // immediate - Bundle b = mWriter.writeToBundle( - initializeDefaultBuilder().setTrigger(Trigger.NOW).build(), - new Bundle()); - - assertEquals("window_start", 0, b.getLong("window_start")); - assertEquals("window_end", 30, b.getLong("window_end")); - - // execution window (oneoff) - JobTrigger.ExecutionWindowTrigger t = Trigger.executionWindow(631, 978); - b = mWriter.writeToBundle( - initializeDefaultBuilder().setTrigger(t).build(), - new Bundle()); - - assertEquals("window_start", t.getWindowStart(), b.getLong("window_start")); - assertEquals("window_end", t.getWindowEnd(), b.getLong("window_end")); - - // execution window (periodic) - b = mWriter.writeToBundle( - initializeDefaultBuilder().setRecurring(true).setTrigger(t).build(), - new Bundle()); - - assertEquals("period", t.getWindowEnd(), b.getLong("period")); - assertEquals("period_flex", t.getWindowEnd() - t.getWindowStart(), b.getLong("period_flex")); - } - - @Test - public void testWriteToBundle_contentUriTrigger() { - ObservedUri observedUri = new ObservedUri(ContactsContract.AUTHORITY_URI, - Flags.FLAG_NOTIFY_FOR_DESCENDANTS); - ContentUriTrigger contentUriTrigger = Trigger.contentUriTrigger(Arrays.asList(observedUri)); - Bundle bundle = mWriter.writeToBundle( - initializeDefaultBuilder().setTrigger(contentUriTrigger).build(), new Bundle()); - Uri[] uris = - (Uri[]) bundle.getParcelableArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_ARRAY); - int[] flags = bundle.getIntArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY); - assertTrue("Array size", uris.length == flags.length && flags.length == 1); - assertEquals(BundleProtocol.PACKED_PARAM_CONTENT_URI_ARRAY, - ContactsContract.AUTHORITY_URI, uris[0]); - assertEquals(BundleProtocol.PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY, - Flags.FLAG_NOTIFY_FOR_DESCENDANTS, flags[0]); - } - - @Test - public void testWriteToBundle_extras() { - Bundle extras = new Bundle(); - - Bundle result = mWriter.writeToBundle( - initializeDefaultBuilder().setExtras(extras).build(), - new Bundle()); - - assertEquals("extras", extras, result.getBundle("extras")); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessageHandlerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessageHandlerTest.java deleted file mode 100644 index 31969a16..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessageHandlerTest.java +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.AppOpsManager; -import android.content.Context; -import android.os.Bundle; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import com.firebase.jobdispatcher.JobInvocation.Builder; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -/** - * Tests {@link GooglePlayMessageHandler}. - */ -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21) -public class GooglePlayMessageHandlerTest { - - @Mock - Looper looper; - @Mock - GooglePlayReceiver receiverMock; - @Mock - Context context; - @Mock - AppOpsManager appOpsManager; - @Mock - Messenger messengerMock; - @Mock - ExecutionDelegator executionDelegatorMock; - - GooglePlayMessageHandler handler; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - handler = new GooglePlayMessageHandler(looper, receiverMock); - when(receiverMock.getExecutionDelegator()).thenReturn(executionDelegatorMock); - when(receiverMock.getApplicationContext()).thenReturn(context); - when(context.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(appOpsManager); - } - - @Test - public void handleMessage_nullNoException() throws Exception { - handler.handleMessage(null); - } - - @Test - public void handleMessage_ignoreIfSenderIsNotGcm() throws Exception { - Message message = Message.obtain(); - message.what = GooglePlayMessageHandler.MSG_START_EXEC; - Bundle data = new Bundle(); - data.putString(REQUEST_PARAM_TAG, "TAG"); - message.setData(data); - message.replyTo = messengerMock; - doThrow(new SecurityException()).when(appOpsManager) - .checkPackage(message.sendingUid, GooglePlayDriver.BACKEND_PACKAGE); - handler.handleMessage(message); - verify(receiverMock, never()).prepareJob(any(GooglePlayMessengerCallback.class), eq(data)); - } - - @Test - public void handleMessage_startExecution_noData() throws Exception { - Message message = Message.obtain(); - message.what = GooglePlayMessageHandler.MSG_START_EXEC; - message.replyTo = messengerMock; - - handler.handleMessage(message); - verify(receiverMock, never()) - .prepareJob(any(GooglePlayMessengerCallback.class), any(Bundle.class)); - } - - @Test - public void handleMessage_startExecution() throws Exception { - Message message = Message.obtain(); - message.what = GooglePlayMessageHandler.MSG_START_EXEC; - Bundle data = new Bundle(); - data.putString(REQUEST_PARAM_TAG, "TAG"); - message.setData(data); - message.replyTo = messengerMock; - JobInvocation jobInvocation = new Builder() - .setTag("tag") - .setService(TestJobService.class.getName()) - .setTrigger(Trigger.NOW).build(); - when(receiverMock.prepareJob(any(GooglePlayMessengerCallback.class), eq(data))) - .thenReturn(jobInvocation); - - handler.handleMessage(message); - - verify(executionDelegatorMock).executeJob(jobInvocation); - } - - @Test - public void handleMessage_stopExecution() throws Exception { - Message message = Message.obtain(); - message.what = GooglePlayMessageHandler.MSG_STOP_EXEC; - JobCoder jobCoder = GooglePlayReceiver.getJobCoder(); - Bundle data = TestUtil.encodeContentUriJob(TestUtil.getContentUriTrigger(), jobCoder); - JobInvocation jobInvocation = jobCoder.decode(data).build(); - message.setData(data); - message.replyTo = messengerMock; - - handler.handleMessage(message); - - final ArgumentCaptor captor = ArgumentCaptor.forClass(JobInvocation.class); - - verify(executionDelegatorMock).stopJob(captor.capture()); - - TestUtil.assertJobsEqual(jobInvocation, captor.getValue()); - } - - @Test - public void handleMessage_stopExecution_invalidNoCrash() throws Exception { - Message message = Message.obtain(); - message.what = GooglePlayMessageHandler.MSG_STOP_EXEC; - message.replyTo = messengerMock; - - handler.handleMessage(message); - - verify(executionDelegatorMock, never()).stopJob(any(JobInvocation.class)); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessengerCallbackTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessengerCallbackTest.java deleted file mode 100644 index b71cf003..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessengerCallbackTest.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG; -import static org.junit.Assert.assertEquals; - -import android.os.Message; -import android.os.Messenger; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -/** - * Tests {@link GooglePlayMessengerCallback}. - */ -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21) -public class GooglePlayMessengerCallbackTest { - - @Mock - Messenger messengerMock; - GooglePlayMessengerCallback callback; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - callback = new GooglePlayMessengerCallback(messengerMock, "tag"); - } - - @Test - public void jobFinished() throws Exception { - final ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); - - callback.jobFinished(JobService.RESULT_SUCCESS); - - Mockito.verify(messengerMock).send(messageCaptor.capture()); - Message message = messageCaptor.getValue(); - assertEquals(message.what, GooglePlayMessageHandler.MSG_RESULT); - assertEquals(message.arg1, JobService.RESULT_SUCCESS); - assertEquals(message.getData().getString(REQUEST_PARAM_TAG), "tag"); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayReceiverTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayReceiverTest.java deleted file mode 100644 index 47d7da57..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayReceiverTest.java +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.TestUtil.encodeContentUriJob; -import static com.firebase.jobdispatcher.TestUtil.encodeRecurringContentUriJob; -import static com.firebase.jobdispatcher.TestUtil.getContentUriTrigger; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import android.app.Service; -import android.content.Intent; -import android.net.Uri; -import android.os.Binder; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Messenger; -import android.os.Parcel; -import android.provider.ContactsContract; -import android.provider.ContactsContract.Contacts; -import android.provider.MediaStore.Images.Media; -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.GooglePlayReceiverTest.ShadowMessenger; -import com.firebase.jobdispatcher.JobInvocation.Builder; -import com.firebase.jobdispatcher.TestUtil.InspectableBinder; -import com.google.android.gms.gcm.PendingCallback; -import java.util.ArrayList; -import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implements; - -@RunWith(RobolectricTestRunner.class) -@Config( - constants = BuildConfig.class, - manifest = Config.NONE, - sdk = 21, - shadows = {ShadowMessenger.class} -) -public class GooglePlayReceiverTest { - - /** - * The default ShadowMessenger implementation causes NPEs when using the - * {@link Messenger#Messenger(Handler)} constructor. We create our own empty Shadow so we can - * just use the standard Android implementation, which is totally fine. - * - * @see Robolectric issue - * - */ - @Implements(Messenger.class) - public static class ShadowMessenger {} - - GooglePlayReceiver receiver; - - JobCoder jobCoder = new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, true); - - @Mock - Messenger messengerMock; - @Mock - IBinder binderMock; - @Mock - JobCallback callbackMock; - @Mock - ExecutionDelegator executionDelegatorMock; - @Mock - Driver driverMock; - @Captor - ArgumentCaptor jobArgumentCaptor; - - ArrayList triggeredUris = new ArrayList<>(); - - { - triggeredUris.add(ContactsContract.AUTHORITY_URI); - triggeredUris.add(Media.EXTERNAL_CONTENT_URI); - } - - Builder jobInvocationBuilder = new Builder() - .setTag("tag") - .setService(TestJobService.class.getName()) - .setTrigger(Trigger.NOW); - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - receiver = spy(new GooglePlayReceiver()); - when(receiver.getExecutionDelegator()).thenReturn(executionDelegatorMock); - receiver.driver = driverMock; - receiver.validationEnforcer = new ValidationEnforcer(new NoopJobValidator()); - } - - @Test - public void onJobFinished_unknownJobCallbackIsNotPresent_ignoreNoException() { - receiver.onJobFinished(jobInvocationBuilder.build(), JobService.RESULT_SUCCESS); - - verifyZeroInteractions(driverMock); - } - - @Test - public void onJobFinished_notRecurringContentJob_sendResult() { - jobInvocationBuilder.setTrigger( - Trigger.contentUriTrigger(Arrays.asList(new ObservedUri(Contacts.CONTENT_URI, 0)))); - - JobInvocation jobInvocation = receiver - .prepareJob(callbackMock, getBundleForContentJobExecution()); - - receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS); - verify(callbackMock).jobFinished(JobService.RESULT_SUCCESS); - verifyZeroInteractions(driverMock); - } - - @Test - public void onJobFinished_successRecurringContentJob_reschedule() { - JobInvocation jobInvocation = receiver - .prepareJob(callbackMock, getBundleForContentJobExecutionRecurring()); - - receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS); - - verify(driverMock).schedule(jobArgumentCaptor.capture()); - - // No need to callback when job finished. - // Reschedule request is treated as two events: completion of old job and scheduling of new - // job with the same parameters. - verifyZeroInteractions(callbackMock); - - Job rescheduledJob = jobArgumentCaptor.getValue(); - TestUtil.assertJobsEqual(jobInvocation, rescheduledJob); - } - - @Test - public void onJobFinished_failWithRetryRecurringContentJob_sendResult() { - JobInvocation jobInvocation = receiver - .prepareJob(callbackMock, getBundleForContentJobExecutionRecurring()); - - receiver.onJobFinished(jobInvocation, JobService.RESULT_FAIL_RETRY); - - // If a job finishes with RESULT_FAIL_RETRY we don't need to send a reschedule request. - // Rescheduling will erase previously triggered URIs. - verify(callbackMock).jobFinished(JobService.RESULT_FAIL_RETRY); - - verifyZeroInteractions(driverMock); - } - - @Test - public void prepareJob() { - Intent intent = new Intent(); - - Bundle encode = encodeContentUriJob(getContentUriTrigger(), jobCoder); - intent.putExtra(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode); - - Parcel container = Parcel.obtain(); - container.writeStrongBinder(new Binder()); - PendingCallback pcb = new PendingCallback(container); - intent.putExtra("callback", pcb); - - ArrayList uris = new ArrayList<>(); - uris.add(ContactsContract.AUTHORITY_URI); - uris.add(Media.EXTERNAL_CONTENT_URI); - intent.putParcelableArrayListExtra(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, uris); - - JobInvocation jobInvocation = receiver.prepareJob(intent); - assertEquals(jobInvocation.getTriggerReason().getTriggeredContentUris(), uris); - } - - @Test - public void prepareJob_messenger() { - JobInvocation jobInvocation = receiver.prepareJob(callbackMock, new Bundle()); - assertNull(jobInvocation); - verify(callbackMock).jobFinished(JobService.RESULT_FAIL_NORETRY); - } - - @Test - public void prepareJob_messenger_noExtras() { - Bundle bundle = getBundleForContentJobExecution(); - - JobInvocation jobInvocation = receiver.prepareJob(callbackMock, bundle); - assertEquals(jobInvocation.getTriggerReason().getTriggeredContentUris(), triggeredUris); - } - - @NonNull - private Bundle getBundleForContentJobExecution() { - Bundle bundle = new Bundle(); - - Bundle encode = encodeContentUriJob(getContentUriTrigger(), jobCoder); - bundle.putBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode); - - bundle.putParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, triggeredUris); - return bundle; - } - - @NonNull - private Bundle getBundleForContentJobExecutionRecurring() { - Bundle bundle = new Bundle(); - - Bundle encode = encodeRecurringContentUriJob(getContentUriTrigger(), jobCoder); - bundle.putBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode); - - bundle.putParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, triggeredUris); - return bundle; - } - - @Test - public void onBind() { - Intent intent = new Intent(GooglePlayReceiver.ACTION_EXECUTE); - IBinder binderA = receiver.onBind(intent); - IBinder binderB = receiver.onBind(intent); - - assertEquals(binderA, binderB); - } - - @Test - public void onBind_nullIntent() { - IBinder binder = receiver.onBind(null); - assertNull(binder); - } - - @Test - public void onBind_wrongAction() { - Intent intent = new Intent("test"); - IBinder binder = receiver.onBind(intent); - assertNull(binder); - } - - @Test - @Config(sdk = VERSION_CODES.KITKAT) - public void onBind_wrongBuild() { - Intent intent = new Intent(GooglePlayReceiver.ACTION_EXECUTE); - IBinder binder = receiver.onBind(intent); - assertNull(binder); - } - - @Test - public void onStartCommand_nullIntent() { - assertResultWasStartNotSticky(receiver.onStartCommand(null, 0, 101)); - verify(receiver).stopSelf(101); - } - - @Test - public void onStartCommand_initAction() { - Intent initIntent = new Intent("com.google.android.gms.gcm.SERVICE_ACTION_INITIALIZE"); - assertResultWasStartNotSticky(receiver.onStartCommand(initIntent, 0, 101)); - verify(receiver).stopSelf(101); - } - - @Test - public void onStartCommand_unknownAction() { - Intent unknownIntent = new Intent("com.example.foo.bar"); - assertResultWasStartNotSticky(receiver.onStartCommand(unknownIntent, 0, 101)); - assertResultWasStartNotSticky(receiver.onStartCommand(unknownIntent, 0, 102)); - assertResultWasStartNotSticky(receiver.onStartCommand(unknownIntent, 0, 103)); - - InOrder inOrder = inOrder(receiver); - inOrder.verify(receiver).stopSelf(101); - inOrder.verify(receiver).stopSelf(102); - inOrder.verify(receiver).stopSelf(103); - } - - @Test - public void onStartCommand_executeActionWithEmptyExtras() { - Intent execIntent = new Intent("com.google.android.gms.gcm.ACTION_TASK_READY"); - assertResultWasStartNotSticky(receiver.onStartCommand(execIntent, 0, 101)); - verify(receiver).stopSelf(101); - } - - @Test - public void onStartCommand_executeAction() { - JobInvocation job = new JobInvocation.Builder() - .setTag("tag") - .setService("com.example.foo.FooService") - .setTrigger(Trigger.NOW) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .setLifetime(Lifetime.UNTIL_NEXT_BOOT) - .setConstraints(new int[]{Constraint.DEVICE_IDLE}) - .build(); - - Intent execIntent = new Intent("com.google.android.gms.gcm.ACTION_TASK_READY") - .putExtra("extras", new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, true) - .encode(job, new Bundle())) - .putExtra("callback", new InspectableBinder().toPendingCallback()); - - when(executionDelegatorMock.executeJob(any(JobInvocation.class))).thenReturn(true); - - assertResultWasStartNotSticky(receiver.onStartCommand(execIntent, 0, 101)); - - verify(receiver, never()).stopSelf(anyInt()); - verify(executionDelegatorMock).executeJob(any(JobInvocation.class)); - - receiver.onJobFinished(job, JobService.RESULT_SUCCESS); - - verify(receiver).stopSelf(101); - } - - private void assertResultWasStartNotSticky(int result) { - assertEquals( - "Result for onStartCommand wasn't START_NOT_STICKY", Service.START_NOT_STICKY, result); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ImmediateTriggerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ImmediateTriggerTest.java deleted file mode 100644 index 1cf57ee6..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ImmediateTriggerTest.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class ImmediateTriggerTest { - /** - * Code coverage. - */ - @Test - public void testPrivateConstructor() throws Exception { - TestUtil.assertHasSinglePrivateConstructor(JobTrigger.ImmediateTrigger.class); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobBuilderTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobBuilderTest.java deleted file mode 100644 index ffb4026e..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobBuilderTest.java +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class JobBuilderTest { - private static final int[] ALL_LIFETIMES = {Lifetime.UNTIL_NEXT_BOOT, Lifetime.FOREVER}; - - private Job.Builder mBuilder; - - @Before - public void setUp() throws Exception { - mBuilder = TestUtil.getBuilderWithNoopValidator(); - } - - @Test - public void testAddConstraints() { - mBuilder.setConstraints() - .addConstraint(Constraint.DEVICE_CHARGING) - .addConstraint(Constraint.ON_UNMETERED_NETWORK); - - int[] expected = {Constraint.DEVICE_CHARGING, Constraint.ON_UNMETERED_NETWORK}; - - assertEquals(Constraint.compact(expected), Constraint.compact(mBuilder.getConstraints())); - } - - @Test - public void testSetLifetime() { - for (int lifetime : ALL_LIFETIMES) { - mBuilder.setLifetime(lifetime); - assertEquals(lifetime, mBuilder.getLifetime()); - } - } - - @Test - public void testSetShouldReplaceCurrent() { - for (boolean replace : new boolean[]{true, false}) { - mBuilder.setReplaceCurrent(replace); - assertEquals(replace, mBuilder.shouldReplaceCurrent()); - } - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobCoderTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobCoderTest.java deleted file mode 100644 index bd3ebfb9..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobCoderTest.java +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static com.firebase.jobdispatcher.TestUtil.encodeContentUriJob; -import static com.firebase.jobdispatcher.TestUtil.getContentUriTrigger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.MediaStore.Images.Media; -import com.firebase.jobdispatcher.Job.Builder; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import java.util.ArrayList; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class JobCoderTest { - private final JobCoder mCoder = new JobCoder(PREFIX, true); - private static final String PREFIX = "prefix"; - private Builder mBuilder; - - private static Builder setValidBuilderDefaults(Builder mBuilder) { - return mBuilder - .setTag("tag") - .setTrigger(Trigger.NOW) - .setService(TestJobService.class) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL); - } - - @Before - public void setUp() throws Exception { - mBuilder = TestUtil.getBuilderWithNoopValidator(); - } - - @Test - public void testCodingIsLossless() { - for (JobParameters input : TestUtil.getJobCombinations(mBuilder)) { - - JobParameters output = mCoder.decode(mCoder.encode(input, input.getExtras())).build(); - - TestUtil.assertJobsEqual(input, output); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testEncode_throwsOnNullBundle() { - mCoder.encode(mBuilder.build(), null); - } - - @Test(expected = IllegalArgumentException.class) - public void testDecode_throwsOnNullBundle() { - mCoder.decode(null); - } - - @Test - public void testDecode_failsWhenMissingFields() { - assertNull("Expected null tag to cause decoding to fail", - mCoder.decode(mCoder.encode( - setValidBuilderDefaults(mBuilder).setTag(null).build(), - new Bundle()))); - - assertNull("Expected null service to cause decoding to fail", - mCoder.decode(mCoder.encode( - setValidBuilderDefaults(mBuilder).setService(null).build(), - new Bundle()))); - } - - @Test(expected = IllegalArgumentException.class) - public void testDecode_failsUnsupportedTrigger() { - mCoder.decode(mCoder.encode(setValidBuilderDefaults(mBuilder).setTrigger(null).build(), - new Bundle())); - } - - @Test - public void testDecode_ignoresMissingRetryStrategy() { - assertNotNull("Expected null retry strategy to cause decode to use a default", - mCoder.decode(mCoder.encode( - setValidBuilderDefaults(mBuilder).setRetryStrategy(null).build(), - new Bundle()))); - } - - @Test - public void encode_contentUriTrigger() { - Bundle encode = TestUtil.encodeContentUriJob(TestUtil.getContentUriTrigger(), mCoder); - int triggerType = encode.getInt(PREFIX + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE); - assertEquals("Trigger type", BundleProtocol.TRIGGER_TYPE_CONTENT_URI, triggerType); - - String json = encode.getString(PREFIX + BundleProtocol.PACKED_PARAM_OBSERVED_URI); - String expectedJson = "{\"uri_flags\":[1,0],\"uris\":[\"content:\\/\\/com.android.contacts" - + "\",\"content:\\/\\/media\\/external\\/images\\/media\"]}"; - assertEquals("Json trigger", expectedJson, json); - } - - @Test - public void decode_contentUriTrigger() { - ContentUriTrigger contentUriTrigger = TestUtil.getContentUriTrigger(); - Bundle bundle = TestUtil.encodeContentUriJob(contentUriTrigger, mCoder); - JobInvocation decode = mCoder.decode(bundle).build(); - ContentUriTrigger trigger = (ContentUriTrigger) decode.getTrigger(); - assertEquals(contentUriTrigger.getUris(), trigger.getUris()); - } - - @Test - public void decode_addBundleAsExtras() { - ContentUriTrigger contentUriTrigger = TestUtil.getContentUriTrigger(); - Bundle bundle = TestUtil.encodeContentUriJob(contentUriTrigger, mCoder); - bundle.putString("test_key", "test_value"); - JobInvocation decode = mCoder.decode(bundle).build(); - assertEquals("test_value", decode.getExtras().getString("test_key")); - } - - @Test - public void decodeIntentBundle() { - Bundle bundle = new Bundle(); - - ContentUriTrigger uriTrigger = getContentUriTrigger(); - Bundle encode = encodeContentUriJob(uriTrigger, mCoder); - bundle.putBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode); - - ArrayList uris = new ArrayList<>(); - uris.add(ContactsContract.AUTHORITY_URI); - uris.add(Media.EXTERNAL_CONTENT_URI); - bundle.putParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, uris); - - JobInvocation jobInvocation = mCoder.decodeIntentBundle(bundle); - - assertEquals(uris, jobInvocation.getTriggerReason().getTriggeredContentUris()); - assertEquals("TAG", jobInvocation.getTag()); - assertEquals(uriTrigger.getUris(), ((ContentUriTrigger) jobInvocation.getTrigger()) - .getUris()); - assertEquals(TestJobService.class.getName(), jobInvocation.getService()); - assertEquals(RetryStrategy.DEFAULT_EXPONENTIAL.getPolicy(), - jobInvocation.getRetryStrategy().getPolicy()); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobInvocationTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobInvocationTest.java deleted file mode 100644 index 769dd19d..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobInvocationTest.java +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.os.Bundle; -import com.firebase.jobdispatcher.JobInvocation.Builder; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class JobInvocationTest { - private Builder builder; - - @Before - public void setUp() { - builder = new Builder() - .setTag("tag") - .setService(TestJobService.class.getName()) - .setTrigger(Trigger.NOW); - } - - @SuppressWarnings("ConstantConditions") - @Test - public void testShouldReplaceCurrent() throws Exception { - assertTrue("Expected shouldReplaceCurrent() to return value passed in constructor", - builder.setReplaceCurrent(true).build().shouldReplaceCurrent()); - assertFalse("Expected shouldReplaceCurrent() to return value passed in constructor", - builder.setReplaceCurrent(false).build().shouldReplaceCurrent()); - } - - @Test - public void extras() throws Exception { - assertNotNull(builder.build().getExtras()); - - Bundle bundle = new Bundle(); - bundle.putLong("test", 1L); - Bundle extras = builder.addExtras(bundle).build().getExtras(); - assertEquals(1, extras.size()); - assertEquals(1L, extras.getLong("test")); - } - - @Test - public void contract_hashCode_equals() { - JobInvocation jobInvocation = builder.build(); - assertEquals(jobInvocation, builder.build()); - assertEquals(jobInvocation.hashCode(), builder.build().hashCode()); - JobInvocation jobInvocationNew = builder.setTag("new").build(); - assertNotEquals(jobInvocation, jobInvocationNew); - assertNotEquals(jobInvocation.hashCode(), jobInvocationNew.hashCode()); - } - - @Test - public void contract_hashCode_equals_triggerShouldBeIgnored() { - JobInvocation jobInvocation = builder.build(); - JobInvocation periodic = builder.setTrigger(Trigger.executionWindow(0, 1)).build(); - assertEquals(jobInvocation, periodic); - assertEquals(jobInvocation.hashCode(), periodic.hashCode()); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceConnectionTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceConnectionTest.java deleted file mode 100644 index 8a8be89f..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceConnectionTest.java +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.os.IBinder; -import android.os.Message; -import com.firebase.jobdispatcher.JobInvocation.Builder; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -/** - * Test for {@link JobServiceConnection}. - */ -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class JobServiceConnectionTest { - - JobInvocation job = new Builder() - .setTag("tag") - .setService(TestJobService.class.getName()) - .setTrigger(Trigger.NOW) - .build(); - - @Mock - Message messageMock; - @Mock - JobService.LocalBinder binderMock; - @Mock - JobService jobServiceMock; - - JobServiceConnection connection; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(binderMock.getService()).thenReturn(jobServiceMock); - connection = new JobServiceConnection(job, messageMock); - } - - @Test - public void fullConnectionCycle() { - assertFalse(connection.isBound()); - - connection.onServiceConnected(null, binderMock); - verify(jobServiceMock).start(job, messageMock); - assertTrue(connection.isBound()); - - connection.onStop(); - verify(jobServiceMock).stop(job); - assertTrue(connection.isBound()); - - connection.onServiceDisconnected(null); - assertFalse(connection.isBound()); - } - - @Test - public void onServiceConnected_shouldNotSendExecutionRequestTwice() { - assertFalse(connection.isBound()); - - connection.onServiceConnected(null, binderMock); - verify(jobServiceMock).start(job, messageMock); - assertTrue(connection.isBound()); - reset(jobServiceMock); - - connection.onServiceConnected(null, binderMock); - verify(jobServiceMock, never()).start(job, messageMock); // start should not be called again - - connection.onStop(); - verify(jobServiceMock).stop(job); - assertTrue(connection.isBound()); - - connection.onServiceDisconnected(null); - assertFalse(connection.isBound()); - } - - @Test - public void stopOnUnboundConnection() { - assertFalse(connection.isBound()); - connection.onStop(); - verify(jobServiceMock, never()).onStopJob(job); - } - - @Test - public void onServiceConnectedWrongBinder() { - IBinder binder = mock(IBinder.class); - connection.onServiceConnected(null, binder); - assertFalse(connection.isBound()); - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceTest.java deleted file mode 100644 index 83c7134f..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceTest.java +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.content.Intent; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Message; -import android.os.Parcel; -import com.firebase.jobdispatcher.JobInvocation.Builder; -import com.google.android.gms.gcm.PendingCallback; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class JobServiceTest { - private static CountDownLatch countDownLatch; - - @Before - public void setUp() throws Exception {} - - @After - public void tearDown() throws Exception { - countDownLatch = null; - } - - @Test - public void testOnStartCommand_handlesNullIntent() throws Exception { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - try { - service.onStartCommand(null, 0, startId); - - verify(service).stopSelf(startId); - } catch (NullPointerException npe) { - fail("Unexpected NullPointerException after calling onStartCommand with a null Intent."); - } - } - - @Test - public void testOnStartCommand_handlesNullAction() throws Exception { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - Intent nullActionIntent = new Intent(); - service.onStartCommand(nullActionIntent, 0, startId); - - verify(service).stopSelf(startId); - } - - @Test - public void testOnStartCommand_handlesEmptyAction() throws Exception { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - Intent emptyActionIntent = new Intent(""); - service.onStartCommand(emptyActionIntent, 0, startId); - - verify(service).stopSelf(startId); - } - - @Test - public void testOnStartCommand_handlesUnknownAction() throws Exception { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - Intent emptyActionIntent = new Intent("foo.bar.baz"); - service.onStartCommand(emptyActionIntent, 0, startId); - - verify(service).stopSelf(startId); - } - - @Test - public void testOnStartCommand_handlesStartJob_nullData() { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE); - service.onStartCommand(executeJobIntent, 0, startId); - - verify(service).stopSelf(startId); - } - - @Test - public void testOnStartCommand_handlesStartJob_noTag() { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE); - Parcel p = Parcel.obtain(); - p.writeStrongBinder(mock(IBinder.class)); - executeJobIntent.putExtra("callback", new PendingCallback(p)); - - service.onStartCommand(executeJobIntent, 0, startId); - - verify(service).stopSelf(startId); - - p.recycle(); - } - - @Test - public void testOnStartCommand_handlesStartJob_noCallback() { - JobService service = spy(new ExampleJobService()); - int startId = 7; - - Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE); - executeJobIntent.putExtra("tag", "foobar"); - - service.onStartCommand(executeJobIntent, 0, startId); - - verify(service).stopSelf(startId); - } - - @Test - public void testOnStartCommand_handlesStartJob_validRequest() throws InterruptedException { - JobService service = spy(new ExampleJobService()); - - HandlerThread ht = new HandlerThread("handler"); - ht.start(); - Handler h = new Handler(ht.getLooper()); - - Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE); - - Job jobSpec = TestUtil.getBuilderWithNoopValidator() - .setTag("tag") - .setService(ExampleJobService.class) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .setTrigger(Trigger.NOW) - .setLifetime(Lifetime.FOREVER) - .build(); - - countDownLatch = new CountDownLatch(1); - - ((JobService.LocalBinder) service.onBind(executeJobIntent)) - .getService() - .start(jobSpec, h.obtainMessage(ExecutionDelegator.JOB_FINISHED, jobSpec)); - - assertTrue("Expected job to run to completion", countDownLatch.await(5, TimeUnit.SECONDS)); - } - - @Test - public void testOnStartCommand_handlesStartJob_doNotStartRunningJobAgain() { - StoppableJobService service = new StoppableJobService(false); - - Job jobSpec = TestUtil.getBuilderWithNoopValidator() - .setTag("tag") - .setService(StoppableJobService.class) - .setTrigger(Trigger.NOW) - .build(); - - ((JobService.LocalBinder) service.onBind(null)).getService().start(jobSpec, null); - ((JobService.LocalBinder) service.onBind(null)).getService().start(jobSpec, null); - - assertEquals(1, service.getNumberOfExecutionRequestsReceived()); - } - - @Test - public void stop_noCallback_finished() { - JobService service = spy(new StoppableJobService(false)); - JobInvocation job = new Builder() - .setTag("Tag") - .setTrigger(Trigger.NOW) - .setService(StoppableJobService.class.getName()) - .build(); - service.stop(job); - verify(service, never()).onStopJob(job); - } - - @Test - public void stop_withCallback_retry() { - JobService service = spy(new StoppableJobService(false)); - - JobInvocation job = new Builder() - .setTag("Tag") - .setTrigger(Trigger.NOW) - .setService(StoppableJobService.class.getName()) - .build(); - - Handler handlerMock = mock(Handler.class); - Message message = Message.obtain(handlerMock); - service.start(job, message); - - service.stop(job); - verify(service).onStopJob(job); - verify(handlerMock).sendMessage(message); - assertEquals(message.arg1, JobService.RESULT_SUCCESS); - } - - @Test - public void stop_withCallback_done() { - JobService service = spy(new StoppableJobService(true)); - - JobInvocation job = new Builder() - .setTag("Tag") - .setTrigger(Trigger.NOW) - .setService(StoppableJobService.class.getName()) - .build(); - - Handler handlerMock = mock(Handler.class); - Message message = Message.obtain(handlerMock); - service.start(job, message); - - service.stop(job); - verify(service).onStopJob(job); - verify(handlerMock).sendMessage(message); - assertEquals(message.arg1, JobService.RESULT_FAIL_RETRY); - } - - @Test - public void onStartJob_jobFinishedReschedule() { - // Verify that a retry request from within onStartJob will cause the retry result to be sent - // to the bouncer service's handler, regardless of what value is ultimately returned from - // onStartJob. - JobService reschedulingService = new JobService() { - @Override - public boolean onStartJob(JobParameters job) { - // Reschedules job. - jobFinished(job, true /* retry this job */); - return false; - } - - @Override - public boolean onStopJob(JobParameters job) { - return false; - } - }; - - Job jobSpec = TestUtil.getBuilderWithNoopValidator() - .setTag("tag") - .setService(reschedulingService.getClass()) - .setTrigger(Trigger.NOW) - .build(); - Handler mock = mock(Handler.class); - Message message = new Message(); - message.setTarget(mock); - reschedulingService.start(jobSpec, message); - - verify(mock).sendMessage(message); - assertEquals(message.arg1, JobService.RESULT_FAIL_RETRY); - } - - @Test - public void onStartJob_jobFinishedNotReschedule() { - // Verify that a termination request from within onStartJob will cause the result to be sent - // to the bouncer service's handler, regardless of what value is ultimately returned from - // onStartJob. - JobService reschedulingService = new JobService() { - @Override - public boolean onStartJob(JobParameters job) { - jobFinished(job, false /* don't retry this job */); - return false; - } - - @Override - public boolean onStopJob(JobParameters job) { - return false; - } - }; - - Job jobSpec = TestUtil.getBuilderWithNoopValidator() - .setTag("tag") - .setService(reschedulingService.getClass()) - .setTrigger(Trigger.NOW) - .build(); - Handler mock = mock(Handler.class); - Message message = new Message(); - message.setTarget(mock); - reschedulingService.start(jobSpec, message); - - verify(mock).sendMessage(message); - assertEquals(message.arg1, JobService.RESULT_SUCCESS); - } - - public static class ExampleJobService extends JobService { - @Override - public boolean onStartJob(JobParameters job) { - countDownLatch.countDown(); - return false; - } - - @Override - public boolean onStopJob(JobParameters job) { - return false; - } - } - - public static class StoppableJobService extends JobService { - - private final boolean shouldReschedule; - - public int getNumberOfExecutionRequestsReceived() { - return amountOfExecutionRequestReceived.get(); - } - - private final AtomicInteger amountOfExecutionRequestReceived = new AtomicInteger(); - - public StoppableJobService(boolean shouldReschedule) { - this.shouldReschedule = shouldReschedule; - } - - @Override - public boolean onStartJob(JobParameters job) { - amountOfExecutionRequestReceived.incrementAndGet(); - return true; - } - - @Override - public boolean onStopJob(JobParameters job) { - return shouldReschedule; - } - - - } -} diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ValidationEnforcerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ValidationEnforcerTest.java deleted file mode 100644 index 2e4e939a..00000000 --- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ValidationEnforcerTest.java +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Collections; -import java.util.List; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) -public class ValidationEnforcerTest { - private static final List ERROR_LIST = Collections.singletonList("error: foo"); - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Mock - private JobValidator mValidator; - - @Mock - private JobParameters mMockJobParameters; - - @Mock - private JobTrigger mMockTrigger; - - private ValidationEnforcer mEnforcer; - private RetryStrategy mRetryStrategy = RetryStrategy.DEFAULT_EXPONENTIAL; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mEnforcer = new ValidationEnforcer(mValidator); - } - - @Test - public void testValidate_retryStrategy() throws Exception { - mEnforcer.validate(mRetryStrategy); - verify(mValidator).validate(mRetryStrategy); - } - - @Test - public void testValidate_jobSpec() throws Exception { - mEnforcer.validate(mMockJobParameters); - verify(mValidator).validate(mMockJobParameters); - } - - @Test - public void testValidate_trigger() throws Exception { - mEnforcer.validate(mMockTrigger); - verify(mValidator).validate(mMockTrigger); - } - - @Test - public void testIsValid_retryStrategy_invalid() throws Exception { - when(mValidator.validate(mRetryStrategy)) - .thenReturn(Collections.singletonList("error: foo")); - - assertFalse("isValid", mEnforcer.isValid(mRetryStrategy)); - } - - @Test - public void testIsValid_retryStrategy_valid() throws Exception { - when(mValidator.validate(mRetryStrategy)).thenReturn(null); - - assertTrue("isValid", mEnforcer.isValid(mRetryStrategy)); - - } - - @Test - public void testIsValid_trigger_invalid() throws Exception { - when(mValidator.validate(mMockTrigger)) - .thenReturn(Collections.singletonList("error: foo")); - - assertFalse("isValid", mEnforcer.isValid(mMockTrigger)); - } - - @Test - public void testIsValid_trigger_valid() throws Exception { - when(mValidator.validate(mMockTrigger)).thenReturn(null); - - assertTrue("isValid", mEnforcer.isValid(mMockTrigger)); - } - - @Test - public void testIsValid_jobSpec_invalid() throws Exception { - when(mValidator.validate(mMockJobParameters)).thenReturn(ERROR_LIST); - - assertFalse("isValid", mEnforcer.isValid(mMockJobParameters)); - } - - @Test - public void testIsValid_jobSpec_valid() throws Exception { - when(mValidator.validate(mMockJobParameters)).thenReturn(null); - - assertTrue("isValid", mEnforcer.isValid(mMockJobParameters)); - } - - @Test - public void testEnsureValid_retryStrategy_valid() throws Exception { - when(mValidator.validate(mRetryStrategy)).thenReturn(null); - - mEnforcer.ensureValid(mRetryStrategy); - } - - @Test - public void testEnsureValid_trigger_valid() throws Exception { - when(mValidator.validate(mMockTrigger)).thenReturn(null); - - mEnforcer.ensureValid(mMockTrigger); - } - - @Test - public void testEnsureValid_jobSpec_valid() throws Exception { - when(mValidator.validate(mMockJobParameters)).thenReturn(null); - - mEnforcer.ensureValid(mMockJobParameters); - } - - @Test - public void testEnsureValid_retryStrategy_invalid() throws Exception { - expectedException.expect(ValidationEnforcer.ValidationException.class); - - when(mValidator.validate(mRetryStrategy)).thenReturn(ERROR_LIST); - mEnforcer.ensureValid(mRetryStrategy); - } - - @Test - public void testEnsureValid_trigger_invalid() throws Exception { - expectedException.expect(ValidationEnforcer.ValidationException.class); - - when(mValidator.validate(mMockTrigger)).thenReturn(ERROR_LIST); - mEnforcer.ensureValid(mMockTrigger); - } - - @Test - public void testEnsureValid_jobSpec_invalid() throws Exception { - expectedException.expect(ValidationEnforcer.ValidationException.class); - - when(mValidator.validate(mMockJobParameters)).thenReturn(ERROR_LIST); - mEnforcer.ensureValid(mMockJobParameters); - } - - @Test - public void testValidationMessages() throws Exception { - when(mValidator.validate(mMockJobParameters)).thenReturn(ERROR_LIST); - - try { - mEnforcer.ensureValid(mMockJobParameters); - - fail("Expected ensureValid to fail"); - } catch (ValidationEnforcer.ValidationException ve) { - assertEquals("Expected ValidationException to have 1 error message", - 1, - ve.getErrors().size()); - } - } -} diff --git a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/NoopJobValidator.java b/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/NoopJobValidator.java deleted file mode 100644 index 079a6eed..00000000 --- a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/NoopJobValidator.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import android.support.annotation.Nullable; -import java.util.List; - -/** - * A very simple Validator that thinks that everything is ok. Used for testing. - */ -class NoopJobValidator implements JobValidator { - - @Nullable - @Override - public List validate(JobParameters job) { - return null; - } - - @Nullable - @Override - public List validate(JobTrigger trigger) { - return null; - } - - @Nullable - @Override - public List validate(RetryStrategy retryStrategy) { - return null; - } -} diff --git a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestJobService.java b/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestJobService.java deleted file mode 100644 index 77b3604a..00000000 --- a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestJobService.java +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -/** A very simple JobService that can be configured for individual tests. */ -public class TestJobService extends JobService { - - public interface JobServiceProxy { - boolean onStartJob(JobParameters job); - - boolean onStopJob(JobParameters job); - } - - public static final JobServiceProxy NOOP_PROXY = - new JobServiceProxy() { - @Override - public boolean onStartJob(JobParameters job) { - return false; - } - - @Override - public boolean onStopJob(JobParameters job) { - return false; - } - }; - - private static final Object lock = new Object(); - - // GuardedBy("lock") - private static JobServiceProxy currentProxy = NOOP_PROXY; - - public static void setProxy(JobServiceProxy proxy) { - synchronized (lock) { - currentProxy = proxy; - } - } - - public static void reset() { - synchronized (lock) { - currentProxy = NOOP_PROXY; - } - } - - @Override - public boolean onStartJob(JobParameters job) { - synchronized (lock) { - return currentProxy.onStartJob(job); - } - } - - @Override - public boolean onStopJob(JobParameters job) { - synchronized (lock) { - return currentProxy.onStopJob(job); - } - } -} diff --git a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestUtil.java b/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestUtil.java deleted file mode 100644 index 85f9fadc..00000000 --- a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestUtil.java +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.firebase.jobdispatcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.os.Binder; -import android.os.Bundle; -import android.os.Parcel; -import android.provider.ContactsContract; -import android.provider.MediaStore.Images.Media; -import android.support.annotation.NonNull; -import com.firebase.jobdispatcher.Job.Builder; -import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger; -import com.firebase.jobdispatcher.ObservedUri.Flags; -import com.google.android.gms.gcm.PendingCallback; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -/** - * Provides common utilities helpful for testing. - */ -public class TestUtil { - - private static final String TAG = "TAG"; - private static final String[] TAG_COMBINATIONS = {"tag", "foobar", "fooooooo", "bz", "100"}; - - private static final int[] LIFETIME_COMBINATIONS = { - Lifetime.UNTIL_NEXT_BOOT, - Lifetime.FOREVER}; - - private static final JobTrigger[] TRIGGER_COMBINATIONS = { - Trigger.executionWindow(0, 30), - Trigger.executionWindow(300, 600), - Trigger.executionWindow(86400, 86400 * 2), - Trigger.NOW, - Trigger.contentUriTrigger( - Arrays.asList(new ObservedUri(ContactsContract.AUTHORITY_URI, 0))), - Trigger.contentUriTrigger(Arrays.asList(new ObservedUri(ContactsContract.AUTHORITY_URI, 0), - new ObservedUri(ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS))) - }; - - @SuppressWarnings("unchecked") - private static final List> SERVICE_COMBINATIONS = - Arrays.asList(TestJobService.class); - - private static final RetryStrategy[] RETRY_STRATEGY_COMBINATIONS = { - RetryStrategy.DEFAULT_LINEAR, - new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 60, 300), - RetryStrategy.DEFAULT_EXPONENTIAL, - new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 300, 600), - }; - - public static void assertHasSinglePrivateConstructor(Class cls) throws Exception { - Constructor[] constructors = cls.getDeclaredConstructors(); - assertEquals("expected number of constructors to be == 1", 1, constructors.length); - - Constructor constructor = constructors[0]; - assertFalse("expected constructor to be inaccessible", constructor.isAccessible()); - - constructor.setAccessible(true); - constructor.newInstance(); - } - - static List> getAllConstraintCombinations() { - List> combos = new LinkedList<>(); - - combos.add(Collections.emptyList()); - for (Integer cur : Constraint.ALL_CONSTRAINTS) { - for (int l = combos.size() - 1; l >= 0; l--) { - List oldCombo = combos.get(l); - List newCombo = Arrays.asList(new Integer[oldCombo.size() + 1]); - - Collections.copy(newCombo, oldCombo); - newCombo.set(oldCombo.size(), cur); - combos.add(newCombo); - } - combos.add(Collections.singletonList(cur)); - } - - return combos; - } - - static int[] toIntArray(List list) { - int[] input = new int[list.size()]; - for (int i = 0; i < list.size(); i++) { - input[i] = list.get(i); - } - return input; - } - - static List getJobCombinations(Builder builder) { - return getCombination(new JobBuilder(builder)); - } - - static List getJobInvocationCombinations() { - return getCombination(new JobInvocationBuilder()); - } - - private static List getCombination( - JobParameterBuilder buildJobParam) { - - List result = new ArrayList<>(); - for (String tag : TAG_COMBINATIONS) { - for (List constraintList : getAllConstraintCombinations()) { - for (boolean recurring : new boolean[]{true, false}) { - for (boolean replaceCurrent : new boolean[]{true, false}) { - for (int lifetime : LIFETIME_COMBINATIONS) { - for (JobTrigger trigger : TRIGGER_COMBINATIONS) { - for (Class service : SERVICE_COMBINATIONS) { - for (Bundle extras : getBundleCombinations()) { - for (RetryStrategy rs : RETRY_STRATEGY_COMBINATIONS) { - result.add(buildJobParam.build( - tag, - replaceCurrent, - constraintList, - recurring, - lifetime, - trigger, - service, - extras, - rs)); - } - } - } - } - } - } - } - } - } - return result; - } - - private static Bundle[] getBundleCombinations() { - List bundles = new LinkedList<>(); - bundles.add(new Bundle()); - - Bundle b = new Bundle(); - b.putString("foo", "bar"); - b.putInt("bar", 1); - b.putLong("baz", 3L); - bundles.add(b); - - return bundles.toArray(new Bundle[bundles.size()]); - } - - static void assertJobsEqual(JobParameters input, JobParameters output) { - assertNotNull("input", input); - assertNotNull("output", output); - - assertEquals("isRecurring()", input.isRecurring(), output.isRecurring()); - assertEquals("shouldReplaceCurrent()", - input.shouldReplaceCurrent(), - output.shouldReplaceCurrent()); - assertEquals("getLifetime()", input.getLifetime(), output.getLifetime()); - assertEquals("getTag()", input.getTag(), output.getTag()); - assertEquals("getService()", input.getService(), output.getService()); - assertEquals("getConstraints()", - Constraint.compact(input.getConstraints()), - Constraint.compact(output.getConstraints())); - - assertTriggersEqual(input.getTrigger(), output.getTrigger()); - assertBundlesEqual(input.getExtras(), output.getExtras()); - assertRetryStrategiesEqual(input.getRetryStrategy(), output.getRetryStrategy()); - } - - static void assertRetryStrategiesEqual(RetryStrategy in, RetryStrategy out) { - String prefix = "getRetryStrategy()."; - - assertEquals(prefix + "getPolicy()", - in.getPolicy(), out.getPolicy()); - assertEquals(prefix + "getInitialBackoff()", - in.getInitialBackoff(), out.getInitialBackoff()); - assertEquals(prefix + "getMaximumBackoff()", - in.getMaximumBackoff(), out.getMaximumBackoff()); - } - - static void assertBundlesEqual(Bundle inExtras, Bundle outExtras) { - if (inExtras == null || outExtras == null) { - assertNull(inExtras); - assertNull(outExtras); - return; - } - - assertEquals("getExtras().size()", inExtras.size(), outExtras.size()); - final Set inKeys = inExtras.keySet(); - for (String key : inKeys) { - assertTrue("getExtras().containsKey(\"" + key + "\")", outExtras.containsKey(key)); - assertEquals("getExtras().get(\"" + key + "\")", inExtras.get(key), outExtras.get(key)); - } - } - - static void assertTriggersEqual(JobTrigger inTrigger, JobTrigger outTrigger) { - assertEquals("", inTrigger.getClass(), outTrigger.getClass()); - - if (inTrigger instanceof JobTrigger.ExecutionWindowTrigger) { - assertEquals("getTrigger().getWindowStart()", - ((JobTrigger.ExecutionWindowTrigger) inTrigger).getWindowStart(), - ((JobTrigger.ExecutionWindowTrigger) outTrigger).getWindowStart()); - assertEquals("getTrigger().getWindowEnd()", - ((JobTrigger.ExecutionWindowTrigger) inTrigger).getWindowEnd(), - ((JobTrigger.ExecutionWindowTrigger) outTrigger).getWindowEnd()); - } else if (inTrigger == Trigger.NOW) { - assertEquals(inTrigger, outTrigger); - } else if (inTrigger instanceof JobTrigger.ContentUriTrigger) { - assertEquals("Collection of URIs", - ((ContentUriTrigger) inTrigger).getUris(), - ((ContentUriTrigger) outTrigger).getUris()); - } else { - fail("Unknown Trigger class: " + inTrigger.getClass()); - } - } - - @NonNull - public static Builder getBuilderWithNoopValidator() { - return new Builder(new ValidationEnforcer(new NoopJobValidator())); - } - - @NonNull - static Bundle encodeContentUriJob(ContentUriTrigger trigger, JobCoder coder) { - Job job = getBuilderWithNoopValidator() - .setTag(TAG) - .setTrigger(trigger) - .setService(TestJobService.class) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .build(); - return coder.encode(job, new Bundle()); - } - - @NonNull - static Bundle encodeRecurringContentUriJob(ContentUriTrigger trigger, JobCoder coder) { - Job job = getBuilderWithNoopValidator() - .setTag(TAG) - .setTrigger(trigger) - .setService(TestJobService.class) - .setReplaceCurrent(true) - .setRecurring(true) - .build(); - return coder.encode(job, new Bundle()); - } - - static ContentUriTrigger getContentUriTrigger() { - ObservedUri contactUri = new ObservedUri( - ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS); - ObservedUri imageUri = new ObservedUri(Media.EXTERNAL_CONTENT_URI, 0); - return Trigger.contentUriTrigger(Arrays.asList(contactUri, imageUri)); - } - - public static class TransactionArguments { - public final int code; - public final Parcel data; - public final int flags; - - public TransactionArguments(int code, Parcel data, int flags) { - this.code = code; - this.data = data; - this.flags = flags; - } - } - - public static class InspectableBinder extends Binder { - private final List transactionArguments = new LinkedList<>(); - - public InspectableBinder() {} - - @Override - protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) { - transactionArguments.add(new TransactionArguments(code, copyParcel(data), flags)); - return true; - } - - public PendingCallback toPendingCallback() { - Parcel container = Parcel.obtain(); - try { - container.writeStrongBinder(this); - container.setDataPosition(0); - return new PendingCallback(container); - } finally { - container.recycle(); - } - } - - private Parcel copyParcel(Parcel data) { - Parcel clone = Parcel.obtain(); - clone.appendFrom(data, 0, data.dataSize()); - clone.setDataPosition(0); - return clone; - } - - public List getArguments() { - return Collections.unmodifiableList(transactionArguments); - } - } - - private static class JobInvocationBuilder implements - JobParameterBuilder { - - @Override - public JobInvocation build(String tag, boolean replaceCurrent, List constraintList, - boolean recurring, int lifetime, JobTrigger trigger, Class service, - Bundle extras, RetryStrategy rs) { - //noinspection WrongConstant - return new JobInvocation.Builder() - .setTag(tag) - .setReplaceCurrent(replaceCurrent) - .setRecurring(recurring) - .setConstraints(toIntArray(constraintList)) - .setLifetime(lifetime) - .setTrigger(trigger) - .setService(service.getName()) - .addExtras(extras) - .setRetryStrategy(rs) - .build(); - } - } - - private static class JobBuilder implements JobParameterBuilder { - - private final Builder builder; - - public JobBuilder(Builder builder){ - this.builder = builder; - } - - @Override - public Job build(String tag, boolean replaceCurrent, List constraintList, - boolean recurring, int lifetime, JobTrigger trigger, Class service, - Bundle extras, RetryStrategy rs) { - //noinspection WrongConstant - return builder - .setTag(tag) - .setReplaceCurrent(replaceCurrent) - .setRecurring(recurring) - .setConstraints(toIntArray(constraintList)) - .setLifetime(lifetime) - .setTrigger(trigger) - .setService(service) - .setExtras(extras) - .setRetryStrategy(rs) - .build(); - } - } - - private interface JobParameterBuilder { - - T build(String tag, boolean replaceCurrent, List constraintList, boolean recurring, - int lifetime, JobTrigger trigger, Class service, Bundle extras, - RetryStrategy rs); - } -} diff --git a/jobdispatcher/src/testLib/java/com/google/android/gms/gcm/PendingCallback.java b/jobdispatcher/src/testLib/java/com/google/android/gms/gcm/PendingCallback.java deleted file mode 100644 index a12317c8..00000000 --- a/jobdispatcher/src/testLib/java/com/google/android/gms/gcm/PendingCallback.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 Google, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.android.gms.gcm; - -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.Keep; - -/** - * Parcelable class to wrap the binder we send to the client over IPC. Only included for the benefit - * of tests. - */ -@Keep -public final class PendingCallback implements Parcelable { - public static final Creator CREATOR = - new Creator() { - @Override - public PendingCallback createFromParcel(Parcel parcel) { - return new PendingCallback(parcel); - } - - @Override - public PendingCallback[] newArray(int i) { - return new PendingCallback[i]; - } - }; - private final IBinder mBinder; - - public PendingCallback(Parcel in) { - mBinder = in.readStrongBinder(); - } - - public IBinder getIBinder() { - return mBinder; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeStrongBinder(mBinder); - } -} diff --git a/settings.gradle b/settings.gradle index 57087241..e7b4def4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':jobdispatcher' +include ':app'