Update Gradle, push unfinished GCM

This commit is contained in:
Marvin W 2016-04-14 22:06:42 +02:00
parent 493d207ab6
commit 2762887513
27 changed files with 2262 additions and 31 deletions

View File

@ -15,7 +15,16 @@
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
String getMyVersionName() {
def stdout = new ByteArrayOutputStream()
if (rootProject.file("gradlew").exists())
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
else // automatic build system, don't tag dirty
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
return stdout.toString().trim().substring(1)
}
subprojects {
group = 'org.microg'
version = '1.0-SNAPSHOT'
version = getMyVersionName()
}

2
extern/GmsApi vendored

@ -1 +1 @@
Subproject commit f0ec7e606f3e6e3a219eb87266c4dae1828b5083
Subproject commit 6aa110657beec0b3e6c26d1030943e0b97683335

View File

@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip

View File

@ -19,23 +19,26 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.android.tools.build:gradle:2.0.0'
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
defaultConfig {
versionName getMyVersionName()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
}
}
dependencies {
compile 'com.android.support:support-v4:23.0.1'
compile 'com.android.support:support-v4:23.3.0'
compile project(':play-services-api')
}

View File

@ -26,7 +26,7 @@ import android.view.View;
import com.google.android.gms.common.ConnectionResult;
import org.microg.gms.common.Constants;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.common.PublicApi;
import org.microg.gms.common.api.GoogleApiClientImpl;
@ -422,7 +422,7 @@ public interface GoogleApiClient {
* Specify that the default account should be used when connecting to services.
*/
public Builder useDefaultAccount() {
this.accountName = Constants.DEFAULT_ACCOUNT;
this.accountName = AuthConstants.DEFAULT_ACCOUNT;
return this;
}
}

View File

@ -19,17 +19,20 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.android.tools.build:gradle:2.0.0'
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
defaultConfig {
versionName getMyVersionName()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
}

View File

@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.microg.gms.wearable">
package="org.microg.gms.cast">
<uses-sdk android:minSdkVersion="9" />

View File

@ -0,0 +1,43 @@
/*
* Copyright 2013-2015 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.
*/
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
versionName getMyVersionName()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
}
}
dependencies {
compile project(':play-services-base')
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.microg.gms.gcm">
<uses-sdk android:minSdkVersion="9" />
<application />
</manifest>

View File

@ -0,0 +1,195 @@
/*
* 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.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.os.AsyncTaskCompat;
import android.util.Log;
import org.microg.gms.common.PublicApi;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
import static org.microg.gms.gcm.GcmConstants.ACTION_NOTIFICATION_OPEN;
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE;
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_DELETED_MESSAGE;
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_GCM;
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_SEND_ERROR;
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_SEND_EVENT;
/**
* Base class for communicating with Google Cloud Messaging.
* <p/>
* It also provides functionality such as automatically displaying
* <a href="https://developer.android.com/google/gcm/server.html">notifications when requested by app server</a>.
* <p/>
* Override base class methods to handle any events required by the application.
* Methods are invoked asynchronously.
* <p/>
* Include the following in the manifest:
* <pre>
* <service
* android:name=".YourGcmListenerService"
* android:exported="false" >
* <intent-filter>
* <action android:name="com.google.android.c2dm.intent.RECEIVE" />
* </intent-filter>
* </service></pre>
*/
@PublicApi
public abstract class GcmListenerService extends Service {
private static final String TAG = "GcmListenerService";
private final Object lock = new Object();
private int startId;
private int counter = 0;
public final IBinder onBind(Intent intent) {
return null;
}
/**
* Called when GCM server deletes pending messages due to exceeded
* storage limits, for example, when the device cannot be reached
* for an extended period of time.
* <p/>
* It is recommended to retrieve any missing messages directly from the
* app server.
*/
public void onDeletedMessages() {
// To be overwritten
}
/**
* Called when a message is received.
*
* @param from describes message sender.
* @param data message data as String key/value pairs.
*/
public void onMessageReceived(String from, Bundle data) {
// To be overwritten
}
/**
* Called when an upstream message has been successfully sent to the
* GCM connection server.
*
* @param msgId of the upstream message sent using
* {@link com.google.android.gms.gcm.GoogleCloudMessaging#send(java.lang.String, java.lang.String, android.os.Bundle)}.
*/
public void onMessageSent(String msgId) {
// To be overwritten
}
/**
* Called when there was an error sending an upstream message.
*
* @param msgId of the upstream message sent using
* {@link com.google.android.gms.gcm.GoogleCloudMessaging#send(java.lang.String, java.lang.String, android.os.Bundle)}.
* @param error description of the error.
*/
public void onSendError(String msgId, String error) {
// To be overwritten
}
public final int onStartCommand(final Intent intent, int flags, int startId) {
synchronized (lock) {
this.startId = startId;
this.counter++;
}
if (intent != null) {
if (ACTION_NOTIFICATION_OPEN.equals(intent.getAction())) {
handlePendingNotification(intent);
finishCounter();
GcmReceiver.completeWakefulIntent(intent);
} else if (ACTION_C2DM_RECEIVE.equals(intent.getAction())) {
AsyncTaskCompat.executeParallel(new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
handleC2dmMessage(intent);
return null;
}
});
} else {
Log.w(TAG, "Unknown intent action: " + intent.getAction());
}
return START_REDELIVER_INTENT;
} else {
finishCounter();
return START_NOT_STICKY;
}
}
private void handleC2dmMessage(Intent intent) {
try {
String messageType = intent.getStringExtra(EXTRA_MESSAGE_TYPE);
if (messageType == null || MESSAGE_TYPE_GCM.equals(messageType)) {
String from = intent.getStringExtra(EXTRA_FROM);
Bundle data = intent.getExtras();
data.remove(EXTRA_MESSAGE_TYPE);
data.remove("android.support.content.wakelockid"); // WakefulBroadcastReceiver.EXTRA_WAKE_LOCK_ID
data.remove(EXTRA_FROM);
onMessageReceived(from, data);
} else if (MESSAGE_TYPE_DELETED_MESSAGE.equals(messageType)) {
onDeletedMessages();
} else if (MESSAGE_TYPE_SEND_EVENT.equals(messageType)) {
onMessageSent(intent.getStringExtra(EXTRA_MESSAGE_ID));
} else if (MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
onSendError(intent.getStringExtra(EXTRA_MESSAGE_ID), intent.getStringExtra(EXTRA_ERROR));
} else {
Log.w(TAG, "Unknown message type: " + messageType);
}
finishCounter();
} finally {
GcmReceiver.completeWakefulIntent(intent);
}
}
private void handlePendingNotification(Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
if (pendingIntent != null) {
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Notification cancelled", e);
}
} else {
Log.w(TAG, "Notification was null");
}
}
private void finishCounter() {
synchronized (lock) {
this.counter--;
if (counter == 0) {
stopSelfResult(startId);
}
}
}
}

View File

@ -0,0 +1,244 @@
/*
* 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.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.text.TextUtils;
import java.util.List;
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
import static org.microg.gms.gcm.GcmConstants.ACTION_SCHEDULE;
import static org.microg.gms.gcm.GcmConstants.ACTION_TASK_READY;
import static org.microg.gms.gcm.GcmConstants.EXTRA_COMPONENT;
import static org.microg.gms.gcm.GcmConstants.EXTRA_SCHEDULER_ACTION;
import static org.microg.gms.gcm.GcmConstants.EXTRA_TAG;
import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_CANCEL;
import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_CANCEL_ALL;
import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_SCHEDULE;
/**
* Class to create apps with robust "send-to-sync", which is the mechanism to sync data with
* servers where new information is available.
* <p/>
* You can use the API to schedule network-oriented tasks, and let Google Play services batch
* network operations across the system. This greatly simplifies the implementation of common
* patterns, such as waiting for network connectivity, network retries, and backoff.
* <p/>
* Tasks must be scheduled based on an execution window in time. During this execution window
* the scheduler will use its discretion in picking an optimal execution time, based on network
* availability (whether the device has connectivity), network activity (whether packages are
* actively being transferred). and load (how many other pending tasks are available for
* execution at that point in time). <strong>If none of these factors are influential, the
* scheduler will always wait until the end of the specified window.</strong>
* <p/>
* To receive the notification from the scheduler that a task is ready to be executed, your
* client app must implement a {@link com.google.android.gms.gcm.GcmTaskService} and filter
* on the action {@link com.google.android.gms.gcm.GcmTaskService#SERVICE_ACTION_EXECUTE_TASK}.
* <p/>
* Note that tags of arbitrary length are <strong>not</strong> allowed; if the tag you
* provide is greater than 100 characters an exception will be thrown when you try to create your
* {@link com.google.android.gms.gcm.Task} object.
* <p/>
* The service should be protected by the permission
* com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE which is used by Google Play
* Services. This prevents other code from invoking the broadcast receiver. Here is an excerpt from
* a sample manifest:
* <pre>
* <service android:name=".MyUploadService"
* android:exported="true"
* android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" >
* <intent-filter>
* <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
* </intent-filter>
* </service>
* </pre>
* An execution contains the tag identifier which your client app provides. This identifies
* one "task", or piece of work, that you mean to perform. Consider the tag to be the key to which
* your task logic is paired.
* <pre>
* // Schedule a task to occur between five and fifteen seconds from now:
* OneoffTask myTask = new OneoffTask.Builder()
* .setService(MyGcmTaskService.class)
* .setExecutionWindow(
* 5 * DateUtil.MINUTE_IN_SECONDS, 15 * DateUtil.MINUTE_IN_SECONDS)
* .setTag("test-upload")
* .build();
* GcmNetworkManager.get(this).schedule(myTask);
* ...
* // Implement service logic to be notified when the task elapses:
* MyUploadService extends GcmTaskService {
*
* @Override public int onRunTask(TaskParams params) {
* // Do some upload work.
* return GcmNetworkManager.RESULT_SUCCESS;
* }
* }
* </pre>
* To help in debugging your tasks, run
* <code>adb shell dumpsys activity service GcmService --endpoints [...]</code>
* If you want to execute your task immediately (for debugging) you can execute tasks from the
* command line via:
* <code>adb shell am broadcast -a "com.google.android.gms.gcm.ACTION_TRIGGER_TASK" \
* -e component <COMPONENT_NAME> -e tag <TAG></code>
* Where <strong>COMPONENT_NAME</strong>: The full
* {@link ComponentName#flattenToString()} returned for your implementation of
* {@link com.google.android.gms.gcm.GcmTaskService}.
* <strong>TAG</strong>: the tag you want to have land in
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
* Example usage for the gradle target GcmTestProxy service:
* <code>adb shell am broadcast -a "com.google.android.gms.gcm.ACTION_TRIGGER_TASK" \
* -e component "com.google.android.gms.gcm.test.proxy/.internal.nstest.TestNetworkTaskService" \
* -e tag "upload"</code>
* <strong>This is only available if the device is a test-keys build. This will replace any
* previously scheduled task with the same tag!</strong> This will have especially awkward effects
* if your original task was a periodic, because the debug task is scheduled as a one-off.
*/
public class GcmNetworkManager {
/**
* Indicates a task has failed, but not to reschedule.
*/
public static final int RESULT_FAILURE = 2;
/**
* Indicates a task has failed to execute, and must be retried with back-off.
*/
public static final int RESULT_RESCHEDULE = 1;
/**
* Indicates a task has successfully been executed, and can be removed from the queue.
*/
public static final int RESULT_SUCCESS = 0;
private static GcmNetworkManager INSTANCE;
private final Context context;
private GcmNetworkManager(Context context) {
this.context = context;
}
/**
* Cancels all tasks previously scheduled against the provided GcmTaskService. Note that a
* cancel will have no effect on an in-flight task.
*
* @param gcmTaskService The endpoint for which you want to cancel all outstanding tasks.
*/
public void cancelAllTasks(Class<? extends GcmTaskService> gcmTaskService) {
validateService(gcmTaskService.getName());
Intent scheduleIntent = createScheduleIntent();
if (scheduleIntent != null) {
scheduleIntent.putExtra(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_CANCEL_ALL);
scheduleIntent.putExtra(EXTRA_COMPONENT, new ComponentName(context, gcmTaskService));
context.sendBroadcast(scheduleIntent);
}
}
/**
* Cancel a task, specified by tag. Note that a cancel will have no effect on an in-flight
* task.
*
* @param tag The tag to uniquely identify this task on this endpoint.
* @param gcmTaskService The endpoint for which you want to cancel all outstanding tasks.
*/
public void cancelTask(String tag, Class<? extends GcmTaskService> gcmTaskService) {
if (TextUtils.isEmpty(tag) || tag.length() < 100) throw new IllegalArgumentException("tag invalid");
validateService(gcmTaskService.getName());
Intent scheduleIntent = createScheduleIntent();
if (scheduleIntent != null) {
scheduleIntent.putExtra(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_CANCEL);
scheduleIntent.putExtra(EXTRA_TAG, tag);
scheduleIntent.putExtra(EXTRA_COMPONENT, new ComponentName(context, gcmTaskService));
context.sendBroadcast(scheduleIntent);
}
}
/**
* Use this function to access the GcmNetworkManager API.
*
* @param context Context of the calling app.
* @return GcmNetworkManager object.
*/
public static GcmNetworkManager getInstance(Context context) {
synchronized (GcmNetworkManager.class) {
if (INSTANCE == null) {
INSTANCE = new GcmNetworkManager(context);
}
return INSTANCE;
}
}
/**
* Entry point to schedule a task with the network manager.
* <p/>
* If GooglePlayServices is unavailable (upgrading, missing, etc). This call will fail silently.
* You should wrap it in a call to {@link com.google.android.gms.common.GooglePlayServicesUtil#isGooglePlayServicesAvailable(android.content.Context)}</p>
*
* @param task Task constructed using {@link com.google.android.gms.gcm.Task.Builder}. Can be
* an instance of {@link com.google.android.gms.gcm.PeriodicTask} or
* {@link com.google.android.gms.gcm.OneoffTask}.
*/
public void schedule(Task task) {
validateService(task.getServiceName());
Intent scheduleIntent = createScheduleIntent();
if (scheduleIntent != null) {
Bundle extras = scheduleIntent.getExtras();
extras.putString(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_SCHEDULE);
task.toBundle(extras);
scheduleIntent.putExtras(extras);
context.sendBroadcast(scheduleIntent);
}
}
private Intent createScheduleIntent() {
if (!packageExists(GMS_PACKAGE_NAME)) return null;
Intent scheduleIntent = new Intent(ACTION_SCHEDULE);
scheduleIntent.setPackage(GMS_PACKAGE_NAME);
scheduleIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0));
return scheduleIntent;
}
private boolean packageExists(String packageName) {
try {
PackageManager pm = context.getPackageManager();
pm.getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private void validateService(String serviceName) {
if (serviceName == null) throw new NullPointerException("No service provided");
Intent taskIntent = new Intent(ACTION_TASK_READY);
taskIntent.setPackage(context.getPackageName());
PackageManager pm = context.getPackageManager();
List<ResolveInfo> serviceResolves = pm.queryIntentServices(taskIntent, 0);
if (serviceResolves == null || serviceResolves.isEmpty())
throw new IllegalArgumentException("No service found");
for (ResolveInfo info : serviceResolves) {
if (serviceName.equals(info.serviceInfo.name)) return;
}
throw new IllegalArgumentException("Service not supported.");
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import com.google.android.gms.iid.InstanceID;
import java.io.IOException;
import java.util.regex.Pattern;
import static org.microg.gms.gcm.GcmConstants.EXTRA_TOPIC;
/**
* GcmPubSub provides a publish-subscribe model for sending GCM topic messages.
* <p/>
* An app can subscribe to different topics defined by the
* developer. The app server can then send messages to the subscribed devices
* without having to maintain topic-subscribers mapping. Topics do not
* need to be explicitly created before subscribing or publishingthey
* are automatically created when publishing or subscribing.
* <pre>
* String topic = "/topics/myTopic";
* String registrationToken = InstanceID.getInstance(context)
* .getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
* GcmPubSub.getInstance(context).subscribe(registrationToken, topic, null);
* // Messages published to the topic will be received as regular GCM messages
* // with 'from' set to "/topics/myTopic"</pre>
* To publish to a topic, see
* <a href="https://developer.android.com/google/gcm/server.html">GCM server documentation</a>.
*/
public class GcmPubSub {
private static final Pattern topicPattern = Pattern.compile("/topics/[a-zA-Z0-9-_.~%]{1,900}");
private static GcmPubSub INSTANCE;
private final InstanceID instanceId;
public GcmPubSub(Context context) {
this.instanceId = InstanceID.getInstance(context);
}
/**
* Returns an instance of GCM PubSub.
*
* @return GcmPubSub instance
*/
public static synchronized GcmPubSub getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = new GcmPubSub(context);
}
return INSTANCE;
}
/**
* Subscribes an app instance to a topic, enabling it to receive messages
* sent to that topic.
* <p/>
* The topic sender must be authorized to send messages to the
* app instance. To authorize it, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
* with the sender ID and {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE}
* <p/>
* Do not call this function on the main thread.
*
* @param registrationToken {@link com.google.android.gms.iid.InstanceID} token that authorizes topic
* sender to send messages to the app instance.
* @param topic developer defined topic name.
* Must match the following regular expression:
* "/topics/[a-zA-Z0-9-_.~%]{1,900}".
* @param extras (optional) additional information.
* @throws IOException if the request fails.
*/
public void subscribe(String registrationToken, String topic, Bundle extras) throws IOException {
if (TextUtils.isEmpty(registrationToken))
throw new IllegalArgumentException("No registration token!");
if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches())
throw new IllegalArgumentException("Invalid topic!");
extras.putString(EXTRA_TOPIC, topic);
instanceId.getToken(registrationToken, topic, extras);
}
/**
* Unsubscribes an app instance from a topic, stopping it from receiving
* any further messages sent to that topic.
* <p/>
* Do not call this function on the main thread.
*
* @param registrationToken {@link com.google.android.gms.iid.InstanceID} token
* for the same sender and scope that was previously
* used for subscribing to the topic.
* @param topic from which to stop receiving messages.
* @throws IOException if the request fails.
*/
public void unsubscribe(String registrationToken, String topic) throws IOException {
if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches())
throw new IllegalArgumentException("Invalid topic!");
Bundle extras = new Bundle();
extras.putString(EXTRA_TOPIC, topic);
instanceId.deleteToken(registrationToken, topic, extras);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
/**
* <code>WakefulBroadcastReceiver</code> that receives GCM messages and delivers them to an
* application-specific {@link com.google.android.gms.gcm.GcmListenerService} subclass.
* <p/>
* This receiver should be declared in your application's manifest file as follows:
* <p/>
* <pre>
* <receiver
* android:name="com.google.android.gms.gcm.GcmReceiver"
* android:exported="true"
* android:permission="com.google.android.c2dm.permission.SEND" >
* <intent-filter>
* <action android:name="com.google.android.c2dm.intent.RECEIVE" />
* <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
* <category android:name="YOUR_PACKAGE_NAME" />
* </intent-filter>
* </receiver></pre>
* The <code>com.google.android.c2dm.permission.SEND</code> permission is held by Google Play
* services. This prevents other apps from invoking the broadcast receiver.
*/
public class GcmReceiver extends WakefulBroadcastReceiver {
public void onReceive(Context context, Intent intent) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,149 @@
/*
* 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;
}
}

View File

@ -0,0 +1,301 @@
/*
* 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.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Looper;
import android.text.TextUtils;
import com.google.android.gms.iid.InstanceID;
import org.microg.gms.common.PublicApi;
import org.microg.gms.gcm.GcmConstants;
import java.io.IOException;
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND;
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELAY;
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE;
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER_LEGACY;
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO;
import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL;
import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK;
/**
* GoogleCloudMessaging (GCM) enables apps to communicate with their app servers
* using simple messages.
* <p/>
* To send or receive messages, the app must get a
* registrationToken from {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}, which authorizes an
* app server to send messages to an app instance. Pass sender ID and
* {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE} as parameters to the method.
* A sender ID is a project number acquired from the API console, as described in
* <a href="http://developer.android.com/google/gcm/gs.html">Getting Started</a>.
* <p/>
* In order to receive GCM messages, declare {@link com.google.android.gms.gcm.GcmReceiver}
* and an implementation of {@link com.google.android.gms.gcm.GcmListenerService} in the app manifest.
* {@link com.google.android.gms.gcm.GcmReceiver} will pass the incoming messages to the implementation
* of {@link com.google.android.gms.gcm.GcmListenerService}. To process messages, override base class
* methods to handle any events required by the application.
* <p/>
* Client apps can send upstream messages back to the app server using the XMPP-based
* <a href="http://developer.android.com/google/gcm/ccs.html">Cloud Connection Server</a>,
* For example:
* <p/>
* gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data);</pre>
* See <a href="https://developers.google.com/cloud-messaging/android/client">Implementing GCM Client on Android</a> for more details.
*/
@PublicApi
public class GoogleCloudMessaging {
/**
* The GCM {@link com.google.android.gms.gcm.GoogleCloudMessaging#register(java.lang.String...)} and {@link com.google.android.gms.gcm.GoogleCloudMessaging#unregister()} methods are
* blocking. Blocking methods must not be called on the main thread.
*/
public static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
/**
* The device can't read the response, or there was a 500/503 from the
* server that can be retried later. The application should use exponential
* back off and retry.
*/
public static final String ERROR_SERVICE_NOT_AVAILABLE = GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
/**
* Specifies scope used in obtaining GCM registrationToken when calling
* {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
*/
public static final String INSTANCE_ID_SCOPE = GcmConstants.INSTANCE_ID_SCOPE_GCM;
/**
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate that the server deleted
* some pending messages because they exceeded the storage limits. The
* application should contact the server to retrieve the discarded messages.
*
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onDeletedMessages()}
*/
@Deprecated
public static final String MESSAGE_TYPE_DELETED = GcmConstants.MESSAGE_TYPE_DELETED_MESSAGE;
/**
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a regular message.
*
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onMessageReceived(java.lang.String, android.os.Bundle)}
*/
@Deprecated
public static final String MESSAGE_TYPE_MESSAGE = GcmConstants.MESSAGE_TYPE_GCM;
/**
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a send error.
* The intent includes the message ID of the message and an error code.
*
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onSendError(java.lang.String, java.lang.String)}
*/
@Deprecated
public static final String MESSAGE_TYPE_SEND_ERROR = GcmConstants.MESSAGE_TYPE_SEND_ERROR;
/**
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a sent message has been received by the GCM
* server. The intent includes the message ID of the message.
*
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onMessageSent(java.lang.String)}
*/
@Deprecated
public static final String MESSAGE_TYPE_SEND_EVENT = GcmConstants.MESSAGE_TYPE_SEND_EVENT;
private static GoogleCloudMessaging INSTANCE;
/**
* Due to it's nature of being a monitored reference, Intents can be used to authenticate a package source.
*/
private PendingIntent selfAuthIntent;
private Context context;
public GoogleCloudMessaging() {
throw new UnsupportedOperationException();
}
/**
* Must be called when your application is done using GCM, to release
* internal resources.
*/
public void close() {
throw new UnsupportedOperationException();
}
private PendingIntent getSelfAuthIntent() {
if (selfAuthIntent == null) {
Intent intent = new Intent();
intent.setPackage("com.google.example.invalidpackage");
selfAuthIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
}
return selfAuthIntent;
}
/**
* Return the singleton instance of GCM.
*/
public static GoogleCloudMessaging getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = new GoogleCloudMessaging();
INSTANCE.context = context.getApplicationContext();
}
return INSTANCE;
}
/**
* Return the message type from an intent passed into a client app's broadcast receiver. There
* are two general categories of messages passed from the server: regular GCM messages,
* and special GCM status messages.
* <p/>
* The possible types are:
* {@link #MESSAGE_TYPE_MESSAGE}, {@link #MESSAGE_TYPE_DELETED}, {@link #MESSAGE_TYPE_SEND_EVENT} and {@link #MESSAGE_TYPE_SEND_ERROR}
* <p/>
* You can use this method to filter based on message type. Since it is likely that GCM will
* be extended in the future with new message types, just ignore any message types you're not
* interested in, or that you don't recognize.
*
* @return The message type or null if the intent is not a GCM intent
*/
public String getMessageType(Intent intent) throws IOException {
if (intent == null || !ACTION_C2DM_RECEIVE.equals(intent.getAction())) return null;
if (!intent.hasExtra(EXTRA_MESSAGE_TYPE)) return MESSAGE_TYPE_MESSAGE;
return intent.getStringExtra(EXTRA_MESSAGE_TYPE);
}
/**
* Register the application for GCM and return the registration ID. You must call this once,
* when your application is installed, and send the returned registration ID to the server.
* <p/>
* Repeated calls to this method will return the original registration ID.
* <p/>
* If you want to modify the list of senders, you must call <code>unregister()</code> first.
* <p/>
* Most applications use a single sender ID. You may use multiple senders if different
* servers may send messages to the app or for testing.</p>
*
* @param senderIds list of project numbers or Google accounts identifying who is allowed to
* send messages to this application.
* @return registration id
* @throws IOException
* @deprecated Instead, for GCM registration, use
* {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}.
* Set authorizedEntity to a sender ID and scope to {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE}.
*/
@Deprecated
public String register(String... senderIds) throws IOException {
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
if (senderIds == null || senderIds.length == 0) throw new IllegalArgumentException("No sender ids");
StringBuilder sb = new StringBuilder(senderIds[0]);
for (int i = 1; i < senderIds.length; i++) {
sb.append(',').append(senderIds[i]);
}
// This seems to be a legacy variant
// TODO: Implement latest version
Bundle extras = new Bundle();
extras.putString(EXTRA_SENDER_LEGACY, sb.toString());
return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras);
}
/**
* Send an upstream ("device to cloud") message. You can only use the upstream feature
* if your GCM implementation uses the XMPP-based
* <a href="http://developer.android.com/google/gcm/ccs.html">Cloud Connection Server</a>.
* <p/>
* The current limits for max storage time and number of outstanding messages per
* application are documented in the
* <a href="http://developer.android.com/google/gcm/index.html">GCM Developers Guide</a>.</p>
*
* @param to string identifying the receiver of the message in the format of
* <code>SENDER_ID@gcm.googleapis.com</code>. The <code>SENDER_ID</code> should be one of the sender
* IDs used when calling {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
* @param msgId ID of the message. This is generated by the application. It must be
* unique for each message. This allows error callbacks and debugging.
* @param timeToLive If 0, we'll attempt to send immediately and return an
* error if we're not connected. Otherwise, the message will be queued.
* As for server-side messages, we don't return an error if the message has been
* dropped because of TTLthis can happen on the server side, and it would require
* extra communication.
* @param data key/value pairs to be sent. Values must be String, any other type will
* be ignored.
* @throws IllegalArgumentException
* @throws IOException
*/
public void send(String to, String msgId, long timeToLive, Bundle data) throws IOException {
if (TextUtils.isEmpty(to)) throw new IllegalArgumentException("Invalid 'to'");
Intent intent = new Intent(ACTION_GCM_SEND);
intent.setPackage(GMS_PACKAGE_NAME);
if (data != null) intent.putExtras(data);
intent.putExtra(EXTRA_APP, getSelfAuthIntent());
intent.putExtra(EXTRA_SEND_TO, to);
intent.putExtra(EXTRA_MESSAGE_ID, msgId);
intent.putExtra(EXTRA_TTL, timeToLive);
intent.putExtra(EXTRA_DELAY, -1);
//intent.putExtra(EXTRA_SEND_FROM, TODO)
context.sendOrderedBroadcast(intent, PERMISSION_GTALK);
}
/**
* Send an upstream ("device to cloud") message. You can only use the upstream feature
* if your GCM implementation uses the XMPP-based
* <a href="http://developer.android.com/google/gcm/ccs.html">Cloud Connection Server</a>.
* <p/>
* When there is an active connection the message will be sent immediately, otherwise the
* message will be queued for the maximum interval.
*
* @param to string identifying the receiver of the message in the format of
* <code>SENDER_ID@gcm.googleapis.com</code>. The <code>SENDER_ID</code> should be one of the sender
* IDs used when calling {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
* @param msgId ID of the message. This is generated by the application. It must be
* unique for each message. This allows error callbacks and debugging.
* @param data key/value pairs to be sent. Values must be Stringany other type will
* be ignored.
* @throws IllegalArgumentException
* @throws IOException
*/
public void send(String to, String msgId, Bundle data) throws IOException {
send(to, msgId, -1, data);
}
/**
* Unregister the application. Calling <code>unregister()</code> stops any
* messages from the server. This is a blocking callyou shouldn't call
* it from the UI thread.
* <p/>
* You should rarely (if ever) need to call this method. Not only is it
* expensive in terms of resources, but it invalidates all your registration IDs
* returned from register() or subscribe(). This should not be done
* unnecessarily. A better approach is to simply have your server stop
* sending messages.
*
* @throws IOException if we can't connect to server to unregister.
* @deprecated Instead use
* {@link com.google.android.gms.iid.InstanceID#deleteToken(java.lang.String, java.lang.String)} or
* {@link com.google.android.gms.iid.InstanceID#deleteInstanceID()}.
*/
@Deprecated
public void unregister() throws IOException {
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
InstanceID.getInstance(context).deleteInstanceID();
}
}

View File

@ -0,0 +1,221 @@
/*
* 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.os.Bundle;
import android.os.Parcel;
import org.microg.gms.common.PublicApi;
/**
* A task that will execute once,at some point within the specified window.
* If one of {@link com.google.android.gms.gcm.GcmNetworkManager#cancelTask(java.lang.String, java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)} or
* {@link com.google.android.gms.gcm.GcmNetworkManager#cancelAllTasks(java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)} is called before this
* executes it will be cancelled.
* <p/>
* Note that you can request a one-off task to be executed at any point in the future, but to
* prevent abuse the scheduler will only set an alarm at a minimum of 30 seconds in the
* future. Your task can still be run earlier than this if some network event occurs to wake up
* the scheduler.
*/
@PublicApi
public class OneoffTask extends com.google.android.gms.gcm.Task {
private final long windowStart;
private final long windowEnd;
private OneoffTask(Builder builder) {
super(builder);
this.windowStart = builder.windowStart;
this.windowEnd = builder.windowEnd;
}
private OneoffTask(Parcel source) {
super(source);
this.windowStart = source.readLong();
this.windowEnd = source.readLong();
}
/**
* @return The number of seconds from now by which this task must have executed.
*/
public long getWindowEnd() {
return windowEnd;
}
/**
* @return The number of seconds from now at which this task is eligible for execution.
*/
public long getWindowStart() {
return windowStart;
}
/**
* Insert the task object into the provided bundle for IPC. Use #fromBundle to recreate the
* object on the other side.
*/
public void toBundle(Bundle bundle) {
super.toBundle(bundle);
bundle.putLong("window_start", this.windowStart);
bundle.putLong("window_end", this.windowEnd);
}
public String toString() {
return super.toString()
+ " windowStart=" + this.getWindowStart()
+ " windowEnd=" + this.getWindowEnd();
}
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeLong(this.windowStart);
parcel.writeLong(this.windowEnd);
}
public static final Creator<OneoffTask> CREATOR = new Creator<OneoffTask>() {
@Override
public OneoffTask createFromParcel(Parcel source) {
return new OneoffTask(source);
}
@Override
public OneoffTask[] newArray(int size) {
return new OneoffTask[size];
}
};
public static class Builder extends Task.Builder {
private long windowStart = -1;
private long windowEnd = -1;
public Builder() {
this.isPersisted = false;
}
public OneoffTask build() {
return new OneoffTask(this);
}
/**
* Mandatory setter for creating a one-off task. You specify the earliest point in
* time in the future from which your task might start executing, as well as the
* latest point in time in the future at which your task must have executed.
*
* @param windowStartDelaySeconds Earliest point from which your task is eligible to
* run.
* @param windowEndDelaySeconds Latest point at which your task must be run.
*/
public OneoffTask.Builder setExecutionWindow(long windowStartDelaySeconds, long windowEndDelaySeconds) {
this.windowEnd = windowEndDelaySeconds;
this.windowStart = windowStartDelaySeconds;
return this;
}
/**
* Optional setter for specifying any extra parameters necessary for the task.
*/
public OneoffTask.Builder setExtras(Bundle extras) {
this.extras = extras;
return this;
}
/**
* Optional setter to specify whether this task should be persisted across reboots..
* Callers <strong>must</strong> hold the permission
* android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is
* ignored.
*
* @param isPersisted True if this task should be persisted across device reboots.
*/
public OneoffTask.Builder setPersisted(boolean isPersisted) {
this.isPersisted = isPersisted;
return this;
}
/**
* Set the network state your task requires to run. <strong>If the specified network is
* unavailable your task will not be executed until it becomes available.</strong>
* <p/>
* The default for either a periodic or one-off task is
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available
* when your task executes.
* <p/>
* In addition, the only guarantee for connectivity is at the moment of execution - it is
* possible for the device to lose data shortly after your task begins executing.
*/
public OneoffTask.Builder setRequiredNetwork(int requiredNetworkState) {
this.requiredNetworkState = requiredNetworkState;
return this;
}
/**
* Set whether your task requires that the device be connected to power in order to
* execute.
* <p/>
* Use this to defer nonessential operations whenever possible. Note that if you set this
* field and the device is not connected to power <strong>your task will not run</strong>
* until the device is plugged in.
* <p/>
* One way to deal with your task not executing until the constraint is met is to schedule
* another task without the constraints that is subject to some deadline that you can abide.
* This task would be responsible for executing your fallback logic.
*/
public OneoffTask.Builder setRequiresCharging(boolean requiresCharging) {
this.requiresCharging = requiresCharging;
return this;
}
/**
* Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.
*
* @param gcmTaskService Endpoint against which you're scheduling this task.
*/
public OneoffTask.Builder setService(Class<? extends GcmTaskService> gcmTaskService) {
this.gcmTaskService = gcmTaskService.getName();
return this;
}
/**
* Mandatory setter for specifying the tag identifer for this task. This tag will be
* returned at execution time to your endpoint. See
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
* Maximum tag length is 100.<
*
* @param tag String identifier for this task. Consecutive schedule calls for the same
* tag will update any preexisting task with the same tag.
*/
public OneoffTask.Builder setTag(String tag) {
this.tag = tag;
return this;
}
/**
* Optional setter to specify whether this task should override any preexisting tasks
* with the same tag. This defaults to false, which means that a new task will not
* override an existing one.
*
* @param updateCurrent True to update the current task with the parameters of the new.
* Default false.
*/
public OneoffTask.Builder setUpdateCurrent(boolean updateCurrent) {
this.updateCurrent = updateCurrent;
return this;
}
}
}

View File

@ -0,0 +1,235 @@
/*
* 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.os.Bundle;
import android.os.Parcel;
import org.microg.gms.common.PublicApi;
/**
* A periodic task is one that will recur at the specified interval, without needing to be
* rescheduled.
* Schedule a task that will recur until the user calls one of
* {@link com.google.android.gms.gcm.GcmNetworkManager#cancelAllTasks(java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)}, or
* {@link com.google.android.gms.gcm.GcmNetworkManager#cancelTask(java.lang.String, java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)} with
* an identifying tag.
* <p/>
* Periodic tasks will not be scheduled if their period is below a certain minimum
* (currently 30 seconds).
*/
@PublicApi
public class PeriodicTask extends com.google.android.gms.gcm.Task {
protected long mFlexInSeconds;
protected long mIntervalInSeconds;
private PeriodicTask(Builder builder) {
super(builder);
this.mIntervalInSeconds = builder.periodInSeconds;
this.mFlexInSeconds = Math.min(builder.flexInSeconds, mIntervalInSeconds);
}
private PeriodicTask(Parcel source) {
super(source);
mIntervalInSeconds = source.readLong();
mFlexInSeconds = Math.min(source.readLong(), mIntervalInSeconds);
}
/**
* @return The number of seconds before the end of the period returned via
* {@link com.google.android.gms.gcm.PeriodicTask#getPeriod()} that this periodic task can be executed early.
*/
public long getFlex() {
return mFlexInSeconds;
}
/**
* @return The period for this task. The number of seconds between subsequent executions.
*/
public long getPeriod() {
return mIntervalInSeconds;
}
/**
* Insert the task object into the provided bundle for IPC. Use #fromBundle to recreate the
* object on the other side.
*/
public void toBundle(Bundle bundle) {
super.toBundle(bundle);
bundle.putLong("period", this.mIntervalInSeconds);
bundle.putLong("period_flex", this.mFlexInSeconds);
}
public String toString() {
return super.toString() + " period=" + this.getPeriod() + " flex=" + this.getFlex();
}
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeLong(this.mIntervalInSeconds);
parcel.writeLong(this.mFlexInSeconds);
}
public static final Creator<PeriodicTask> CREATOR = new Creator<PeriodicTask>() {
@Override
public PeriodicTask createFromParcel(Parcel source) {
return new PeriodicTask(source);
}
@Override
public PeriodicTask[] newArray(int size) {
return new PeriodicTask[size];
}
};
public static class Builder extends com.google.android.gms.gcm.Task.Builder {
private long flexInSeconds = -1;
private long periodInSeconds = -1;
public Builder() {
isPersisted = true;
}
public PeriodicTask build() {
return new PeriodicTask(this);
}
/**
* Optional setter for specifying any extra parameters necessary for the task.
*/
public PeriodicTask.Builder setExtras(Bundle extras) {
this.extras = extras;
return this;
}
/**
* Optional setter for specifying how close to the end of the period set in
* {@link com.google.android.gms.gcm.PeriodicTask.Builder#setPeriod(long)} you are willing to execute.
* <p/>
* For example, specifying a period of 30 seconds, with a flex value of 10 seconds
* will allow the scheduler to determine the best moment between the 20th and 30th
* second at which to execute your task.
*/
public PeriodicTask.Builder setFlex(long flexInSeconds) {
this.flexInSeconds = flexInSeconds;
return this;
}
/**
* Mandatory setter for creating a periodic task. This specifies that you would like
* this task to recur at most once every <code>mIntervalInSeconds.</code>
* <p/>
* By default you have no control over where within this period the task will execute.
* If you want to restrict the task to run within a certain timeframe from the end of
* the period, use {@link com.google.android.gms.gcm.PeriodicTask.Builder#setFlex(long)}
*/
public PeriodicTask.Builder setPeriod(long periodInSeconds) {
this.periodInSeconds = periodInSeconds;
return this;
}
/**
* Optional setter to specify whether this task should be persisted across reboots. This
* defaults to true for periodic tasks,
* <p/>
* Callers <strong>must</strong> hold the permission
* android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is
* ignored.
*
* @param isPersisted True if this task should be persisted across device reboots.
*/
public PeriodicTask.Builder setPersisted(boolean isPersisted) {
this.isPersisted = isPersisted;
return this;
}
/**
* Set the network state your task requires to run. <strong>If the specified network is
* unavailable your task will not be executed until it becomes available.</strong>
* <p/>
* The default for either a periodic or one-off task is
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available
* when your task executes.
* <p/>
* In addition, the only guarantee for connectivity is at the moment of execution - it is
* possible for the device to lose data shortly after your task begins executing.
*/
public PeriodicTask.Builder setRequiredNetwork(int requiredNetworkState) {
this.requiredNetworkState = requiredNetworkState;
return this;
}
/**
* Set whether your task requires that the device be connected to power in order to
* execute.
* <p/>
* Use this to defer nonessential operations whenever possible. Note that if you set this
* field and the device is not connected to power <strong>your task will not run</strong>
* until the device is plugged in.
* <p/>
* One way to deal with your task not executing until the constraint is met is to schedule
* another task without the constraints that is subject to some deadline that you can abide.
* This task would be responsible for executing your fallback logic.
*/
public PeriodicTask.Builder setRequiresCharging(boolean requiresCharging) {
this.requiresCharging = requiresCharging;
return this;
}
/**
* <p>Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.</p>
*
* @param gcmTaskService Endpoint against which you're scheduling this task.
*/
public PeriodicTask.Builder setService(Class<? extends GcmTaskService> gcmTaskService) {
this.gcmTaskService = gcmTaskService.getName();
return this;
}
/**
* Mandatory setter for specifying the tag identifer for this task. This tag will be
* returned at execution time to your endpoint. See
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
* <p/>
* Maximum tag length is 100.
*
* @param tag String identifier for this task. Consecutive schedule calls for the same
* tag will update any preexisting task with the same tag.
*/
public PeriodicTask.Builder setTag(String tag) {
this.tag = tag;
return this;
}
/**
* Optional setter to specify whether this task should override any preexisting tasks
* with the same tag. This defaults to false, which means that a new task will not
* override an existing one.
*
* @param updateCurrent True to update the current task with the parameters of the new.
* Default false.
*/
public PeriodicTask.Builder setUpdateCurrent(boolean updateCurrent) {
this.updateCurrent = updateCurrent;
return this;
}
}
}

View File

@ -0,0 +1,271 @@
/*
* 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.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import org.microg.gms.common.PublicApi;
/**
* Encapsulates the parameters of a task that you will schedule on the
* {@link com.google.android.gms.gcm.GcmNetworkManager}.
* <p/>
* Construct instances of either {@link com.google.android.gms.gcm.PeriodicTask} or
* {@link com.google.android.gms.gcm.OneoffTask} with the desired parameters/behaviour and
* schedule them using {@link com.google.android.gms.gcm.GcmNetworkManager#schedule(com.google.android.gms.gcm.Task)}.
*/
@PublicApi
public abstract class Task implements Parcelable {
/**
* <p>The maximum size allowed for extras bundle in bytes.
* </p>
*/
public static final int EXTRAS_LIMIT_BYTES = 10240;
/**
* <p>Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)}
* that your task will execute [...] of whether network is available.
* </p>
*/
public static final int NETWORK_STATE_ANY = 2;
/**
* <p>Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)}
* that your task will only execute if [...] sort of data connection is available -
* either metered or unmetered. <strong>This is the default.</strong></p>
*/
public static final int NETWORK_STATE_CONNECTED = 0;
/**
* <p>Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)}
* that your task will only execute if there is an unmetered network connection available.
* </p>
*/
public static final int NETWORK_STATE_UNMETERED = 1;
protected static final long UNINITIALIZED = -1;
private final String serviceName;
private final String tag;
private final boolean updateCurrent;
private final boolean persisted;
private final int requiredNetwork;
private final boolean requiresCharging;
private final Bundle extras;
Task(Builder builder) {
this.serviceName = builder.gcmTaskService;
this.tag = builder.tag;
this.updateCurrent = builder.updateCurrent;
this.persisted = builder.isPersisted;
this.requiredNetwork = builder.requiredNetworkState;
this.requiresCharging = builder.requiresCharging;
this.extras = builder.extras;
}
Task(Parcel in) {
this.serviceName = in.readString();
this.tag = in.readString();
this.updateCurrent = in.readInt() == 1;
this.persisted = in.readInt() == 1;
this.requiredNetwork = NETWORK_STATE_ANY;
this.requiresCharging = false;
this.extras = null;
}
public int describeContents() {
return 0;
}
/**
* @return The extra parameters for the task set by the client.
*/
public Bundle getExtras() {
return extras;
}
/**
* If the specified network is unavailable, your task <strong>will not be run</strong> until
* it is.
*
* @return The network type that this task requires in order to run. See the NETWORK_TYPE_*
* flavours for an explanation of what this value can be.
*/
public int getRequiredNetwork() {
return requiredNetwork;
}
/**
* If the device is not charging and this is set to true, your task <strong>will not be run
* </strong> until it is.
*
* @return Whether or not this task depends on the device being connected to power in order to
* execute.
*/
public boolean getRequiresCharging() {
return requiresCharging;
}
/**
* @return The {@link com.google.android.gms.gcm.GcmTaskService} component that this task
* will execute on.
*/
public String getServiceName() {
return serviceName;
}
/**
* @return The String identifier for this task, that is returned to
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
* when this task executes.
*/
public String getTag() {
return tag;
}
/**
* @return Whether this task will be persisted across devices restarts or Google Play Services
* crashes.
*/
public boolean isPersisted() {
return persisted;
}
/**
* @return Whether or not this task will update a pre-existing task in the scheduler queue.
*/
public boolean isUpdateCurrent() {
return updateCurrent;
}
public void toBundle(Bundle bundle) {
bundle.putString("tag", this.tag);
bundle.putBoolean("update_current", this.updateCurrent);
bundle.putBoolean("persisted", this.persisted);
bundle.putString("service", this.serviceName);
bundle.putInt("requiredNetwork", this.requiredNetwork);
bundle.putBoolean("requiresCharging", this.requiresCharging);
bundle.putBundle("retryStrategy", null); // TODO
bundle.putBundle("extras", this.extras);
}
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(serviceName);
parcel.writeString(tag);
parcel.writeInt(updateCurrent ? 1 : 0);
parcel.writeInt(persisted ? 1 : 0);
}
/**
* <p>Builder object to construct these tasks before sending them to the network manager. Use
* either {@link com.google.android.gms.gcm.PeriodicTask.Builder} or
* {@link com.google.android.gms.gcm.Task.Builder}</p>
*/
public abstract static class Builder {
protected Bundle extras;
protected String gcmTaskService;
protected boolean isPersisted;
protected int requiredNetworkState;
protected boolean requiresCharging;
protected String tag;
protected boolean updateCurrent;
public Builder() {
throw new UnsupportedOperationException();
}
public abstract Task build();
/**
* Optional setter for specifying any extra parameters necessary for the task.
*/
public abstract Task.Builder setExtras(Bundle extras);
/**
* Optional setter to specify whether this task should be persisted across reboots. This
* defaults to true for periodic tasks, and is not supported for one-off tasks.
* <p/>
* Callers <strong>must</strong> hold the permission
* android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is
* ignored.
*
* @param isPersisted True if this task should be persisted across device reboots.
*/
public abstract Task.Builder setPersisted(boolean isPersisted);
/**
* Set the network state your task requires to run. <strong>If the specified network is
* unavailable your task will not be executed until it becomes available.</strong>
* <p/>
* The default for either a periodic or one-off task is
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available
* when your task executes.
* <p/>
* In addition, the only guarantee for connectivity is at the moment of execution - it is
* possible for the device to lose data shortly after your task begins executing.
*/
public abstract Task.Builder setRequiredNetwork(int requiredNetworkState);
/**
* Set whether your task requires that the device be connected to power in order to
* execute.
* <p/>
* Use this to defer nonessential operations whenever possible. Note that if you set this
* field and the device is not connected to power <strong>your task will not run</strong>
* until the device is plugged in.
* <p/>
* One way to deal with your task not executing until the constraint is met is to schedule
* another task without the constraints that is subject to some deadline that you can abide.
* This task would be responsible for executing your fallback logic.
*/
public abstract Task.Builder setRequiresCharging(boolean requiresCharging);
/**
* Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.
*
* @param gcmTaskService Endpoint against which you're scheduling this task.
*/
public abstract Task.Builder setService(Class<? extends GcmTaskService> gcmTaskService);
/**
* Mandatory setter for specifying the tag identifer for this task. This tag will be
* returned at execution time to your endpoint. See
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
* <p/>
* Maximum tag length is 100.
*
* @param tag String identifier for this task. Consecutive schedule calls for the same tag
* will update any preexisting task with the same tag.
*/
public abstract Task.Builder setTag(String tag);
/**
* Optional setter to specify whether this task should override any preexisting tasks with
* the same tag. This defaults to false, which means that a new task will not override an
* existing one.
*
* @param updateCurrent True to update the current task with the parameters of the new.
* Default false.
*/
public abstract Task.Builder setUpdateCurrent(boolean updateCurrent);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.os.Bundle;
import org.microg.gms.common.PublicApi;
/**
* Container of parameters handed off to the client app in
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}.
*/
@PublicApi
public class TaskParams {
private final String tag;
private final Bundle extras;
public TaskParams(String tag) {
this(tag, null);
}
public TaskParams(String tag, Bundle extras) {
this.tag = tag;
this.extras = extras;
}
public Bundle getExtras() {
return extras;
}
public String getTag() {
return tag;
}
}

View File

@ -0,0 +1,166 @@
/*
* 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.iid;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import org.microg.gms.common.PublicApi;
import org.microg.gms.gcm.GcmConstants;
import java.io.IOException;
/**
* Instance ID provides a unique identifier for each app instance and a mechanism
* to authenticate and authorize actions (for example, sending a GCM message).
* <p/>
* Instance ID is stable but may become invalid, if:
* [...]
* If Instance ID has become invalid, the app can call {@link com.google.android.gms.iid.InstanceID#getId()}
* to request a new Instance ID.
* To prove ownership of Instance ID and to allow servers to access data or
* services associated with the app, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}.
*/
public class InstanceID {
/**
* Error returned when failed requests are retried too often. Use
* exponential backoff when retrying requests
*/
public static final String ERROR_BACKOFF = "RETRY_LATER";
/**
* Blocking methods must not be called on the main thread.
*/
public static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
/**
* Tokens can't be generated. Only devices with Google Play are supported.
*/
public static final String ERROR_MISSING_INSTANCEID_SERVICE = "MISSING_INSTANCEID_SERVICE";
/**
* The device cannot read the response, or there was a server error.
* Application should retry the request later using exponential backoff
* and retry (on each subsequent failure increase delay before retrying).
*/
public static final String ERROR_SERVICE_NOT_AVAILABLE = GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
/**
* Timeout waiting for a response.
*/
public static final String ERROR_TIMEOUT = "TIMEOUT";
/**
* Resets Instance ID and revokes all tokens.
*
* @throws IOException
*/
public void deleteInstanceID() throws IOException {
throw new UnsupportedOperationException();
}
/**
* Revokes access to a scope (action) for an entity previously
* authorized by {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}.
* <p/>
* Do not call this function on the main thread.
*
* @param authorizedEntity Entity that must no longer have access.
* @param scope Action that entity is no longer authorized to perform.
* @throws IOException if the request fails.
*/
public void deleteToken(String authorizedEntity, String scope) throws IOException {
deleteToken(authorizedEntity, scope, new Bundle());
}
@PublicApi(exclude = true)
public void deleteToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
throw new UnsupportedOperationException();
}
/**
* Returns time when instance ID was created.
*
* @return Time when instance ID was created (milliseconds since Epoch).
*/
public long getCreationTime() {
throw new UnsupportedOperationException();
}
/**
* Returns a stable identifier that uniquely identifies the app instance.
*
* @return The identifier for the application instance.
*/
public String getId() {
throw new UnsupportedOperationException();
}
/**
* Returns an instance of this class.
*
* @return InstanceID instance.
*/
public static InstanceID getInstance(Context context) {
throw new UnsupportedOperationException();
}
/**
* Returns a token that authorizes an Entity (example: cloud service) to perform
* an action on behalf of the application identified by Instance ID.
* <p/>
* This is similar to an OAuth2 token except, it applies to the
* application instance instead of a user.
* <p/>
* Do not call this function on the main thread.
*
* @param authorizedEntity Entity authorized by the token.
* @param scope Action authorized for authorizedEntity.
* @param extras additional parameters specific to each token scope.
* Bundle keys starting with 'GCM.' and 'GOOGLE.' are
* reserved.
* @return a token that can identify and authorize the instance of the
* application on the device.
* @throws IOException if the request fails.
*/
public String getToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
throw new UnsupportedOperationException();
}
/**
* Returns a token that authorizes an Entity (example: cloud service) to perform
* an action on behalf of the application identified by Instance ID.
* <p/>
* This is similar to an OAuth2 token except, it applies to the
* application instance instead of a user.
* <p/>
* Do not call this function on the main thread.
*
* @param authorizedEntity Entity authorized by the token.
* @param scope Action authorized for authorizedEntity.
* @return a token that can identify and authorize the instance of the
* application on the device.
* @throws IOException if the request fails.
*/
public String getToken(String authorizedEntity, String scope) throws IOException {
return getToken(authorizedEntity, scope, null);
}
}

View File

@ -0,0 +1,138 @@
/*
* 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.iid;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import com.google.android.gms.gcm.GcmReceiver;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT;
/**
* Base class to handle Instance ID service notifications on token
* refresh.
* <p/>
* Any app using Instance ID or GCM must include a class extending
* InstanceIDListenerService and implement {@link com.google.android.gms.iid.InstanceIDListenerService#onTokenRefresh()}.
* <p/>
* Include the following in the manifest:
* <pre>
* <service android:name=".YourInstanceIDListenerService" android:exported="false">
* <intent-filter>
* <action android:name="com.google.android.gms.iid.InstanceID"/>
* </intent-filter>
* </service></pre>
* Do not export this service. Instead, keep it private to prevent other apps
* accessing your service.
*/
public class InstanceIDListenerService extends Service {
private BroadcastReceiver registrationReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
handleIntent(intent);
stop();
}
};
private MessengerCompat messengerCompat = new MessengerCompat(new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
handleIntent((Intent) msg.obj);
}
});
private int counter = 0;
private int startId = -1;
private void handleIntent(Intent intent) {
// TODO
}
public IBinder onBind(Intent intent) {
if (intent != null && ACTION_INSTANCE_ID.equals(intent.getAction())) {
return messengerCompat.getBinder();
}
return null;
}
public void onCreate() {
IntentFilter filter = new IntentFilter(ACTION_C2DM_REGISTRATION);
filter.addCategory(getPackageName());
registerReceiver(registrationReceiver, filter);
}
public void onDestroy() {
unregisterReceiver(registrationReceiver);
}
public int onStartCommand(Intent intent, int flags, int startId) {
synchronized (this) {
this.counter++;
if (startId > this.startId) this.startId = startId;
}
try {
if (intent != null) {
if (ACTION_INSTANCE_ID.equals(intent.getAction()) && intent.hasExtra(EXTRA_GSF_INTENT)) {
startService((Intent) intent.getParcelableExtra(EXTRA_GSF_INTENT));
return START_STICKY;
}
handleIntent(intent);
if (intent.hasExtra(EXTRA_FROM)) GcmReceiver.completeWakefulIntent(intent);
}
} finally {
stop();
}
return START_NOT_STICKY;
}
/**
* Called when the system determines that the tokens need to be refreshed. The application
* should call getToken() and send the tokens to all application servers.
* <p/>
* This will not be called very frequently, it is needed for key rotation and to handle special
* cases.
* <p/>
* The system will throttle the refresh event across all devices to avoid overloading
* application servers with token updates.
*/
public void onTokenRefresh() {
// To be overwritten
}
private void stop() {
synchronized (this) {
counter--;
if (counter <= 0) {
stopSelf(startId);
}
}
}
}

View File

@ -19,17 +19,20 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.android.tools.build:gradle:2.0.0'
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
defaultConfig {
versionName getMyVersionName()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
}

View File

@ -23,11 +23,11 @@ import android.os.Looper;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import org.microg.gms.common.Constants;
import org.microg.gms.location.LocationConstants;
public interface FusedLocationProviderApi {
String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION";
String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION;
String KEY_MOCK_LOCATION = LocationConstants.KEY_MOCK_LOCATION;
Location getLastLocation(GoogleApiClient client);

View File

@ -27,7 +27,7 @@ import com.google.android.gms.location.internal.IGoogleLocationManagerService;
import org.microg.gms.common.Constants;
import org.microg.gms.common.GmsClient;
import org.microg.gms.common.Services;
import org.microg.gms.common.GmsService;
public abstract class GoogleLocationManagerClient extends GmsClient<IGoogleLocationManagerService> {
public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks
@ -37,7 +37,7 @@ public abstract class GoogleLocationManagerClient extends GmsClient<IGoogleLocat
@Override
protected String getActionString() {
return Services.LOCATION_MANAGER.ACTION_LEGACY;
return GmsService.LOCATION_MANAGER.ACTION;
}
@Override

View File

@ -19,17 +19,20 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.android.tools.build:gradle:2.0.0'
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
defaultConfig {
versionName getMyVersionName()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
}

View File

@ -19,17 +19,20 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.android.tools.build:gradle:2.0.0'
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
defaultConfig {
versionName getMyVersionName()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
}
@ -38,6 +41,7 @@ android {
dependencies {
compile project(':play-services-base')
compile project(':play-services-cast')
compile project(':play-services-gcm')
compile project(':play-services-location')
compile project(':play-services-wearable')
}

View File

@ -20,6 +20,7 @@ include ':play-services-api'
include ':play-services-base'
include ':play-services-cast'
include ':play-services-gcm'
include ':play-services-location'
include ':play-services-wearable'