2015-03-22 13:32:51 +00:00
|
|
|
/*
|
2017-06-12 22:30:41 +00:00
|
|
|
* Copyright (C) 2013-2017 microG Project Team
|
2015-03-22 13:32:51 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2015-04-02 21:46:47 +00:00
|
|
|
package org.microg.gms.gcm;
|
2015-03-22 13:32:51 +00:00
|
|
|
|
2022-01-28 00:15:01 +00:00
|
|
|
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
|
|
|
|
import static android.os.Build.VERSION.SDK_INT;
|
|
|
|
import static org.microg.gms.common.PackageUtils.warnIfNotPersistentProcess;
|
|
|
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
|
|
|
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
|
|
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_OVERRIDE;
|
|
|
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_COLLAPSE_KEY;
|
|
|
|
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_MESSENGER;
|
|
|
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
|
|
|
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;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.ACTION_ACK;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.ACTION_CONNECT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.ACTION_RECONNECT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.ACTION_SEND;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.EXTRA_REASON;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_CLOSE_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_DATA_MESSAGE_STANZA_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_HEARTBEAT_ACK_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_HEARTBEAT_PING_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_IQ_STANZA_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_REQUEST_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_RESPONSE_TAG;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_ACK;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_CONNECT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_HEARTBEAT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_INPUT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_INPUT_ERROR;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT_DONE;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT_ERROR;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT_READY;
|
|
|
|
import static org.microg.gms.gcm.McsConstants.MSG_TEARDOWN;
|
|
|
|
|
2022-03-08 19:18:49 +00:00
|
|
|
import android.annotation.SuppressLint;
|
2015-08-04 11:05:47 +00:00
|
|
|
import android.app.AlarmManager;
|
|
|
|
import android.app.PendingIntent;
|
2015-10-10 22:46:58 +00:00
|
|
|
import android.app.Service;
|
2016-02-08 02:38:07 +00:00
|
|
|
import android.content.ComponentName;
|
2015-03-22 13:32:51 +00:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
2016-01-12 22:27:43 +00:00
|
|
|
import android.content.pm.PackageManager;
|
2020-09-11 08:11:10 +00:00
|
|
|
import android.content.pm.PermissionInfo;
|
2016-01-12 22:27:43 +00:00
|
|
|
import android.content.pm.ResolveInfo;
|
2017-04-26 20:03:34 +00:00
|
|
|
import android.net.ConnectivityManager;
|
2022-01-26 05:26:56 +00:00
|
|
|
import android.net.NetworkInfo;
|
2016-01-11 19:49:17 +00:00
|
|
|
import android.os.Build;
|
2016-11-20 18:37:13 +00:00
|
|
|
import android.os.Bundle;
|
2015-08-04 11:05:47 +00:00
|
|
|
import android.os.Handler;
|
2015-10-10 22:46:58 +00:00
|
|
|
import android.os.IBinder;
|
2015-08-04 11:05:47 +00:00
|
|
|
import android.os.Looper;
|
2016-11-20 18:37:13 +00:00
|
|
|
import android.os.Messenger;
|
|
|
|
import android.os.Parcelable;
|
2015-08-04 11:05:47 +00:00
|
|
|
import android.os.PowerManager;
|
|
|
|
import android.os.SystemClock;
|
2019-04-21 14:39:18 +00:00
|
|
|
import android.os.UserHandle;
|
2015-03-22 13:32:51 +00:00
|
|
|
import android.util.Log;
|
|
|
|
|
2022-03-08 19:18:49 +00:00
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.annotation.RequiresApi;
|
2020-07-08 18:04:23 +00:00
|
|
|
import androidx.legacy.content.WakefulBroadcastReceiver;
|
|
|
|
|
2020-12-23 02:12:18 +00:00
|
|
|
import com.mgoogle.android.gms.R;
|
2015-03-22 13:32:51 +00:00
|
|
|
import com.squareup.wire.Message;
|
|
|
|
|
|
|
|
import org.microg.gms.checkin.LastCheckinInfo;
|
2020-09-09 15:26:42 +00:00
|
|
|
import org.microg.gms.common.ForegroundServiceContext;
|
2020-12-23 02:12:18 +00:00
|
|
|
import org.microg.gms.common.ForegroundServiceInfo;
|
2016-11-20 18:37:13 +00:00
|
|
|
import org.microg.gms.common.PackageUtils;
|
2015-04-02 21:46:47 +00:00
|
|
|
import org.microg.gms.gcm.mcs.AppData;
|
|
|
|
import org.microg.gms.gcm.mcs.Close;
|
|
|
|
import org.microg.gms.gcm.mcs.DataMessageStanza;
|
2020-09-11 08:11:10 +00:00
|
|
|
import org.microg.gms.gcm.mcs.Extension;
|
2015-04-02 21:46:47 +00:00
|
|
|
import org.microg.gms.gcm.mcs.HeartbeatAck;
|
|
|
|
import org.microg.gms.gcm.mcs.HeartbeatPing;
|
2020-09-11 08:11:10 +00:00
|
|
|
import org.microg.gms.gcm.mcs.IqStanza;
|
2015-04-02 21:46:47 +00:00
|
|
|
import org.microg.gms.gcm.mcs.LoginRequest;
|
|
|
|
import org.microg.gms.gcm.mcs.LoginResponse;
|
|
|
|
import org.microg.gms.gcm.mcs.Setting;
|
2015-03-22 13:32:51 +00:00
|
|
|
|
2016-05-18 12:10:17 +00:00
|
|
|
import java.io.Closeable;
|
2019-04-21 14:39:18 +00:00
|
|
|
import java.lang.reflect.Field;
|
2019-04-21 18:10:45 +00:00
|
|
|
import java.lang.reflect.Method;
|
2015-03-22 13:32:51 +00:00
|
|
|
import java.net.Socket;
|
2022-01-26 05:26:56 +00:00
|
|
|
import java.net.SocketException;
|
2016-11-20 18:37:13 +00:00
|
|
|
import java.util.ArrayList;
|
2015-03-23 01:14:07 +00:00
|
|
|
import java.util.Collections;
|
2016-01-12 22:27:43 +00:00
|
|
|
import java.util.List;
|
2020-09-11 08:11:10 +00:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
2015-03-22 13:32:51 +00:00
|
|
|
|
|
|
|
import javax.net.ssl.SSLContext;
|
|
|
|
|
2016-11-20 18:37:13 +00:00
|
|
|
import okio.ByteString;
|
|
|
|
|
2020-12-23 02:12:18 +00:00
|
|
|
@ForegroundServiceInfo(value = "Cloud messaging", res = R.string.service_name_mcs)
|
2015-10-10 22:46:58 +00:00
|
|
|
public class McsService extends Service implements Handler.Callback {
|
2015-03-22 13:32:51 +00:00
|
|
|
private static final String TAG = "GmsGcmMcsSvc";
|
2015-04-02 21:46:47 +00:00
|
|
|
|
2020-08-04 23:57:23 +00:00
|
|
|
public static final String SELF_CATEGORY = "com.google.android.gsf.gtalkservice";
|
2015-03-22 13:32:51 +00:00
|
|
|
public static final String IDLE_NOTIFICATION = "IdleNotification";
|
|
|
|
public static final String FROM_FIELD = "gcm@android.com";
|
2015-04-02 21:46:47 +00:00
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
public static final String SERVICE_HOST = "mtalk.google.com";
|
2022-02-05 11:44:50 +00:00
|
|
|
// A few ports are available: 443, 5228-5230 but also 5222-5223
|
|
|
|
// See https://github.com/microg/GmsCore/issues/408
|
2015-08-04 11:05:47 +00:00
|
|
|
public static final int SERVICE_PORT = 5228;
|
2022-02-05 11:44:50 +00:00
|
|
|
// Likely if the main port 5228 is blocked by a firewall, the other 52xx are blocked as well
|
|
|
|
public static final int SERVICE_PORT_FALLBACK = 443;
|
2015-08-04 11:05:47 +00:00
|
|
|
|
2016-01-12 22:27:43 +00:00
|
|
|
private static final int WAKELOCK_TIMEOUT = 5000;
|
2022-01-26 05:26:56 +00:00
|
|
|
// On bad mobile network a ping can take >60s, so we wait for an ACK for 90s
|
|
|
|
private static final int HEARTBEAT_ACK_AFTER_PING_TIMEOUT_MS = 90000;
|
2016-01-12 22:27:43 +00:00
|
|
|
|
2022-01-26 05:26:56 +00:00
|
|
|
private static long lastHeartbeatPingElapsedRealtime = -1;
|
2016-01-11 19:54:25 +00:00
|
|
|
private static long lastHeartbeatAckElapsedRealtime = -1;
|
2017-04-26 20:03:34 +00:00
|
|
|
private static long lastIncomingNetworkRealtime = 0;
|
2016-11-15 23:52:49 +00:00
|
|
|
private static long startTimestamp = 0;
|
2017-04-26 20:03:34 +00:00
|
|
|
public static String activeNetworkPref = null;
|
2022-01-26 05:26:56 +00:00
|
|
|
private boolean wasTornDown = false;
|
2022-01-29 20:23:50 +00:00
|
|
|
private final AtomicInteger nextMessageId = new AtomicInteger(0x1000000);
|
2015-03-22 13:32:51 +00:00
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private static Socket sslSocket;
|
|
|
|
private static McsInputStream inputStream;
|
|
|
|
private static McsOutputStream outputStream;
|
|
|
|
|
|
|
|
private PendingIntent heartbeatIntent;
|
|
|
|
|
2015-10-10 22:46:58 +00:00
|
|
|
private static HandlerThread handlerThread;
|
|
|
|
private static Handler rootHandler;
|
2015-08-04 11:05:47 +00:00
|
|
|
|
2016-11-15 23:52:49 +00:00
|
|
|
private GcmDatabase database;
|
2016-08-28 11:01:35 +00:00
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private AlarmManager alarmManager;
|
|
|
|
private PowerManager powerManager;
|
|
|
|
private static PowerManager.WakeLock wakeLock;
|
2015-03-22 13:32:51 +00:00
|
|
|
|
2015-10-03 20:47:05 +00:00
|
|
|
private static long currentDelay = 0;
|
2015-08-17 21:28:01 +00:00
|
|
|
|
|
|
|
private Intent connectIntent;
|
|
|
|
|
2022-01-29 20:23:50 +00:00
|
|
|
private static final int maxTtl = 24 * 60 * 60;
|
2016-11-20 18:37:13 +00:00
|
|
|
|
2022-03-08 19:18:49 +00:00
|
|
|
@Nullable
|
2019-04-21 18:10:45 +00:00
|
|
|
private Method getUserIdMethod;
|
2022-03-08 19:18:49 +00:00
|
|
|
@Nullable
|
|
|
|
private Object deviceIdleController;
|
|
|
|
@Nullable
|
2019-04-21 18:10:45 +00:00
|
|
|
private Method addPowerSaveTempWhitelistAppMethod;
|
2022-03-08 19:18:49 +00:00
|
|
|
@Nullable
|
|
|
|
@RequiresApi(Build.VERSION_CODES.S)
|
|
|
|
private Object powerExemptionManager;
|
|
|
|
@Nullable
|
|
|
|
@RequiresApi(Build.VERSION_CODES.S)
|
|
|
|
private Method addToTemporaryAllowListMethod;
|
2019-04-21 18:10:45 +00:00
|
|
|
|
2015-10-10 22:46:58 +00:00
|
|
|
private class HandlerThread extends Thread {
|
2016-05-18 12:10:17 +00:00
|
|
|
|
|
|
|
public HandlerThread() {
|
|
|
|
setName("McsHandler");
|
|
|
|
}
|
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
Looper.prepare();
|
2015-08-17 21:28:01 +00:00
|
|
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mcs");
|
|
|
|
wakeLock.setReferenceCounted(false);
|
|
|
|
synchronized (McsService.class) {
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler = new Handler(Looper.myLooper(), McsService.this);
|
2015-08-17 21:28:01 +00:00
|
|
|
if (connectIntent != null) {
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, connectIntent));
|
2015-08-17 21:28:01 +00:00
|
|
|
WakefulBroadcastReceiver.completeWakefulIntent(connectIntent);
|
|
|
|
}
|
|
|
|
}
|
2015-08-04 11:05:47 +00:00
|
|
|
Looper.loop();
|
|
|
|
}
|
2015-07-04 11:47:27 +00:00
|
|
|
}
|
|
|
|
|
2020-09-11 08:11:10 +00:00
|
|
|
private static void logd(Context context, String msg) {
|
|
|
|
if (context == null || GcmPrefs.get(context).isGcmLogEnabled()) Log.d(TAG, msg);
|
2016-05-11 23:07:22 +00:00
|
|
|
}
|
|
|
|
|
2015-03-22 13:32:51 +00:00
|
|
|
@Override
|
2022-03-08 19:18:49 +00:00
|
|
|
@SuppressLint("PrivateApi")
|
2015-08-04 11:05:47 +00:00
|
|
|
public void onCreate() {
|
|
|
|
super.onCreate();
|
2016-11-20 18:37:13 +00:00
|
|
|
TriggerReceiver.register(this);
|
2016-11-15 23:52:49 +00:00
|
|
|
database = new GcmDatabase(this);
|
2015-08-04 11:05:47 +00:00
|
|
|
heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0);
|
|
|
|
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
|
|
|
|
powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
2019-12-04 21:30:26 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") == PackageManager.PERMISSION_GRANTED) {
|
2019-04-21 18:10:45 +00:00
|
|
|
try {
|
2022-03-08 19:18:49 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
|
|
Class<?> powerExemptionManagerClass = Class.forName("android.os.PowerExemptionManager");
|
|
|
|
powerExemptionManager = getSystemService(powerExemptionManagerClass);
|
|
|
|
addToTemporaryAllowListMethod =
|
|
|
|
powerExemptionManagerClass.getMethod("addToTemporaryAllowList", String.class, int.class, String.class, long.class);
|
|
|
|
} else {
|
|
|
|
String deviceIdleControllerName = "deviceidle";
|
|
|
|
try {
|
|
|
|
Field field = Context.class.getField("DEVICE_IDLE_CONTROLLER");
|
|
|
|
deviceIdleControllerName = (String) field.get(null);
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
|
|
|
IBinder binder = (IBinder) Class.forName("android.os.ServiceManager")
|
|
|
|
.getMethod("getService", String.class).invoke(null, deviceIdleControllerName);
|
|
|
|
if (binder != null) {
|
|
|
|
deviceIdleController = Class.forName("android.os.IDeviceIdleController$Stub")
|
|
|
|
.getMethod("asInterface", IBinder.class).invoke(null, binder);
|
|
|
|
getUserIdMethod = UserHandle.class.getMethod("getUserId", int.class);
|
|
|
|
addPowerSaveTempWhitelistAppMethod = deviceIdleController.getClass()
|
|
|
|
.getMethod("addPowerSaveTempWhitelistApp", String.class, long.class, int.class, String.class);
|
|
|
|
}
|
2019-04-21 18:10:45 +00:00
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
|
|
|
}
|
2015-08-17 21:28:01 +00:00
|
|
|
synchronized (McsService.class) {
|
2015-10-10 22:46:58 +00:00
|
|
|
if (handlerThread == null) {
|
|
|
|
handlerThread = new HandlerThread();
|
|
|
|
handlerThread.start();
|
2015-08-17 21:28:01 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 13:14:08 +00:00
|
|
|
public static void stop(Context context) {
|
|
|
|
context.stopService(new Intent(context, McsService.class));
|
|
|
|
closeAll();
|
|
|
|
}
|
|
|
|
|
2016-11-20 18:37:13 +00:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
2022-01-26 05:26:56 +00:00
|
|
|
Log.d(TAG, "onDestroy");
|
2017-02-08 13:14:08 +00:00
|
|
|
alarmManager.cancel(heartbeatIntent);
|
|
|
|
closeAll();
|
2016-11-20 18:37:13 +00:00
|
|
|
database.close();
|
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
2015-10-10 22:46:58 +00:00
|
|
|
@Override
|
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-09-11 08:11:10 +00:00
|
|
|
public synchronized static boolean isConnected(Context context) {
|
2021-06-26 07:45:33 +00:00
|
|
|
warnIfNotPersistentProcess(McsService.class);
|
2016-05-11 23:07:22 +00:00
|
|
|
if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) {
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(null, "Connection is not enabled or dead.");
|
2016-05-11 23:07:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
2022-01-26 05:26:56 +00:00
|
|
|
// consider connection to be dead if we did not receive an ack within 90s to our ping
|
2020-12-23 02:12:18 +00:00
|
|
|
int heartbeatMs = GcmPrefs.get(context).getHeartbeatMsFor(activeNetworkPref);
|
2022-01-26 05:26:56 +00:00
|
|
|
// if disabled for active network, heartbeatMs will be -1
|
2017-04-26 20:03:34 +00:00
|
|
|
if (heartbeatMs < 0) {
|
|
|
|
closeAll();
|
2016-05-11 23:07:22 +00:00
|
|
|
return false;
|
2022-01-26 05:26:56 +00:00
|
|
|
} else {
|
|
|
|
boolean noAckReceived = lastHeartbeatAckElapsedRealtime < lastHeartbeatPingElapsedRealtime;
|
|
|
|
long timeSinceLastPing = SystemClock.elapsedRealtime() - lastHeartbeatPingElapsedRealtime;
|
|
|
|
if (noAckReceived && timeSinceLastPing > HEARTBEAT_ACK_AFTER_PING_TIMEOUT_MS) {
|
|
|
|
logd(null, "No heartbeat for " + timeSinceLastPing / 1000 + "s, connection assumed to be dead after 90s");
|
|
|
|
GcmPrefs.get(context).learnTimeout(context, activeNetworkPref);
|
|
|
|
return false;
|
|
|
|
}
|
2016-05-11 23:07:22 +00:00
|
|
|
}
|
|
|
|
return true;
|
2015-04-02 21:46:47 +00:00
|
|
|
}
|
|
|
|
|
2016-11-15 23:52:49 +00:00
|
|
|
public static long getStartTimestamp() {
|
2021-06-26 07:45:33 +00:00
|
|
|
warnIfNotPersistentProcess(McsService.class);
|
2016-11-15 23:52:49 +00:00
|
|
|
return startTimestamp;
|
|
|
|
}
|
|
|
|
|
2015-10-03 22:15:24 +00:00
|
|
|
public static void scheduleReconnect(Context context) {
|
|
|
|
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
|
2015-10-10 22:46:58 +00:00
|
|
|
long delay = getCurrentDelay();
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(context, "Scheduling reconnect in " + delay / 1000 + " seconds...");
|
2019-12-04 21:30:26 +00:00
|
|
|
PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0);
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi);
|
|
|
|
} else {
|
|
|
|
alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi);
|
|
|
|
}
|
2015-10-03 22:15:24 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 19:49:17 +00:00
|
|
|
public void scheduleHeartbeat(Context context) {
|
|
|
|
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
|
|
|
|
|
2020-12-23 02:12:18 +00:00
|
|
|
int heartbeatMs = GcmPrefs.get(this).getHeartbeatMsFor(activeNetworkPref);
|
2017-04-26 20:03:34 +00:00
|
|
|
if (heartbeatMs < 0) {
|
|
|
|
closeAll();
|
|
|
|
}
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(context, "Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds...");
|
2019-12-04 21:30:26 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
// This is supposed to work even when running in idle and without battery optimization disabled
|
|
|
|
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent);
|
|
|
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
|
|
// With KitKat, the alarms become inexact by default, but with the newly available setWindow we can get inexact alarms with guarantees.
|
2017-04-26 20:03:34 +00:00
|
|
|
// Schedule the alarm to fire within the interval [heartbeatMs/3*4, heartbeatMs]
|
|
|
|
alarmManager.setWindow(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs / 4 * 3, heartbeatMs / 4,
|
2016-01-11 19:49:17 +00:00
|
|
|
heartbeatIntent);
|
2019-12-04 21:30:26 +00:00
|
|
|
} else {
|
|
|
|
alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent);
|
2016-01-11 19:49:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-10-03 20:47:05 +00:00
|
|
|
public synchronized static long getCurrentDelay() {
|
|
|
|
long delay = currentDelay == 0 ? 5000 : currentDelay;
|
2021-09-11 07:36:50 +00:00
|
|
|
if (currentDelay < GcmPrefs.INTERVAL) currentDelay += 10000;
|
|
|
|
if (currentDelay >= GcmPrefs.INTERVAL && currentDelay < 600000) currentDelay += GcmPrefs.INTERVAL;
|
2015-10-03 20:47:05 +00:00
|
|
|
return delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized static void resetCurrentDelay() {
|
|
|
|
currentDelay = 0;
|
|
|
|
}
|
|
|
|
|
2015-10-10 22:46:58 +00:00
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
@Override
|
2015-10-10 22:46:58 +00:00
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
2020-09-09 15:26:42 +00:00
|
|
|
ForegroundServiceContext.completeForegroundService(this, intent, TAG);
|
2015-08-17 21:28:01 +00:00
|
|
|
synchronized (McsService.class) {
|
2015-10-10 22:46:58 +00:00
|
|
|
if (rootHandler != null) {
|
2016-01-12 22:27:43 +00:00
|
|
|
if (intent == null) return START_REDELIVER_INTENT;
|
|
|
|
wakeLock.acquire(WAKELOCK_TIMEOUT);
|
|
|
|
Object reason = intent.hasExtra(EXTRA_REASON) ? intent.getExtras().get(EXTRA_REASON) : intent;
|
2015-08-17 21:28:01 +00:00
|
|
|
if (ACTION_CONNECT.equals(intent.getAction())) {
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, reason));
|
2015-08-17 21:28:01 +00:00
|
|
|
} else if (ACTION_HEARTBEAT.equals(intent.getAction())) {
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_HEARTBEAT, reason));
|
2016-11-20 18:37:13 +00:00
|
|
|
} else if (ACTION_SEND.equals(intent.getAction())) {
|
|
|
|
handleSendMessage(intent);
|
2020-09-11 08:11:10 +00:00
|
|
|
} else if (ACTION_ACK.equals(intent.getAction())) {
|
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_ACK, reason));
|
2015-08-17 21:28:01 +00:00
|
|
|
}
|
|
|
|
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
|
|
|
} else if (connectIntent == null) {
|
|
|
|
connectIntent = intent;
|
2021-03-22 03:04:09 +00:00
|
|
|
} else if (intent != null) {
|
2015-08-17 21:28:01 +00:00
|
|
|
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
2015-03-23 01:14:07 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-11 19:49:17 +00:00
|
|
|
return START_REDELIVER_INTENT;
|
2015-03-23 01:14:07 +00:00
|
|
|
}
|
|
|
|
|
2016-11-20 18:37:13 +00:00
|
|
|
private void handleSendMessage(Intent intent) {
|
|
|
|
String messageId = intent.getStringExtra(EXTRA_MESSAGE_ID);
|
|
|
|
String collapseKey = intent.getStringExtra(EXTRA_COLLAPSE_KEY);
|
|
|
|
|
2020-08-04 23:57:23 +00:00
|
|
|
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
|
2016-11-20 18:37:13 +00:00
|
|
|
intent.removeExtra(EXTRA_MESSENGER);
|
|
|
|
|
|
|
|
Parcelable app = intent.getParcelableExtra(EXTRA_APP);
|
|
|
|
String packageName = null;
|
|
|
|
if (app instanceof PendingIntent) {
|
|
|
|
packageName = PackageUtils.packageFromPendingIntent((PendingIntent) app);
|
|
|
|
}
|
|
|
|
if (packageName == null) {
|
|
|
|
Log.w(TAG, "Failed to send message, missing package name");
|
|
|
|
return;
|
|
|
|
}
|
2020-09-11 08:11:10 +00:00
|
|
|
if (packageName.equals(getPackageName()) && intent.hasExtra(EXTRA_APP_OVERRIDE)) {
|
|
|
|
packageName = intent.getStringExtra(EXTRA_APP_OVERRIDE);
|
|
|
|
intent.removeExtra(EXTRA_APP_OVERRIDE);
|
|
|
|
}
|
2016-11-20 18:37:13 +00:00
|
|
|
intent.removeExtra(EXTRA_APP);
|
|
|
|
|
|
|
|
int ttl;
|
|
|
|
try {
|
2020-09-11 08:11:10 +00:00
|
|
|
if (intent.hasExtra(EXTRA_TTL)) {
|
|
|
|
ttl = Integer.parseInt(intent.getStringExtra(EXTRA_TTL));
|
|
|
|
if (ttl < 0 || ttl > maxTtl) {
|
|
|
|
ttl = maxTtl;
|
|
|
|
}
|
|
|
|
} else {
|
2016-11-20 18:37:13 +00:00
|
|
|
ttl = maxTtl;
|
|
|
|
}
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
// TODO: error TtlUnsupported
|
|
|
|
Log.w(TAG, e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
String to = intent.getStringExtra(EXTRA_SEND_TO);
|
|
|
|
if (to == null) {
|
|
|
|
// TODO: error missing_to
|
|
|
|
Log.w(TAG, "missing to");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
String from = intent.getStringExtra(EXTRA_SEND_FROM);
|
|
|
|
if (from != null) {
|
|
|
|
intent.removeExtra(EXTRA_SEND_FROM);
|
|
|
|
} else {
|
|
|
|
from = intent.getStringExtra(EXTRA_FROM);
|
|
|
|
}
|
|
|
|
if (from == null) {
|
|
|
|
GcmDatabase.Registration reg = database.getRegistration(packageName, PackageUtils.firstSignatureDigest(this, packageName));
|
|
|
|
if (reg != null) from = reg.registerId;
|
|
|
|
}
|
|
|
|
if (from == null) {
|
|
|
|
Log.e(TAG, "Can't send message, missing from!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
|
|
|
|
intent.removeExtra(EXTRA_REGISTRATION_ID);
|
|
|
|
|
|
|
|
List<AppData> appData = new ArrayList<>();
|
|
|
|
Bundle extras = intent.getExtras();
|
|
|
|
for (String key : extras.keySet()) {
|
|
|
|
if (!key.startsWith("google.")) {
|
|
|
|
Object val = extras.get(key);
|
|
|
|
if (val instanceof String) {
|
2020-08-04 23:57:23 +00:00
|
|
|
appData.add(new AppData.Builder().key(key).value((String) val).build());
|
2016-11-20 18:37:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] rawDataArray = intent.getByteArrayExtra("rawData");
|
|
|
|
ByteString rawData = rawDataArray != null ? ByteString.of(rawDataArray) : null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
DataMessageStanza msg = new DataMessageStanza.Builder()
|
|
|
|
.sent(System.currentTimeMillis() / 1000L)
|
2020-09-11 08:11:10 +00:00
|
|
|
.id(Integer.toHexString(nextMessageId.incrementAndGet()))
|
|
|
|
.persistent_id(messageId)
|
2016-11-20 18:37:13 +00:00
|
|
|
.token(collapseKey)
|
|
|
|
.from(from)
|
|
|
|
.reg_id(registrationId)
|
|
|
|
.to(to)
|
|
|
|
.category(packageName)
|
|
|
|
.raw_data(rawData)
|
|
|
|
.app_data(appData).build();
|
|
|
|
|
|
|
|
send(MCS_DATA_MESSAGE_STANZA_TAG, msg);
|
2020-08-04 23:57:23 +00:00
|
|
|
database.noteAppMessage(packageName, DataMessageStanza.ADAPTER.encodedSize(msg));
|
2016-11-20 18:37:13 +00:00
|
|
|
} catch (Exception e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-05 11:44:50 +00:00
|
|
|
private boolean connect(int port) {
|
2015-03-22 13:32:51 +00:00
|
|
|
try {
|
2022-01-26 05:26:56 +00:00
|
|
|
wasTornDown = false;
|
2017-04-26 20:03:34 +00:00
|
|
|
|
2022-02-05 11:44:50 +00:00
|
|
|
logd(this, "Starting MCS connection to port " + port + "...");
|
|
|
|
Socket socket = new Socket(SERVICE_HOST, port);
|
|
|
|
logd(this, "Connected to " + SERVICE_HOST + ":" + port);
|
|
|
|
sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, port, true);
|
|
|
|
logd(this, "Activated SSL with " + SERVICE_HOST + ":" + port);
|
2015-10-10 22:46:58 +00:00
|
|
|
inputStream = new McsInputStream(sslSocket.getInputStream(), rootHandler);
|
|
|
|
outputStream = new McsOutputStream(sslSocket.getOutputStream(), rootHandler);
|
2015-08-04 11:05:47 +00:00
|
|
|
inputStream.start();
|
|
|
|
outputStream.start();
|
2015-08-17 21:28:01 +00:00
|
|
|
|
2016-11-15 23:52:49 +00:00
|
|
|
startTimestamp = System.currentTimeMillis();
|
2022-01-26 05:26:56 +00:00
|
|
|
lastHeartbeatPingElapsedRealtime = SystemClock.elapsedRealtime();
|
2016-01-11 19:54:25 +00:00
|
|
|
lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime();
|
2017-04-26 20:03:34 +00:00
|
|
|
lastIncomingNetworkRealtime = SystemClock.elapsedRealtime();
|
2016-01-11 19:49:17 +00:00
|
|
|
scheduleHeartbeat(this);
|
2015-03-22 13:32:51 +00:00
|
|
|
} catch (Exception e) {
|
2022-02-05 11:44:50 +00:00
|
|
|
Log.w(TAG, "Exception while connecting to " + SERVICE_HOST + ":" + port, e);
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, e));
|
2022-02-05 11:44:50 +00:00
|
|
|
closeAll();
|
|
|
|
return false;
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
2022-02-05 11:44:50 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private synchronized void connect() {
|
|
|
|
closeAll();
|
|
|
|
|
|
|
|
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
|
|
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
|
|
|
|
activeNetworkPref = GcmPrefs.get(this).getNetworkPrefForInfo(activeNetworkInfo);
|
|
|
|
if (!GcmPrefs.get(this).isEnabledFor(activeNetworkInfo)) {
|
|
|
|
logd(this, "Don't connect, because disabled for " + activeNetworkInfo.getTypeName());
|
|
|
|
scheduleReconnect(this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!connect(SERVICE_PORT))
|
|
|
|
connect(SERVICE_PORT_FALLBACK);
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private void handleClose(Close close) {
|
|
|
|
throw new RuntimeException("Server requested close!");
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private void handleLoginResponse(LoginResponse loginResponse) {
|
2015-03-22 13:32:51 +00:00
|
|
|
if (loginResponse.error == null) {
|
2021-06-26 07:45:33 +00:00
|
|
|
GcmPrefs.clearLastPersistedId(this);
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Logged in");
|
2015-08-04 11:05:47 +00:00
|
|
|
wakeLock.release();
|
2015-03-22 13:32:51 +00:00
|
|
|
} else {
|
2015-08-04 11:05:47 +00:00
|
|
|
throw new RuntimeException("Could not login: " + loginResponse.error);
|
2015-03-23 01:14:07 +00:00
|
|
|
}
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private void handleCloudMessage(DataMessageStanza message) {
|
2015-03-22 13:32:51 +00:00
|
|
|
if (message.persistent_id != null) {
|
2021-06-26 07:45:33 +00:00
|
|
|
GcmPrefs.get(this).extendLastPersistedId(this, message.persistent_id);
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
if (SELF_CATEGORY.equals(message.category)) {
|
|
|
|
handleSelfMessage(message);
|
|
|
|
} else {
|
|
|
|
handleAppMessage(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-11 23:07:22 +00:00
|
|
|
private void handleHeartbeatPing(HeartbeatPing ping) {
|
2015-03-22 13:32:51 +00:00
|
|
|
HeartbeatAck.Builder ack = new HeartbeatAck.Builder().status(ping.status);
|
|
|
|
if (inputStream.newStreamIdAvailable()) {
|
|
|
|
ack.last_stream_id_received(inputStream.getStreamId());
|
|
|
|
}
|
2016-01-12 22:27:43 +00:00
|
|
|
send(MCS_HEARTBEAT_ACK_TAG, ack.build());
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private void handleHeartbeatAck(HeartbeatAck ack) {
|
2021-06-26 07:45:33 +00:00
|
|
|
GcmPrefs.get(this).learnReached(this, activeNetworkPref, SystemClock.elapsedRealtime() - lastIncomingNetworkRealtime);
|
2016-01-11 19:54:25 +00:00
|
|
|
lastHeartbeatAckElapsedRealtime = SystemClock.elapsedRealtime();
|
2015-08-04 11:05:47 +00:00
|
|
|
wakeLock.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
private LoginRequest buildLoginRequest() {
|
|
|
|
LastCheckinInfo info = LastCheckinInfo.read(this);
|
2015-03-22 13:32:51 +00:00
|
|
|
return new LoginRequest.Builder()
|
|
|
|
.adaptive_heartbeat(false)
|
|
|
|
.auth_service(LoginRequest.AuthService.ANDROID_ID)
|
2021-06-26 07:45:33 +00:00
|
|
|
.auth_token(Long.toString(info.getSecurityToken()))
|
2015-03-22 13:32:51 +00:00
|
|
|
.id("android-" + SDK_INT)
|
|
|
|
.domain("mcs.android.com")
|
2021-06-26 07:45:33 +00:00
|
|
|
.device_id("android-" + Long.toHexString(info.getAndroidId()))
|
2015-03-22 13:32:51 +00:00
|
|
|
.network_type(1)
|
2021-06-26 07:45:33 +00:00
|
|
|
.resource(Long.toString(info.getAndroidId()))
|
|
|
|
.user(Long.toString(info.getAndroidId()))
|
2015-03-22 13:32:51 +00:00
|
|
|
.use_rmq2(true)
|
2020-08-04 23:57:23 +00:00
|
|
|
.setting(Collections.singletonList(new Setting.Builder().name("new_vc").value("1").build()))
|
2016-05-11 23:07:22 +00:00
|
|
|
.received_persistent_id(GcmPrefs.get(this).getLastPersistedIds())
|
2015-03-22 13:32:51 +00:00
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleAppMessage(DataMessageStanza msg) {
|
2019-04-21 14:39:18 +00:00
|
|
|
String packageName = msg.category;
|
2020-08-04 23:57:23 +00:00
|
|
|
database.noteAppMessage(packageName, DataMessageStanza.ADAPTER.encodedSize(msg));
|
2019-04-21 14:39:18 +00:00
|
|
|
GcmDatabase.App app = database.getApp(packageName);
|
2016-11-15 23:52:49 +00:00
|
|
|
|
2015-03-22 13:32:51 +00:00
|
|
|
Intent intent = new Intent();
|
2016-01-12 22:27:43 +00:00
|
|
|
intent.setAction(ACTION_C2DM_RECEIVE);
|
|
|
|
intent.putExtra(EXTRA_FROM, msg.from);
|
2019-04-18 17:50:34 +00:00
|
|
|
intent.putExtra(EXTRA_MESSAGE_ID, msg.id);
|
2020-09-11 08:11:10 +00:00
|
|
|
if (msg.persistent_id != null) {
|
|
|
|
intent.putExtra(EXTRA_MESSAGE_ID, msg.persistent_id);
|
|
|
|
}
|
2016-11-20 18:37:13 +00:00
|
|
|
if (app.wakeForDelivery) {
|
|
|
|
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
|
|
|
} else {
|
|
|
|
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
|
|
|
|
}
|
2016-04-18 08:29:52 +00:00
|
|
|
if (msg.token != null) intent.putExtra(EXTRA_COLLAPSE_KEY, msg.token);
|
2015-03-22 13:32:51 +00:00
|
|
|
for (AppData appData : msg.app_data) {
|
|
|
|
intent.putExtra(appData.key, appData.value);
|
|
|
|
}
|
2016-11-15 23:52:49 +00:00
|
|
|
|
2020-09-11 08:11:10 +00:00
|
|
|
String receiverPermission = null;
|
2018-08-28 12:10:52 +00:00
|
|
|
try {
|
2019-04-21 14:39:18 +00:00
|
|
|
String name = packageName + ".permission.C2D_MESSAGE";
|
2020-09-11 08:11:10 +00:00
|
|
|
PermissionInfo info = getPackageManager().getPermissionInfo(name, 0);
|
|
|
|
if (info.packageName.equals(packageName)) {
|
|
|
|
receiverPermission = name;
|
|
|
|
}
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
// Keep null, no valid permission found
|
2018-08-28 12:10:52 +00:00
|
|
|
}
|
|
|
|
|
2020-09-11 08:11:10 +00:00
|
|
|
if (receiverPermission == null) {
|
|
|
|
// Without receiver permission, we only restrict by package name
|
2020-10-19 15:40:24 +00:00
|
|
|
if (app.wakeForDelivery) addPowerSaveTempWhitelistApp(packageName);
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Deliver message to all receivers in package " + packageName);
|
|
|
|
intent.setPackage(packageName);
|
|
|
|
sendOrderedBroadcast(intent, null);
|
2016-11-15 23:52:49 +00:00
|
|
|
} else {
|
2020-09-11 08:11:10 +00:00
|
|
|
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
|
|
|
|
if (infos == null || infos.isEmpty()) {
|
|
|
|
logd(this, "No target for message, wut?");
|
|
|
|
} else {
|
|
|
|
for (ResolveInfo resolveInfo : infos) {
|
|
|
|
Intent targetIntent = new Intent(intent);
|
|
|
|
targetIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
|
|
|
|
if (resolveInfo.activityInfo.packageName.equals(packageName)) {
|
2020-10-19 15:40:24 +00:00
|
|
|
if (app.wakeForDelivery) addPowerSaveTempWhitelistApp(packageName);
|
2020-09-11 08:11:10 +00:00
|
|
|
// We don't need receiver permission for our own package
|
|
|
|
logd(this, "Deliver message to own receiver " + resolveInfo);
|
|
|
|
sendOrderedBroadcast(targetIntent, null);
|
|
|
|
} else if (resolveInfo.filter.hasCategory(packageName)) {
|
|
|
|
// Permission required
|
|
|
|
logd(this, "Deliver message to third-party receiver (with permission check)" + resolveInfo);
|
|
|
|
sendOrderedBroadcast(targetIntent, receiverPermission);
|
2019-04-21 14:39:18 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-15 23:52:49 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 15:40:24 +00:00
|
|
|
private void addPowerSaveTempWhitelistApp(String packageName) {
|
2022-03-08 19:18:49 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
|
|
try {
|
|
|
|
if (addToTemporaryAllowListMethod != null && powerExemptionManager != null) {
|
|
|
|
logd(this, "Adding app " + packageName + " to the temp allowlist");
|
|
|
|
addToTemporaryAllowListMethod.invoke(powerExemptionManager, packageName, 0, "GCM Push", 10000);
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.e(TAG, "Error adding app" + packageName + " to the temp allowlist.", e);
|
|
|
|
}
|
|
|
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
2020-10-19 15:40:24 +00:00
|
|
|
try {
|
|
|
|
if (getUserIdMethod != null && addPowerSaveTempWhitelistAppMethod != null && deviceIdleController != null) {
|
|
|
|
int userId = (int) getUserIdMethod.invoke(null, getPackageManager().getApplicationInfo(packageName, 0).uid);
|
|
|
|
logd(this, "Adding app " + packageName + " for userId " + userId + " to the temp whitelist");
|
|
|
|
addPowerSaveTempWhitelistAppMethod.invoke(deviceIdleController, packageName, 10000, userId, "GCM Push");
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
private void handleSelfMessage(DataMessageStanza msg) {
|
2015-03-22 13:32:51 +00:00
|
|
|
for (AppData appData : msg.app_data) {
|
|
|
|
if (IDLE_NOTIFICATION.equals(appData.key)) {
|
|
|
|
DataMessageStanza.Builder msgResponse = new DataMessageStanza.Builder()
|
2020-09-11 08:11:10 +00:00
|
|
|
.id(Integer.toHexString(nextMessageId.incrementAndGet()))
|
2015-03-22 13:32:51 +00:00
|
|
|
.from(FROM_FIELD)
|
|
|
|
.sent(System.currentTimeMillis() / 1000)
|
|
|
|
.ttl(0)
|
|
|
|
.category(SELF_CATEGORY)
|
2020-08-04 23:57:23 +00:00
|
|
|
.app_data(Collections.singletonList(new AppData.Builder().key(IDLE_NOTIFICATION).value("false").build()));
|
2015-03-22 13:32:51 +00:00
|
|
|
if (inputStream.newStreamIdAvailable()) {
|
|
|
|
msgResponse.last_stream_id_received(inputStream.getStreamId());
|
|
|
|
}
|
2016-01-12 22:27:43 +00:00
|
|
|
send(MCS_DATA_MESSAGE_STANZA_TAG, msgResponse.build());
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:27:43 +00:00
|
|
|
private void send(int type, Message message) {
|
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_OUTPUT, type, 0, message));
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 22:27:43 +00:00
|
|
|
private void sendOutputStream(int what, int arg, Object obj) {
|
2015-08-04 11:05:47 +00:00
|
|
|
McsOutputStream os = outputStream;
|
2016-01-17 14:56:03 +00:00
|
|
|
if (os != null && os.isAlive()) {
|
2015-08-04 11:05:47 +00:00
|
|
|
Handler outputHandler = os.getHandler();
|
|
|
|
if (outputHandler != null)
|
2016-01-12 22:27:43 +00:00
|
|
|
outputHandler.sendMessage(outputHandler.obtainMessage(what, arg, 0, obj));
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean handleMessage(android.os.Message msg) {
|
|
|
|
switch (msg.what) {
|
|
|
|
case MSG_INPUT:
|
2016-01-12 22:27:43 +00:00
|
|
|
handleInput(msg.arg1, (Message) msg.obj);
|
2015-08-04 11:05:47 +00:00
|
|
|
return true;
|
|
|
|
case MSG_OUTPUT:
|
2016-01-12 22:27:43 +00:00
|
|
|
sendOutputStream(MSG_OUTPUT, msg.arg1, msg.obj);
|
2015-08-04 11:05:47 +00:00
|
|
|
return true;
|
|
|
|
case MSG_INPUT_ERROR:
|
|
|
|
case MSG_OUTPUT_ERROR:
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "I/O error: " + msg.obj);
|
2022-01-26 05:26:56 +00:00
|
|
|
if (msg.obj instanceof SocketException) {
|
|
|
|
SocketException e = (SocketException) msg.obj;
|
|
|
|
if ("Connection reset".equals(e.getMessage())) {
|
|
|
|
GcmPrefs.get(this).learnTimeout(this, activeNetworkPref);
|
|
|
|
}
|
|
|
|
}
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, msg.obj));
|
2015-08-04 11:05:47 +00:00
|
|
|
return true;
|
|
|
|
case MSG_TEARDOWN:
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Teardown initiated, reason: " + msg.obj);
|
2015-08-04 11:05:47 +00:00
|
|
|
handleTeardown(msg);
|
|
|
|
return true;
|
|
|
|
case MSG_CONNECT:
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Connect initiated, reason: " + msg.obj);
|
|
|
|
if (!isConnected(this)) {
|
2015-08-04 11:05:47 +00:00
|
|
|
connect();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case MSG_HEARTBEAT:
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Heartbeat initiated, reason: " + msg.obj);
|
|
|
|
if (isConnected(this)) {
|
2015-08-04 11:05:47 +00:00
|
|
|
HeartbeatPing.Builder ping = new HeartbeatPing.Builder();
|
|
|
|
if (inputStream.newStreamIdAvailable()) {
|
|
|
|
ping.last_stream_id_received(inputStream.getStreamId());
|
|
|
|
}
|
2016-01-12 22:27:43 +00:00
|
|
|
send(MCS_HEARTBEAT_PING_TAG, ping.build());
|
2022-01-26 05:26:56 +00:00
|
|
|
lastHeartbeatPingElapsedRealtime = SystemClock.elapsedRealtime();
|
2016-01-11 19:49:17 +00:00
|
|
|
scheduleHeartbeat(this);
|
2015-08-04 11:05:47 +00:00
|
|
|
} else {
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Ignoring heartbeat, not connected!");
|
2015-10-10 22:46:58 +00:00
|
|
|
scheduleReconnect(this);
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
|
|
|
return true;
|
2020-09-11 08:11:10 +00:00
|
|
|
case MSG_ACK:
|
|
|
|
logd(this, "Ack initiated, reason: " + msg.obj);
|
|
|
|
if (isConnected(this)) {
|
|
|
|
IqStanza.Builder iq = new IqStanza.Builder()
|
|
|
|
.type(IqStanza.IqType.SET)
|
|
|
|
.id("")
|
|
|
|
.extension(new Extension.Builder().id(13).data(ByteString.EMPTY).build()) // StreamAck
|
|
|
|
.status(0L);
|
|
|
|
if (inputStream.newStreamIdAvailable()) {
|
|
|
|
iq.last_stream_id_received(inputStream.getStreamId());
|
|
|
|
}
|
|
|
|
send(MCS_IQ_STANZA_TAG, iq.build());
|
|
|
|
} else {
|
|
|
|
logd(this, "Ignoring ack, not connected!");
|
|
|
|
}
|
|
|
|
return true;
|
2015-08-04 11:05:47 +00:00
|
|
|
case MSG_OUTPUT_READY:
|
2020-09-11 08:11:10 +00:00
|
|
|
logd(this, "Sending login request...");
|
2016-01-12 22:27:43 +00:00
|
|
|
send(MCS_LOGIN_REQUEST_TAG, buildLoginRequest());
|
|
|
|
return true;
|
|
|
|
case MSG_OUTPUT_DONE:
|
|
|
|
handleOutputDone(msg);
|
2015-08-04 11:05:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
2022-01-26 05:26:56 +00:00
|
|
|
Log.w(TAG, "Unknown message (" + msg.what + "): " + msg);
|
2015-08-04 11:05:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:27:43 +00:00
|
|
|
private void handleOutputDone(android.os.Message msg) {
|
2020-08-31 23:23:19 +00:00
|
|
|
if (msg.arg1 == MCS_HEARTBEAT_PING_TAG) {
|
|
|
|
wakeLock.release();
|
2016-01-12 22:27:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleInput(int type, Message message) {
|
2015-08-04 11:05:47 +00:00
|
|
|
try {
|
2016-01-12 22:27:43 +00:00
|
|
|
switch (type) {
|
|
|
|
case MCS_DATA_MESSAGE_STANZA_TAG:
|
|
|
|
handleCloudMessage((DataMessageStanza) message);
|
|
|
|
break;
|
|
|
|
case MCS_HEARTBEAT_PING_TAG:
|
2016-05-11 23:07:22 +00:00
|
|
|
handleHeartbeatPing((HeartbeatPing) message);
|
2016-01-12 22:27:43 +00:00
|
|
|
break;
|
|
|
|
case MCS_HEARTBEAT_ACK_TAG:
|
|
|
|
handleHeartbeatAck((HeartbeatAck) message);
|
|
|
|
break;
|
|
|
|
case MCS_CLOSE_TAG:
|
|
|
|
handleClose((Close) message);
|
|
|
|
break;
|
|
|
|
case MCS_LOGIN_RESPONSE_TAG:
|
|
|
|
handleLoginResponse((LoginResponse) message);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Log.w(TAG, "Unknown message: " + message);
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
2015-10-03 20:47:05 +00:00
|
|
|
resetCurrentDelay();
|
2017-04-26 20:03:34 +00:00
|
|
|
lastIncomingNetworkRealtime = SystemClock.elapsedRealtime();
|
2015-08-04 11:05:47 +00:00
|
|
|
} catch (Exception e) {
|
2022-01-26 05:26:56 +00:00
|
|
|
Log.w(TAG, "Exception when handling input: " + message, e);
|
2015-10-10 22:46:58 +00:00
|
|
|
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, e));
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 13:14:08 +00:00
|
|
|
private static void tryClose(Closeable closeable) {
|
2016-05-18 12:10:17 +00:00
|
|
|
if (closeable != null) {
|
|
|
|
try {
|
|
|
|
closeable.close();
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
2016-05-18 12:10:17 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 13:14:08 +00:00
|
|
|
private static void closeAll() {
|
2022-01-26 05:26:56 +00:00
|
|
|
logd(null, "Closing all sockets...");
|
2016-05-18 12:10:17 +00:00
|
|
|
tryClose(inputStream);
|
|
|
|
tryClose(outputStream);
|
2016-12-23 17:59:29 +00:00
|
|
|
if (sslSocket != null) {
|
|
|
|
try {
|
|
|
|
sslSocket.close();
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
2016-12-23 17:59:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleTeardown(android.os.Message msg) {
|
2022-01-26 05:26:56 +00:00
|
|
|
if (wasTornDown) {
|
|
|
|
// This can get called multiple times from different places via MSG_TEARDOWN
|
|
|
|
// this causes the reconnect delay to increase with each call to scheduleReconnect(),
|
|
|
|
// increasing the time we are disconnected.
|
|
|
|
logd(this, "Was torn down already, not doing it again");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
wasTornDown = true;
|
2016-12-23 17:59:29 +00:00
|
|
|
closeAll();
|
2015-12-04 19:26:59 +00:00
|
|
|
|
2015-10-23 16:27:50 +00:00
|
|
|
scheduleReconnect(this);
|
2015-12-04 19:26:59 +00:00
|
|
|
|
2015-08-04 11:05:47 +00:00
|
|
|
alarmManager.cancel(heartbeatIntent);
|
2015-08-17 21:28:01 +00:00
|
|
|
if (wakeLock != null) {
|
2016-01-12 22:27:43 +00:00
|
|
|
try {
|
|
|
|
wakeLock.release();
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
2015-08-17 21:28:01 +00:00
|
|
|
}
|
2015-08-04 11:05:47 +00:00
|
|
|
}
|
2015-03-22 13:32:51 +00:00
|
|
|
}
|