Merge pull request #14 from X1nto/master
changed launcher icon and removed old microg icons
@ -67,7 +67,7 @@ public class ForegroundServiceContext extends ContextWrapper {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private static Notification buildForegroundNotification(Context context) {
|
||||
NotificationChannel channel = new NotificationChannel("foreground-service", "Foreground Service", NotificationManager.IMPORTANCE_NONE);
|
||||
NotificationChannel channel = new NotificationChannel("foreground-service", "Foreground Service", NotificationManager.IMPORTANCE_MIN);
|
||||
channel.setShowBadge(false);
|
||||
channel.setLockscreenVisibility(0);
|
||||
channel.setVibrationPattern(new long[0]);
|
||||
@ -75,9 +75,8 @@ public class ForegroundServiceContext extends ContextWrapper {
|
||||
return new Notification.Builder(context, channel.getId())
|
||||
.setOngoing(true)
|
||||
.setContentTitle("Running in background")
|
||||
.setContentText("This notification ensures, that microG does not get killed.")
|
||||
.setContentText("This notification ensures that microG does not get killed.")
|
||||
.setSmallIcon(R.drawable.ic_foreground_notification)
|
||||
.setColor(0xFFFFFF)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdk()
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
minSdkVersion androidMinSdk()
|
||||
targetSdkVersion androidTargetSdk()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':play-services-base')
|
||||
api project(':play-services-cast-api')
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
POM_NAME=Play Services Cast Library
|
||||
POM_DESCRIPTION=The Play Services Library module to access the Cast API
|
||||
|
||||
POM_PACKAGING=aar
|
||||
|
||||
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||
|
||||
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
|
||||
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
POM_LICENCE_DIST=repo
|
||||
|
||||
POM_DEVELOPER_ID=mar-v-in
|
||||
POM_DEVELOPER_NAME=Marvin W
|
||||
|
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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 package="org.microg.gms.cast"/>
|
@ -1,223 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.cast;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import org.microg.gms.cast.CastApiBuilder;
|
||||
import org.microg.gms.cast.CastApiImpl;
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@PublicApi
|
||||
public final class Cast {
|
||||
|
||||
/**
|
||||
* A constant indicating that the Google Cast device is not the currently active video input.
|
||||
*/
|
||||
public static final int ACTIVE_INPUT_STATE_NO = 0;
|
||||
|
||||
/**
|
||||
* A constant indicating that it is not known (and/or not possible to know) whether the Google Cast device is
|
||||
* the currently active video input. Active input state can only be reported when the Google Cast device is
|
||||
* connected to a TV or AVR with CEC support.
|
||||
*/
|
||||
public static final int ACTIVE_INPUT_STATE_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* A constant indicating that the Google Cast device is the currently active video input.
|
||||
*/
|
||||
public static final int ACTIVE_INPUT_STATE_YES = 1;
|
||||
|
||||
/**
|
||||
* A boolean extra for the connection hint bundle passed to
|
||||
* {@link GoogleApiClient.ConnectionCallbacks#onConnected(Bundle)} that indicates that the connection was
|
||||
* re-established, but the receiver application that was in use at the time of the connection loss is no longer
|
||||
* running on the receiver.
|
||||
*/
|
||||
public static final String EXTRA_APP_NO_LONGER_RUNNING = "com.google.android.gms.cast.EXTRA_APP_NO_LONGER_RUNNING";
|
||||
|
||||
/**
|
||||
* The maximum raw message length (in bytes) that is supported by a Cast channel.
|
||||
*/
|
||||
public static final int MAX_MESSAGE_LENGTH = 65536;
|
||||
|
||||
/**
|
||||
* The maximum length (in characters) of a namespace name.
|
||||
*/
|
||||
public static final int MAX_NAMESPACE_LENGTH = 128;
|
||||
|
||||
/**
|
||||
* A constant indicating that the Google Cast device is not currently in standby.
|
||||
*/
|
||||
public static final int STANDBY_STATE_NO = 0;
|
||||
|
||||
/**
|
||||
* A constant indicating that it is not known (and/or not possible to know) whether the Google Cast device is
|
||||
* currently in standby. Standby state can only be reported when the Google Cast device is connected to a TV or
|
||||
* AVR with CEC support.
|
||||
*/
|
||||
public static final int STANDBY_STATE_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* A constant indicating that the Google Cast device is currently in standby.
|
||||
*/
|
||||
public static final int STANDBY_STATE_YES = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Cast features.
|
||||
*/
|
||||
public static final Api<CastOptions> API = new Api<CastOptions>(new CastApiBuilder());
|
||||
|
||||
/**
|
||||
* An implementation of the CastApi interface. The interface is used to interact with a cast device.
|
||||
*/
|
||||
public static final Cast.CastApi CastApi = new CastApiImpl();
|
||||
|
||||
private Cast() {
|
||||
}
|
||||
|
||||
public interface ApplicationConnectionResult extends Result {
|
||||
ApplicationMetadata getApplicationMetadata();
|
||||
|
||||
String getApplicationStatus();
|
||||
|
||||
String getSessionId();
|
||||
|
||||
boolean getWasLaunched();
|
||||
}
|
||||
|
||||
public interface CastApi {
|
||||
int getActiveInputState(GoogleApiClient client);
|
||||
|
||||
ApplicationMetadata getApplicationMetadata(GoogleApiClient client);
|
||||
|
||||
String getApplicationStatus(GoogleApiClient client);
|
||||
|
||||
int getStandbyState(GoogleApiClient client);
|
||||
|
||||
double getVolume(GoogleApiClient client);
|
||||
|
||||
boolean isMute(GoogleApiClient client);
|
||||
|
||||
PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client);
|
||||
|
||||
PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId, String sessionId);
|
||||
|
||||
PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId);
|
||||
|
||||
PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, LaunchOptions launchOptions);
|
||||
|
||||
PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId);
|
||||
|
||||
@Deprecated
|
||||
PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, boolean relaunchIfRunning);
|
||||
|
||||
PendingResult<Status> leaveApplication(GoogleApiClient client);
|
||||
|
||||
void removeMessageReceivedCallbacks(GoogleApiClient client, String namespace) throws IOException;
|
||||
|
||||
void requestStatus(GoogleApiClient client) throws IOException;
|
||||
|
||||
PendingResult<Status> sendMessage(GoogleApiClient client, String namespace, String message);
|
||||
|
||||
void setMessageReceivedCallbacks(GoogleApiClient client, String namespace, Cast.MessageReceivedCallback callbacks) throws IOException;
|
||||
|
||||
void setMute(GoogleApiClient client, boolean mute) throws IOException;
|
||||
|
||||
void setVolume(GoogleApiClient client, double volume) throws IOException;
|
||||
|
||||
PendingResult<Status> stopApplication(GoogleApiClient client);
|
||||
|
||||
PendingResult<Status> stopApplication(GoogleApiClient client, String sessionId);
|
||||
}
|
||||
|
||||
public static class CastOptions implements Api.ApiOptions.HasOptions {
|
||||
private final CastDevice castDevice;
|
||||
private final Listener castListener;
|
||||
private final boolean verboseLoggingEnabled;
|
||||
|
||||
public CastOptions(CastDevice castDevice, Listener castListener, boolean verboseLoggingEnabled) {
|
||||
this.castDevice = castDevice;
|
||||
this.castListener = castListener;
|
||||
this.verboseLoggingEnabled = verboseLoggingEnabled;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Builder builder(CastDevice castDevice, Listener castListener) {
|
||||
return new Builder(castDevice, castListener);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final CastDevice castDevice;
|
||||
private final Listener castListener;
|
||||
private boolean verboseLoggingEnabled;
|
||||
|
||||
public Builder(CastDevice castDevice, Listener castListener) {
|
||||
this.castDevice = castDevice;
|
||||
this.castListener = castListener;
|
||||
}
|
||||
|
||||
public CastOptions build() {
|
||||
return new CastOptions(castDevice, castListener, verboseLoggingEnabled);
|
||||
}
|
||||
|
||||
public Builder setVerboseLoggingEnabled(boolean verboseLoggingEnabled) {
|
||||
this.verboseLoggingEnabled = verboseLoggingEnabled;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Listener {
|
||||
public void onActiveInputStateChanged(int activeInputState) {
|
||||
|
||||
}
|
||||
|
||||
public void onApplicationDisconnected(int statusCode) {
|
||||
|
||||
}
|
||||
|
||||
public void onApplicationMetadataChanged(ApplicationMetadata applicationMetadata) {
|
||||
|
||||
}
|
||||
|
||||
public void onApplicationStatusChanged() {
|
||||
|
||||
}
|
||||
|
||||
public void onStandbyStateChanged(int standbyState) {
|
||||
|
||||
}
|
||||
|
||||
public void onVolumeChanged() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public interface MessageReceivedCallback {
|
||||
void onMessageReceived(CastDevice castDevice, String namespace, String message);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.cast;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Presentation;
|
||||
import android.content.Context;
|
||||
import android.view.Display;
|
||||
|
||||
@TargetApi(17)
|
||||
public class CastPresentation extends Presentation {
|
||||
public CastPresentation(Context outerContext, Display display) {
|
||||
super(outerContext, display);
|
||||
}
|
||||
|
||||
public CastPresentation(Context outerContext, Display display, int theme) {
|
||||
super(outerContext, display, theme);
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.cast;
|
||||
|
||||
import android.view.Display;
|
||||
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import org.microg.gms.cast.CastRemoteDisplayApiBuilder;
|
||||
import org.microg.gms.cast.CastRemoteDisplayApiImpl;
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
@PublicApi
|
||||
public final class CastRemoteDisplay {
|
||||
/**
|
||||
* Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the CastRemoteDisplay features.
|
||||
*/
|
||||
public static final Api<CastRemoteDisplayOptions> API = new Api<CastRemoteDisplayOptions>(new CastRemoteDisplayApiBuilder());
|
||||
|
||||
/**
|
||||
* An implementation of the CastRemoteDisplayAPI interface. The interface is used to interact with a cast device.
|
||||
*/
|
||||
public static final CastRemoteDisplayApi CastApi = new CastRemoteDisplayApiImpl();
|
||||
|
||||
private CastRemoteDisplay() {
|
||||
}
|
||||
|
||||
public static final class CastRemoteDisplayOptions implements Api.ApiOptions.HasOptions {
|
||||
private CastDevice castDevice;
|
||||
private CastRemoteDisplaySessionCallbacks callbacks;
|
||||
|
||||
private CastRemoteDisplayOptions(CastDevice castDevice, CastRemoteDisplaySessionCallbacks callbacks) {
|
||||
this.castDevice = castDevice;
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private CastDevice castDevice;
|
||||
private CastRemoteDisplaySessionCallbacks callbacks;
|
||||
|
||||
public Builder(CastDevice castDevice, CastRemoteDisplaySessionCallbacks callbacks) {
|
||||
this.castDevice = castDevice;
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
public CastRemoteDisplayOptions build() {
|
||||
return new CastRemoteDisplayOptions(castDevice, callbacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface CastRemoteDisplaySessionCallbacks {
|
||||
void onRemoteDisplayEnded(Status status);
|
||||
}
|
||||
|
||||
public interface CastRemoteDisplaySessionResult extends Result {
|
||||
Display getPresentationDisplay();
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.cast;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
|
||||
public interface CastRemoteDisplayApi {
|
||||
PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> startRemoteDisplay(GoogleApiClient apiClient, String applicationId);
|
||||
|
||||
PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> stopRemoteDisplay(GoogleApiClient apiClient);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.cast;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class CastRemoteDisplayLocalService extends Service {
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.cast;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.google.android.gms.cast.Cast;
|
||||
import com.google.android.gms.common.api.AccountInfo;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
|
||||
import org.microg.gms.common.api.ApiBuilder;
|
||||
import org.microg.gms.common.api.ApiConnection;
|
||||
|
||||
public class CastApiBuilder implements ApiBuilder<Cast.CastOptions>{
|
||||
@Override
|
||||
public ApiConnection build(Context context, Looper looper, Cast.CastOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||
return new CastClientImpl(context, options, callbacks, connectionFailedListener);
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.cast;
|
||||
|
||||
import com.google.android.gms.cast.ApplicationMetadata;
|
||||
import com.google.android.gms.cast.Cast;
|
||||
import com.google.android.gms.cast.LaunchOptions;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
// TODO
|
||||
public class CastApiImpl implements Cast.CastApi {
|
||||
@Override
|
||||
public int getActiveInputState(GoogleApiClient client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationMetadata getApplicationMetadata(GoogleApiClient client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApplicationStatus(GoogleApiClient client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStandbyState(GoogleApiClient client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getVolume(GoogleApiClient client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMute(GoogleApiClient client) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId, String sessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, LaunchOptions launchOptions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, boolean relaunchIfRunning) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Status> leaveApplication(GoogleApiClient client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMessageReceivedCallbacks(GoogleApiClient client, String namespace) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestStatus(GoogleApiClient client) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Status> sendMessage(GoogleApiClient client, String namespace, String message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageReceivedCallbacks(GoogleApiClient client, String namespace, Cast.MessageReceivedCallback callbacks) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMute(GoogleApiClient client, boolean mute) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(GoogleApiClient client, double volume) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Status> stopApplication(GoogleApiClient client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<Status> stopApplication(GoogleApiClient client, String sessionId) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.cast;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.android.gms.cast.Cast;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
|
||||
import org.microg.gms.common.DummyApiConnection;
|
||||
|
||||
public class CastClientImpl extends DummyApiConnection {
|
||||
public CastClientImpl(Context context, Cast.CastOptions options, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.cast;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.google.android.gms.cast.CastRemoteDisplay;
|
||||
import com.google.android.gms.common.api.AccountInfo;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
|
||||
import org.microg.gms.common.DummyApiConnection;
|
||||
import org.microg.gms.common.api.ApiBuilder;
|
||||
import org.microg.gms.common.api.ApiConnection;
|
||||
|
||||
public class CastRemoteDisplayApiBuilder implements ApiBuilder<CastRemoteDisplay.CastRemoteDisplayOptions> {
|
||||
@Override
|
||||
public ApiConnection build(Context context, Looper looper, CastRemoteDisplay.CastRemoteDisplayOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||
return new DummyApiConnection();
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.cast;
|
||||
|
||||
import com.google.android.gms.cast.CastRemoteDisplay;
|
||||
import com.google.android.gms.cast.CastRemoteDisplayApi;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
|
||||
public class CastRemoteDisplayApiImpl implements CastRemoteDisplayApi {
|
||||
@Override
|
||||
public PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> startRemoteDisplay(GoogleApiClient apiClient, String applicationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> stopRemoteDisplay(GoogleApiClient apiClient) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -34,7 +34,8 @@
|
||||
|
||||
<permission-tree
|
||||
android:name="com.mgoogle.android.googleapps.permission.GOOGLE_AUTH"
|
||||
android:icon="@mipmap/ic_launcher" />
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Vanced microG"/>
|
||||
|
||||
<permission
|
||||
android:name="org.mgoogle.gms.STATUS_BROADCAST"
|
||||
@ -78,7 +79,7 @@
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Vanced MicroG">
|
||||
android:label="Vanced microG">
|
||||
<meta-data
|
||||
android:name="fake-signature"
|
||||
android:value="@string/fake_signature" />
|
||||
@ -276,7 +277,7 @@
|
||||
<!-- microG Settings shown in Launcher -->
|
||||
<activity
|
||||
android:name="org.microg.gms.ui.SettingsActivity"
|
||||
android:icon="@mipmap/ic_microg_settings"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/gms_settings_name"
|
||||
android:roundIcon="@mipmap/ic_microg_settings"
|
||||
android:theme="@style/Theme.AppCompat.DayNight">
|
||||
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 17 KiB |
@ -19,7 +19,6 @@
|
||||
<string name="gms_settings_summary">Vanced microG services einrichten.</string>
|
||||
|
||||
<string name="just_a_sec">Einen kurzen Moment…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Durch Fortsetzen erlaubst du dieser App und Google deine Informationen nach ihren entsprechenden AGB und Datenschutzrichtlinien zu nutzen.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> versucht:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> versucht zu nutzen:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Ajustes de Vanced microG</string>
|
||||
|
||||
<string name="just_a_sec">Un momento…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Al continuar, permitirás a esta aplicación y a Google usar tu información de acuerdo con sus respectivos términos de servicio y política de privacidad.</string>
|
||||
<string name="ask_scope_permission_title">%1$s quiere:</string>
|
||||
<string name="ask_service_permission_title">%1$s quiere usar:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Paramètres de Vanced microG</string>
|
||||
|
||||
<string name="just_a_sec">Juste une seconde…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">En poursuivant, vous autorisez cette application et Google à utiliser vos informations en accord avec leurs termes de service et politiques de vie privée respectifs.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> voudrait :</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> voudrait utiliser :</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Ustawienia Vanced microG</string>
|
||||
|
||||
<string name="just_a_sec">Chwileczkę…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Kontynuując, zgadzasz się na wykorzystywanie swoich danych przez Google oraz usługi microG zgodnie z warunkami korzystania oraz polityką prywatności Google.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> chce:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> chce wykorzystać:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Configurações Vanced microG</string>
|
||||
|
||||
<string name="just_a_sec">Aguarde um momento…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Ao continuar, você permite que este aplicativo e o Google usem suas informações de acordo com seus respectivos termos de serviço e políticas de privacidade.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> gostaria de:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> gostaria de usar:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Setări Vanced microG</string>
|
||||
|
||||
<string name="just_a_sec">Doar o secundă…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Continuând, permiteți aceastei aplicații și Google să utilizeze informațiile dvs. în conformitate cu condiţiile de utilizare şi politicile de confidențialitate ale lor.</string>
|
||||
<string name="ask_scope_permission_title">%s ar dori să:</string>
|
||||
<string name="ask_service_permission_title">%s ar dori să utilizeze:</string>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="gms_settings_summary">Конфигурирование Vanced microG.</string>
|
||||
|
||||
<string name="just_a_sec">Пожалуйста, подождите…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Продолжая, вы позволяете этому приложению и Google использовать свою информацию в соответствии с их соответствующими условиями предоставления услуг и политиками конфиденциальности.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> хотел бы:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> хотел бы использовать:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Поставке Vanced микроГ</string>
|
||||
|
||||
<string name="just_a_sec">Само тренутак…</string>
|
||||
<string name="google_account_label">Гугл</string>
|
||||
<string name="ask_permission_tos">Ако наставите дозволићете овој апликацији и Гуглу да користе ваше податке у складу са њиховим условима коришћења и политикама приватности.</string>
|
||||
<string name="ask_scope_permission_title">%s жели да:</string>
|
||||
<string name="ask_service_permission_title">%s жели да користи:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Налаштування Vanced microG</string>
|
||||
|
||||
<string name="just_a_sec">Зачекайте трохи…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">Продовжуючи, ви дозволяєте даному додатку та Google використовувати ваші дані згідно з їх відповідними умовами надання послуг та політикою конфіденційності.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> бажав би:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> бажав би використовувати:</string>
|
||||
|
@ -18,7 +18,6 @@
|
||||
<string name="gms_settings_name">Vanced microG設定</string>
|
||||
|
||||
<string name="just_a_sec">請稍等…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">當您繼續下一步時,代表您同意Google和本程式在遵守服務條款和隱私權政策下使用您的個人資訊。</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g>將會:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g>將會使用:</string>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="gms_settings_summary">Setup Vanced microG.</string>
|
||||
|
||||
<string name="just_a_sec">Just a sec…</string>
|
||||
<string name="google_account_label">Google</string>
|
||||
<string name="ask_permission_tos">By continuing, you allow this app and Google to use your information in accordance with their respective terms of service and privacy policies.</string>
|
||||
<string name="ask_scope_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> would like to:</string>
|
||||
<string name="ask_service_permission_title"><xliff:g example="F-Droid">%1$s</xliff:g> would like to use:</string>
|
||||
|
@ -14,10 +14,11 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<account-authenticator
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountPreferences="@xml/preferences_auth"
|
||||
android:accountType="com.mgoogle"
|
||||
android:customTokens="true"
|
||||
android:icon="@drawable/ic_microg"
|
||||
android:smallIcon="@drawable/ic_microg"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:smallIcon="@mipmap/ic_launcher"
|
||||
android:label="Vanced microG" />
|
||||
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdk()
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
minSdkVersion androidMinSdk()
|
||||
targetSdkVersion androidTargetSdk()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':play-services-iid')
|
||||
// compile project(':play-services-measurement')
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
POM_NAME=Play Services GCM Library
|
||||
POM_DESCRIPTION=The Play Services Library module to access Google Cloud Messaging
|
||||
|
||||
POM_PACKAGING=aar
|
||||
|
||||
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||
|
||||
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
|
||||
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
POM_LICENCE_DIST=repo
|
||||
|
||||
POM_DEVELOPER_ID=mar-v-in
|
||||
POM_DEVELOPER_NAME=Marvin W
|
||||
|
@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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 package="org.microg.gms.gcm"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Permissions required for GCM -->
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<!-- Move to play-services-measurement -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
</manifest>
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.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())) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
handleC2dmMessage(intent);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.");
|
||||
}
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 publishing—they
|
||||
* 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: " + topic);
|
||||
|
||||
if (extras == null) extras = new Bundle();
|
||||
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: " + topic);
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_TOPIC, topic);
|
||||
instanceId.deleteToken(registrationToken, topic, extras);
|
||||
}
|
||||
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.Manifest;
|
||||
import android.content.ComponentName;
|
||||
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.Build;
|
||||
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
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_RAWDATA;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA_BASE64;
|
||||
import static org.microg.gms.gcm.GcmConstants.GCMID_INSTANCE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.GCMID_REFRESH;
|
||||
|
||||
/**
|
||||
* <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 {
|
||||
private static final String TAG = "GcmReceiver";
|
||||
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
sanitizeIntent(context, intent);
|
||||
enforceIntentClassName(context, intent);
|
||||
sendIntent(context, intent);
|
||||
if (getResultCode() == 0) setResultCodeIfOrdered(-1);
|
||||
}
|
||||
|
||||
private void sanitizeIntent(Context context, Intent intent) {
|
||||
intent.setComponent(null);
|
||||
intent.setPackage(context.getPackageName());
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
intent.removeCategory(context.getPackageName());
|
||||
}
|
||||
String from = intent.getStringExtra(EXTRA_FROM);
|
||||
if (ACTION_C2DM_REGISTRATION.equals(intent.getAction()) || GCMID_INSTANCE_ID.equals(from) || GCMID_REFRESH.equals(from)) {
|
||||
intent.setAction(ACTION_INSTANCE_ID);
|
||||
}
|
||||
String base64encoded = intent.getStringExtra(EXTRA_RAWDATA_BASE64);
|
||||
if (base64encoded != null) {
|
||||
intent.putExtra(EXTRA_RAWDATA, Base64.decode(base64encoded, Base64.DEFAULT));
|
||||
intent.removeExtra(EXTRA_RAWDATA_BASE64);
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceIntentClassName(Context context, Intent intent) {
|
||||
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);
|
||||
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
|
||||
Log.e(TAG, "Failed to resolve target intent service, skipping classname enforcement");
|
||||
return;
|
||||
}
|
||||
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
if (!context.getPackageName().equals(serviceInfo.packageName) || serviceInfo.name == null) {
|
||||
Log.e(TAG, "Error resolving target intent service, skipping classname enforcement. Resolved service was: " + serviceInfo.packageName + "/" + serviceInfo.name);
|
||||
return;
|
||||
}
|
||||
intent.setClassName(context, serviceInfo.name.startsWith(".") ? (context.getPackageName() + serviceInfo.name) : serviceInfo.name);
|
||||
}
|
||||
|
||||
private void sendIntent(Context context, Intent intent) {
|
||||
setResultCodeIfOrdered(500);
|
||||
try {
|
||||
ComponentName startedComponent;
|
||||
if (context.checkCallingOrSelfPermission(Manifest.permission.WAKE_LOCK) == PackageManager.PERMISSION_GRANTED) {
|
||||
startedComponent = startWakefulService(context, intent);
|
||||
} else {
|
||||
Log.d(TAG, "Missing wake lock permission, service start may be delayed");
|
||||
startedComponent = context.startService(intent);
|
||||
}
|
||||
if (startedComponent == null) {
|
||||
Log.e(TAG, "Error while delivering the message: ServiceIntent not found.");
|
||||
setResultCodeIfOrdered(404);
|
||||
} else {
|
||||
setResultCodeIfOrdered(-1);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Error while delivering the message to the serviceIntent", e);
|
||||
setResultCodeIfOrdered(401);
|
||||
}
|
||||
}
|
||||
|
||||
private void setResultCodeIfOrdered(int code) {
|
||||
if (isOrderedBroadcast()) {
|
||||
setResultCode(code);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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;
|
||||
}
|
||||
|
||||
}
|
@ -1,322 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.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.CloudMessagingRpc;
|
||||
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.EXTRA_DELAY;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||
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_REGISTRATION_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER_LEGACY;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private CloudMessagingRpc rpc;
|
||||
private Context context;
|
||||
|
||||
public GoogleCloudMessaging() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called when your application is done using GCM, to release
|
||||
* internal resources.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
instance = null;
|
||||
rpc.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the singleton instance of GCM.
|
||||
*/
|
||||
public static GoogleCloudMessaging getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
instance = new GoogleCloudMessaging();
|
||||
instance.context = context.getApplicationContext();
|
||||
instance.rpc = new CloudMessagingRpc(instance.context);
|
||||
}
|
||||
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) {
|
||||
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]);
|
||||
}
|
||||
String sender = sb.toString();
|
||||
|
||||
if (isLegacyFallback()) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER_LEGACY, sender);
|
||||
return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras);
|
||||
} else {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER, sender);
|
||||
return rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(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 TTL—this 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'");
|
||||
|
||||
if (isLegacyFallback()) {
|
||||
Bundle extras = new Bundle();
|
||||
for (String key : data.keySet()) {
|
||||
Object o = extras.get(key);
|
||||
if (o instanceof String) {
|
||||
extras.putString("gcm." + key, (String) o);
|
||||
}
|
||||
}
|
||||
extras.putString(EXTRA_SEND_TO, to);
|
||||
extras.putString(EXTRA_MESSAGE_ID, msgId);
|
||||
InstanceID.getInstance(context).requestToken("GCM", "upstream", extras);
|
||||
} else {
|
||||
Bundle extras = data != null ? new Bundle(data) : new Bundle();
|
||||
extras.putString(EXTRA_SEND_TO, to);
|
||||
extras.putString(EXTRA_SEND_FROM, getFrom(to));
|
||||
extras.putString(EXTRA_MESSAGE_ID, msgId);
|
||||
extras.putLong(EXTRA_TTL, timeToLive);
|
||||
extras.putInt(EXTRA_DELAY, -1);
|
||||
rpc.sendGcmMessage(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/>
|
||||
* 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 String—any 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 call—you 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();
|
||||
}
|
||||
|
||||
private boolean isLegacyFallback() {
|
||||
String gcmPackageName = CloudMessagingRpc.getGcmPackageName(context);
|
||||
return gcmPackageName != null && gcmPackageName.endsWith(".gsf");
|
||||
}
|
||||
|
||||
private String getFrom(String to) {
|
||||
int i = to.indexOf('@');
|
||||
if (i > 0) {
|
||||
to = to.substring(0, i);
|
||||
}
|
||||
return InstanceID.getInstance(context).getStore().get("", to, INSTANCE_ID_SCOPE);
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class PendingCallback implements Parcelable {
|
||||
private final IBinder binder;
|
||||
|
||||
public PendingCallback(IBinder binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
private PendingCallback(Parcel in) {
|
||||
this.binder = in.readStrongBinder();
|
||||
}
|
||||
|
||||
public IBinder getBinder() {
|
||||
return binder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongBinder(binder);
|
||||
}
|
||||
|
||||
public static final Creator<PendingCallback> CREATOR = new Creator<PendingCallback>() {
|
||||
@Override
|
||||
public PendingCallback createFromParcel(Parcel source) {
|
||||
return new PendingCallback(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingCallback[] newArray(int size) {
|
||||
return new PendingCallback[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -1,235 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,271 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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);
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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;
|
||||
}
|
||||
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.gcm;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
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.google.android.gms.iid.InstanceID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static com.google.android.gms.gcm.GoogleCloudMessaging.ERROR_SERVICE_NOT_AVAILABLE;
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||
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_ERROR;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE;
|
||||
|
||||
public class CloudMessagingRpc {
|
||||
private static final AtomicInteger messageIdCounter = new AtomicInteger(1);
|
||||
private static String gcmPackageName;
|
||||
|
||||
private final BlockingQueue<Intent> messengerResponseQueue = new LinkedBlockingQueue<Intent>();
|
||||
private final Messenger messenger = new Messenger(new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg == null || !(msg.obj instanceof Intent)) {
|
||||
// Invalid message -> drop
|
||||
return;
|
||||
}
|
||||
Intent intent = (Intent) msg.obj;
|
||||
if (ACTION_C2DM_REGISTRATION.equals(intent.getAction())) {
|
||||
messengerResponseQueue.add(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 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 CloudMessagingRpc(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static String getGcmPackageName(Context context) {
|
||||
if (gcmPackageName != null) {
|
||||
return gcmPackageName;
|
||||
}
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
for (ResolveInfo resolveInfo : packageManager.queryIntentServices(new Intent(ACTION_C2DM_REGISTER), 0)) {
|
||||
if (packageManager.checkPermission(PERMISSION_RECEIVE, resolveInfo.serviceInfo.packageName) == PERMISSION_GRANTED) {
|
||||
return gcmPackageName = resolveInfo.serviceInfo.packageName;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||
return gcmPackageName = appInfo.packageName;
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
}
|
||||
try {
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(GSF_PACKAGE_NAME, 0);
|
||||
return gcmPackageName = appInfo.packageName;
|
||||
} catch (PackageManager.NameNotFoundException ex3) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
// Cancel the authentication
|
||||
if (selfAuthIntent != null) {
|
||||
selfAuthIntent.cancel();
|
||||
selfAuthIntent = null;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Intent sendRegisterMessageBlocking(Bundle extras) throws IOException {
|
||||
sendRegisterMessage(extras);
|
||||
Intent resultIntent;
|
||||
try {
|
||||
resultIntent = messengerResponseQueue.poll(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
if (resultIntent == null) {
|
||||
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
|
||||
}
|
||||
return resultIntent;
|
||||
}
|
||||
|
||||
private void sendRegisterMessage(Bundle extras) {
|
||||
Intent intent = new Intent(ACTION_C2DM_REGISTER);
|
||||
intent.setPackage(getGcmPackageName(context));
|
||||
extras.putString(EXTRA_MESSAGE_ID, "google.rpc" + messageIdCounter.getAndIncrement());
|
||||
intent.putExtras(extras);
|
||||
intent.putExtra(EXTRA_MESSENGER, messenger);
|
||||
intent.putExtra(EXTRA_APP, getSelfAuthIntent());
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
public void sendGcmMessage(Bundle extras) {
|
||||
Intent intent = new Intent(ACTION_GCM_SEND);
|
||||
intent.setPackage(GMS_PACKAGE_NAME);
|
||||
intent.putExtras(extras);
|
||||
intent.putExtra(EXTRA_APP, getSelfAuthIntent());
|
||||
context.sendOrderedBroadcast(intent, PERMISSION_GTALK);
|
||||
}
|
||||
|
||||
public String handleRegisterMessageResult(Intent resultIntent) throws IOException {
|
||||
if (resultIntent == null) throw new IOException(InstanceID.ERROR_SERVICE_NOT_AVAILABLE);
|
||||
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
|
||||
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
|
||||
if (result != null) return result;
|
||||
result = resultIntent.getStringExtra(EXTRA_ERROR);
|
||||
throw new IOException(result != null ? result : InstanceID.ERROR_SERVICE_NOT_AVAILABLE);
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdk()
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
minSdkVersion androidMinSdk()
|
||||
targetSdkVersion androidTargetSdk()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':play-services-base')
|
||||
api project(':play-services-iid-api')
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
POM_NAME=Play Services IID Library
|
||||
POM_DESCRIPTION=The Play Services Library module to access the InstanceID API
|
||||
|
||||
POM_PACKAGING=aar
|
||||
|
||||
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||
|
||||
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
|
||||
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
POM_LICENCE_DIST=repo
|
||||
|
||||
POM_DEVELOPER_ID=mar-v-in
|
||||
POM_DEVELOPER_NAME=Marvin W
|
||||
|
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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 package="org.microg.gms.iid"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Permissions required for IID -->
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.gcm.GcmConstants;
|
||||
import org.microg.gms.iid.InstanceIdRpc;
|
||||
import org.microg.gms.iid.InstanceIdStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELETE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SCOPE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBSCIPTION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBTYPE;
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>App deletes Instance ID</li>
|
||||
* <li>Device is factory reset</li>
|
||||
* <li>User uninstalls the app</li>
|
||||
* <li>User clears app data</li>
|
||||
* </ul>
|
||||
* 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)}.
|
||||
*/
|
||||
@PublicApi
|
||||
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";
|
||||
|
||||
private static final int RSA_KEY_SIZE = 2048;
|
||||
private static final String TAG = "InstanceID";
|
||||
|
||||
private static InstanceIdStore storeInstance;
|
||||
private static InstanceIdRpc rpc;
|
||||
private static Map<String, InstanceID> instances = new HashMap<String, InstanceID>();
|
||||
|
||||
private final String subtype;
|
||||
private KeyPair keyPair;
|
||||
private long creationTime;
|
||||
|
||||
private InstanceID(String subtype) {
|
||||
this.subtype = subtype == null ? "" : subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets Instance ID and revokes all tokens.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void deleteInstanceID() throws IOException {
|
||||
deleteToken("*", "*");
|
||||
creationTime = 0;
|
||||
storeInstance.delete(subtype + "|");
|
||||
keyPair = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, null);
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public void deleteToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||
|
||||
storeInstance.delete(subtype, authorizedEntity, scope);
|
||||
|
||||
if (extras == null) extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER, authorizedEntity);
|
||||
extras.putString(EXTRA_SUBSCIPTION, authorizedEntity);
|
||||
extras.putString(EXTRA_DELETE, "1");
|
||||
extras.putString("X-" + EXTRA_DELETE, "1");
|
||||
extras.putString(EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype);
|
||||
extras.putString("X-" + EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype);
|
||||
if (scope != null) extras.putString(EXTRA_SCOPE, scope);
|
||||
|
||||
rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras, getKeyPair()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time when instance ID was created.
|
||||
*
|
||||
* @return Time when instance ID was created (milliseconds since Epoch).
|
||||
*/
|
||||
public long getCreationTime() {
|
||||
if (creationTime == 0) {
|
||||
String s = storeInstance.get(subtype, "cre");
|
||||
if (s != null) {
|
||||
creationTime = Long.parseLong(s);
|
||||
}
|
||||
}
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stable identifier that uniquely identifies the app instance.
|
||||
*
|
||||
* @return The identifier for the application instance.
|
||||
*/
|
||||
public String getId() {
|
||||
return sha1KeyPair(getKeyPair());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of this class.
|
||||
*
|
||||
* @return InstanceID instance.
|
||||
*/
|
||||
public static InstanceID getInstance(Context context) {
|
||||
String subtype = "";
|
||||
if (storeInstance == null) {
|
||||
storeInstance = new InstanceIdStore(context.getApplicationContext());
|
||||
rpc = new InstanceIdRpc(context.getApplicationContext());
|
||||
}
|
||||
InstanceID instance = instances.get(subtype);
|
||||
if (instance == null) {
|
||||
instance = new InstanceID(subtype);
|
||||
instances.put(subtype, instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public InstanceIdStore getStore() {
|
||||
return storeInstance;
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public String requestToken(String authorizedEntity, String scope, Bundle extras) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private synchronized KeyPair getKeyPair() {
|
||||
if (keyPair == null) {
|
||||
keyPair = storeInstance.getKeyPair(subtype);
|
||||
if (keyPair == null) {
|
||||
try {
|
||||
KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
rsaGenerator.initialize(RSA_KEY_SIZE);
|
||||
keyPair = rsaGenerator.generateKeyPair();
|
||||
creationTime = System.currentTimeMillis();
|
||||
storeInstance.put(subtype, keyPair, creationTime);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public static String sha1KeyPair(KeyPair keyPair) {
|
||||
try {
|
||||
byte[] digest = MessageDigest.getInstance("SHA1").digest(keyPair.getPublic().getEncoded());
|
||||
digest[0] = (byte) (112 + (0xF & digest[0]) & 0xFF);
|
||||
return Base64.encodeToString(digest, 0, 8, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 android.support.v4.content.WakefulBroadcastReceiver;
|
||||
|
||||
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))
|
||||
WakefulBroadcastReceiver.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,423 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.iid;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.iid.InstanceID;
|
||||
import com.google.android.gms.iid.MessengerCompat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_BACKOFF;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_MISSING_INSTANCEID_SERVICE;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_SERVICE_NOT_AVAILABLE;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_TIMEOUT;
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.MAX_REFERENCE_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
||||
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_APP;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_CODE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_NAME;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_CLIENT_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_GMS_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_IS_MESSENGER2;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_KID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_OS_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_PUBLIC_KEY;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SIGNATURE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_USE_GSF;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE;
|
||||
|
||||
public class InstanceIdRpc {
|
||||
private static final String TAG = "InstanceID/Rpc";
|
||||
|
||||
private static final int BLOCKING_WAIT_TIME = 30000;
|
||||
|
||||
private static String iidPackageName;
|
||||
private static int lastRequestId;
|
||||
private static int retryCount;
|
||||
private static Map<String, Object> blockingResponses = new HashMap<String, Object>();
|
||||
|
||||
private long nextAttempt;
|
||||
private int interval;
|
||||
private Context context;
|
||||
private PendingIntent selfAuthToken;
|
||||
private Messenger messenger;
|
||||
private Messenger myMessenger;
|
||||
private MessengerCompat messengerCompat;
|
||||
|
||||
public InstanceIdRpc(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static String getIidPackageName(Context context) {
|
||||
if (iidPackageName != null) {
|
||||
return iidPackageName;
|
||||
}
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
for (ResolveInfo resolveInfo : packageManager.queryIntentServices(new Intent(ACTION_C2DM_REGISTER), 0)) {
|
||||
if (packageManager.checkPermission(PERMISSION_RECEIVE, resolveInfo.serviceInfo.packageName) == PERMISSION_GRANTED) {
|
||||
return iidPackageName = resolveInfo.serviceInfo.packageName;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||
return iidPackageName = appInfo.packageName;
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
}
|
||||
try {
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(GSF_PACKAGE_NAME, 0);
|
||||
return iidPackageName = appInfo.packageName;
|
||||
} catch (PackageManager.NameNotFoundException ex3) {
|
||||
Log.w(TAG, "Both Google Play Services and legacy GSF package are missing");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getGmsVersionCode(final Context context) {
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
return packageManager.getPackageInfo(getIidPackageName(context), 0).versionCode;
|
||||
} catch (PackageManager.NameNotFoundException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getSelfVersionCode(final Context context) {
|
||||
try {
|
||||
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
|
||||
} catch (PackageManager.NameNotFoundException neverHappens) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSelfVersionName(final Context context) {
|
||||
try {
|
||||
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
|
||||
} catch (PackageManager.NameNotFoundException neverHappens) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
if (myMessenger != null) return;
|
||||
getIidPackageName(context);
|
||||
myMessenger = new Messenger(new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg == null) {
|
||||
return;
|
||||
}
|
||||
if (msg.obj instanceof Intent) {
|
||||
Intent intent = (Intent) msg.obj;
|
||||
intent.setExtrasClassLoader(MessengerCompat.class.getClassLoader());
|
||||
if (intent.hasExtra(EXTRA_MESSENGER)) {
|
||||
Parcelable messengerCandidate = intent.getParcelableExtra(EXTRA_MESSENGER);
|
||||
if (messengerCandidate instanceof MessengerCompat) {
|
||||
messengerCompat = (MessengerCompat) messengerCandidate;
|
||||
} else if (messengerCandidate instanceof Messenger) {
|
||||
messenger = (Messenger) messengerCandidate;
|
||||
}
|
||||
}
|
||||
handleResponseInternal(intent);
|
||||
} else {
|
||||
Log.w(TAG, "Dropping invalid message");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void handleResponseInternal(Intent resultIntent) {
|
||||
if (resultIntent == null) return;
|
||||
if (!ACTION_C2DM_REGISTRATION.equals(resultIntent.getAction()) && !ACTION_INSTANCE_ID.equals(resultIntent.getAction()))
|
||||
return;
|
||||
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
|
||||
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
|
||||
if (result == null) {
|
||||
handleError(resultIntent);
|
||||
return;
|
||||
}
|
||||
retryCount = 0;
|
||||
nextAttempt = 0;
|
||||
interval = 0;
|
||||
|
||||
String requestId = null;
|
||||
if (result.startsWith("|")) {
|
||||
// parse structured response
|
||||
String[] split = result.split("\\|");
|
||||
if (!"ID".equals(split[1])) {
|
||||
Log.w(TAG, "Unexpected structured response " + result);
|
||||
}
|
||||
requestId = split[2];
|
||||
if (split.length > 4) {
|
||||
if ("SYNC".equals(split[3])) {
|
||||
// TODO: sync
|
||||
} else if("RST".equals(split[3])) {
|
||||
// TODO: rst
|
||||
resultIntent.removeExtra(EXTRA_REGISTRATION_ID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
result = split[split.length-1];
|
||||
if (result.startsWith(":"))
|
||||
result = result.substring(1);
|
||||
resultIntent.putExtra(EXTRA_REGISTRATION_ID, result);
|
||||
}
|
||||
setResponse(requestId, resultIntent);
|
||||
}
|
||||
|
||||
private void handleError(Intent resultIntent) {
|
||||
String error = resultIntent.getStringExtra("error");
|
||||
if (error == null) return;
|
||||
String requestId = null;
|
||||
if (error.startsWith("|")) {
|
||||
// parse structured error message
|
||||
String[] split = error.split("\\|");
|
||||
if (!"ID".equals(split[1])) {
|
||||
Log.w(TAG, "Unexpected structured response " + error);
|
||||
}
|
||||
if (split.length > 2) {
|
||||
requestId = split[2];
|
||||
error = split[3];
|
||||
if (error.startsWith(":"))
|
||||
error = error.substring(1);
|
||||
} else {
|
||||
error = "UNKNOWN";
|
||||
}
|
||||
resultIntent.putExtra("error", error);
|
||||
}
|
||||
setResponse(requestId, resultIntent);
|
||||
long retryAfter = resultIntent.getLongExtra("Retry-After", 0);
|
||||
if (retryAfter > 0) {
|
||||
interval = (int) (retryAfter * 1000);
|
||||
nextAttempt = SystemClock.elapsedRealtime() + interval;
|
||||
Log.d(TAG, "Server requested retry delay: " + interval);
|
||||
} else if (ERROR_SERVICE_NOT_AVAILABLE.equals(error) || "AUTHENTICATION_FAILED".equals(error)
|
||||
&& GSF_PACKAGE_NAME.equals(getIidPackageName(context))) {
|
||||
retryCount++;
|
||||
if (retryCount < 3) return;
|
||||
if (retryCount == 3) interval = 1000 + new Random().nextInt(1000);
|
||||
interval = interval * 2;
|
||||
nextAttempt = SystemClock.elapsedRealtime() + interval;
|
||||
Log.d(TAG, "Setting retry delay to " + interval);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized PendingIntent getSelfAuthToken() {
|
||||
if (selfAuthToken == null) {
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage("com.google.example.invalidpackage");
|
||||
selfAuthToken = PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||
}
|
||||
return selfAuthToken;
|
||||
}
|
||||
|
||||
private static synchronized String getRequestId() {
|
||||
return Integer.toString(lastRequestId++);
|
||||
}
|
||||
|
||||
private void sendRegisterMessage(Bundle data, KeyPair keyPair, String requestId) throws IOException {
|
||||
long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
if (nextAttempt != 0 && elapsedRealtime <= nextAttempt) {
|
||||
Log.w(TAG, "Had to wait for " + interval + ", that's still " + (nextAttempt - elapsedRealtime));
|
||||
throw new IOException(ERROR_BACKOFF);
|
||||
}
|
||||
initialize();
|
||||
if (iidPackageName == null) {
|
||||
throw new IOException(ERROR_MISSING_INSTANCEID_SERVICE);
|
||||
}
|
||||
Intent intent = new Intent(ACTION_C2DM_REGISTER);
|
||||
intent.setPackage(iidPackageName);
|
||||
data.putString(EXTRA_GMS_VERSION, Integer.toString(getGmsVersionCode(context)));
|
||||
data.putString(EXTRA_OS_VERSION, Integer.toString(Build.VERSION.SDK_INT));
|
||||
data.putString(EXTRA_APP_VERSION_CODE, Integer.toString(getSelfVersionCode(context)));
|
||||
data.putString(EXTRA_APP_VERSION_NAME, getSelfVersionName(context));
|
||||
data.putString(EXTRA_CLIENT_VERSION, "iid-" + MAX_REFERENCE_VERSION);
|
||||
data.putString(EXTRA_APP_ID, InstanceID.sha1KeyPair(keyPair));
|
||||
String pub = base64encode(keyPair.getPublic().getEncoded());
|
||||
data.putString(EXTRA_PUBLIC_KEY, pub);
|
||||
data.putString(EXTRA_SIGNATURE, sign(keyPair, context.getPackageName(), pub));
|
||||
intent.putExtras(data);
|
||||
intent.putExtra(EXTRA_APP, getSelfAuthToken());
|
||||
sendRequest(intent, requestId);
|
||||
}
|
||||
|
||||
private static String sign(KeyPair keyPair, String... payload) {
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = TextUtils.join("\n", payload).getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(TAG, "Unable to encode", e);
|
||||
return null;
|
||||
}
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
try {
|
||||
Signature signature = Signature.getInstance(privateKey instanceof RSAPrivateKey ? "SHA256withRSA" : "SHA256withECDSA");
|
||||
signature.initSign(privateKey);
|
||||
signature.update(bytes);
|
||||
return base64encode(signature.sign());
|
||||
} catch (GeneralSecurityException e) {
|
||||
Log.e(TAG, "Unable to sign", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String base64encode(byte[] bytes) {
|
||||
return Base64.encodeToString(bytes, Base64.URL_SAFE + Base64.NO_PADDING + Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
private void sendRequest(Intent intent, String requestId) {
|
||||
intent.putExtra(EXTRA_KID, "|ID|" + requestId + "|");
|
||||
intent.putExtra("X-" + EXTRA_KID, "|ID|" + requestId + "|");
|
||||
Log.d(TAG, "Sending " + intent.getExtras());
|
||||
if (messenger != null) {
|
||||
intent.putExtra(EXTRA_MESSENGER, myMessenger);
|
||||
Message msg = Message.obtain();
|
||||
msg.obj = intent;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "Messenger failed, falling back to service");
|
||||
}
|
||||
}
|
||||
|
||||
boolean useGsf = iidPackageName.endsWith(".gsf");
|
||||
if (intent.hasExtra(EXTRA_USE_GSF))
|
||||
useGsf = "1".equals(intent.getStringExtra(EXTRA_USE_GSF));
|
||||
|
||||
if (useGsf) {
|
||||
Intent holder = new Intent(ACTION_INSTANCE_ID);
|
||||
holder.setPackage(context.getPackageName());
|
||||
holder.putExtra(EXTRA_GSF_INTENT, intent);
|
||||
context.startService(holder);
|
||||
} else {
|
||||
intent.putExtra(EXTRA_MESSENGER, myMessenger);
|
||||
intent.putExtra(EXTRA_IS_MESSENGER2, "1");
|
||||
if (messengerCompat != null) {
|
||||
Message msg = Message.obtain();
|
||||
msg.obj = intent;
|
||||
try {
|
||||
messengerCompat.send(msg);
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "Messenger failed, falling back to service");
|
||||
}
|
||||
}
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public Intent sendRegisterMessageBlocking(Bundle data, KeyPair keyPair) throws IOException {
|
||||
Intent intent = sendRegisterMessageBlockingInternal(data, keyPair);
|
||||
if (intent != null && intent.hasExtra(EXTRA_MESSENGER)) {
|
||||
// Now with a messenger
|
||||
intent = sendRegisterMessageBlockingInternal(data, keyPair);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
private Intent sendRegisterMessageBlockingInternal(Bundle data, KeyPair keyPair) throws IOException {
|
||||
ConditionVariable cv = new ConditionVariable();
|
||||
String requestId = getRequestId();
|
||||
synchronized (InstanceIdRpc.class) {
|
||||
blockingResponses.put(requestId, cv);
|
||||
}
|
||||
|
||||
sendRegisterMessage(data, keyPair, requestId);
|
||||
|
||||
cv.block(BLOCKING_WAIT_TIME);
|
||||
synchronized (InstanceIdRpc.class) {
|
||||
Object res = blockingResponses.remove(requestId);
|
||||
if (res instanceof Intent) {
|
||||
return (Intent) res;
|
||||
} else if (res instanceof String) {
|
||||
throw new IOException((String) res);
|
||||
}
|
||||
Log.w(TAG, "No response " + res);
|
||||
throw new IOException(ERROR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
public String handleRegisterMessageResult(Intent resultIntent) throws IOException {
|
||||
if (resultIntent == null) throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
|
||||
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
|
||||
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
|
||||
if (result != null) return result;
|
||||
result = resultIntent.getStringExtra(EXTRA_ERROR);
|
||||
throw new IOException(result != null ? result : ERROR_SERVICE_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
private void setResponse(String requestId, Object response) {
|
||||
if (requestId == null) {
|
||||
for (String r : blockingResponses.keySet()) {
|
||||
setResponse(r, response);
|
||||
}
|
||||
}
|
||||
Object old = blockingResponses.get(requestId);
|
||||
blockingResponses.put(requestId, response);
|
||||
if (old instanceof ConditionVariable) {
|
||||
((ConditionVariable) old).open();
|
||||
} else if (old instanceof Messenger) {
|
||||
Message msg = Message.obtain();
|
||||
msg.obj = response;
|
||||
try {
|
||||
((Messenger) old).send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to send response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 org.microg.gms.iid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class InstanceIdStore {
|
||||
private static final String TAG = "InstanceID/Store";
|
||||
private static final String PREF_NAME = "com.google.android.gms.appid";
|
||||
|
||||
private Context context;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
public InstanceIdStore(Context context) {
|
||||
this.context = context;
|
||||
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public synchronized String get(String key) {
|
||||
return sharedPreferences.getString(key, null);
|
||||
}
|
||||
|
||||
public String get(String subtype, String key) {
|
||||
return get(subtype + "|S|" + key);
|
||||
}
|
||||
|
||||
public String get(String subtype, String authorizedEntity, String scope) {
|
||||
return get(subtype + "|T|" + authorizedEntity + "|" + scope);
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair(String subtype) {
|
||||
String pub = get(subtype, "|P|");
|
||||
String priv = get(subtype, "|K|");
|
||||
if (pub == null || priv == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
byte[] pubKey = Base64.decode(pub, Base64.URL_SAFE);
|
||||
byte[] privKey = Base64.decode(priv, Base64.URL_SAFE);
|
||||
KeyFactory rsaFactory = KeyFactory.getInstance("RSA");
|
||||
return new KeyPair(rsaFactory.generatePublic(new X509EncodedKeySpec(pubKey)), rsaFactory.generatePrivate(new PKCS8EncodedKeySpec(privKey)));
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Invalid key stored " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void put(String key, String value) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public void put(String subtype, String key, String value) {
|
||||
put(subtype + "|S|" + key, value);
|
||||
}
|
||||
|
||||
public void put(String subtype, String authorizedEntity, String scope, String value) {
|
||||
put(subtype + "|T|" + authorizedEntity + "|" + scope, value);
|
||||
}
|
||||
|
||||
public synchronized void put(String subtype, KeyPair keyPair, long timestamp) {
|
||||
put(subtype, "|P|", Base64.encodeToString(keyPair.getPublic().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
|
||||
put(subtype, "|K|", Base64.encodeToString(keyPair.getPrivate().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
|
||||
put(subtype, "cre", Long.toString(timestamp));
|
||||
}
|
||||
|
||||
public synchronized void delete() {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public synchronized void delete(String prefix) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
for (String key : sharedPreferences.getAll().keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
editor.remove(key);
|
||||
}
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public synchronized void delete(String subtype, String authorizedEntity, String scope) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(subtype + "|T|" + authorizedEntity + "|" + scope);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdk()
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
minSdkVersion androidMinSdk()
|
||||
targetSdkVersion androidTargetSdk()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':play-services-base')
|
||||
api project(':play-services-cast')
|
||||
api project(':play-services-gcm')
|
||||
api project(':play-services-location')
|
||||
api project(':play-services-wearable')
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
POM_NAME=Play Services Library
|
||||
POM_DESCRIPTION=The whole package of Play Services libraries. Use play-services-* artifacts if you only need certain parts
|
||||
|
||||
POM_PACKAGING=aar
|
||||
|
||||
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||
|
||||
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||
|
||||
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
POM_LICENCE_DIST=repo
|
||||
|
||||
POM_DEVELOPER_ID=mar-v-in
|
||||
POM_DEVELOPER_NAME=Marvin W
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 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">
|
||||
|
||||
<application />
|
||||
</manifest>
|