/* * Copyright 2013-2016 microG Project Team * * 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.app.Service; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.PowerManager; import android.util.Log; import org.microg.gms.common.PublicApi; import org.microg.gms.gcm.GcmConstants; /** * Implemented by the client application to provide an endpoint for the {@link com.google.android.gms.gcm.GcmNetworkManager} * to call back to when a task is ready to be executed. * <p/> * Clients must add this service to their manifest and implement * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}. * This service must provide an {@link IntentFilter} on the action * {@link com.google.android.gms.gcm.GcmTaskService#SERVICE_ACTION_EXECUTE_TASK}. Here's an example: * <pre> * <service android:name="MyTaskService" * android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" * android:exported="true"> * <intent-filter> * <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/> * </intent-filter> * </service> * </pre> * The return value of onRunTask(TaskParams) will determine what the manager does with subsequent * executions of this task. Specifically you can return {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE} * to have this task be re-executed again shortly subject to exponential back-off. Returning * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_FAILURE} for a periodic task will only affect the executing * instance of the task, and future tasks will be executed as normal. * <p/> * Once a task is running it will not be cancelled, however a newly scheduled task with the same * tag will not be executed until the active task has completed. This newly scheduled task will * replace the previous task, regardless of whether the previous task returned * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}. * <p/> * Bear in mind that your service may receive multiple calls from the scheduler at once * (specifically if you've made multiple schedule requests that overlap). If this is the case, your * implementation of {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} must be thread-safe. * <p/> * The scheduler will hold a {@link PowerManager.WakeLock} for your service, however * <strong>after three minutes of execution if your task has not returned it will be considered to * have timed out, and the wakelock will be released.</strong> Rescheduling your task at this point * will have no effect. * If you suspect your task will run longer than this you should start your own service * explicitly or use some other mechanism; this API is intended for relatively quick network * operations. * <p/> * Your task will run at priority Process.THREAD_PRIORITY_BACKGROUND. If this * is not appropriate, you should start your own service with suitably * conditioned threads. */ @PublicApi public abstract class GcmTaskService extends Service { private static final String TAG = "GcmTaskService"; /** * Action broadcast by the GcmNetworkManager to the requesting package when * a scheduled task is ready for execution. */ public static final String SERVICE_ACTION_EXECUTE_TASK = GcmConstants.ACTION_TASK_READY; /** * Action that a {@link com.google.android.gms.gcm.GcmTaskService} is started with when the service needs to initialize * its tasks. */ public static final String SERVICE_ACTION_INITIALIZE = GcmConstants.ACTION_TASK_INITIALZE; /** * You must protect your service with this permission to avoid being bound to by an * application other than Google Play Services. */ public static final String SERVICE_PERMISSION = GcmConstants.PERMISSION_NETWORK_TASK; public IBinder onBind(Intent intent) { return null; } /** * When your package is removed or updated, all of its network tasks are cleared by the * GcmNetworkManager. You can override this method to reschedule them in the case of an * updated package. This is not called when your application is first installed. * <p/> * This is called on your application's main thread. */ public void onInitializeTasks() { // To be overwritten } /** * Override this function to provide the logic for your task execution. * * @param params Parameters provided at schedule time with * {@link com.google.android.gms.gcm.OneoffTask.Builder#setTag(java.lang.String)} * @return One of {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_SUCCESS}, * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}, or * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_FAILURE}. */ public abstract int onRunTask(TaskParams params); /** * Receives the command to begin doing work, for which it spawns another thread. */ public int onStartCommand(Intent intent, int flags, int startId) { intent.setExtrasClassLoader(PendingCallback.class.getClassLoader()); if (SERVICE_ACTION_EXECUTE_TASK.equals(intent.getAction())) { String tag = intent.getStringExtra("tag"); Parcelable callback = intent.getParcelableExtra("callback"); Bundle extras = intent.getBundleExtra("extras"); if (callback == null || !(callback instanceof PendingCallback)) { Log.w(TAG, tag + ": Invalid callback!"); return START_NOT_STICKY; } // TODO ensure single instance // TODO run task in new thread } else if (SERVICE_ACTION_INITIALIZE.equals(intent.getAction())) { this.onInitializeTasks(); // TODO ensure single instance } return START_NOT_STICKY; } }