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
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 ;
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 ;
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 ;
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 ;
2016-05-11 23:07:22 +00:00
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP ;
2015-03-22 13:32:51 +00:00
import static android.os.Build.VERSION.SDK_INT ;
2021-06-26 07:45:33 +00:00
import static org.microg.gms.common.PackageUtils.warnIfNotPersistentProcess ;
2016-01-12 22:27:43 +00:00
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE ;
2016-11-20 18:37:13 +00:00
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP ;
2020-09-11 08:11:10 +00:00
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_OVERRIDE ;
2016-04-18 08:29:52 +00:00
import static org.microg.gms.gcm.GcmConstants.EXTRA_COLLAPSE_KEY ;
2016-01-12 22:27:43 +00:00
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM ;
2016-11-20 18:37:13 +00:00
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 ;
2020-09-11 08:11:10 +00:00
import static org.microg.gms.gcm.McsConstants.ACTION_ACK ;
2016-01-12 22:27:43 +00:00
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 ;
2016-11-20 18:37:13 +00:00
import static org.microg.gms.gcm.McsConstants.ACTION_SEND ;
2016-01-12 22:27:43 +00:00
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 ;
2020-09-11 08:11:10 +00:00
import static org.microg.gms.gcm.McsConstants.MCS_IQ_STANZA_TAG ;
2016-01-12 22:27:43 +00:00
import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_REQUEST_TAG ;
import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_RESPONSE_TAG ;
2020-09-11 08:11:10 +00:00
import static org.microg.gms.gcm.McsConstants.MSG_ACK ;
2016-01-12 22:27:43 +00:00
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 ;
2015-03-22 13:32:51 +00:00
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 " ;
public static final int SERVICE_PORT = 5228 ;
2016-01-12 22:27:43 +00:00
private static final int WAKELOCK_TIMEOUT = 5000 ;
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 ;
2020-09-11 08:11:10 +00:00
private 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 ;
2016-11-20 18:37:13 +00:00
private static int maxTtl = 24 * 60 * 60 ;
2019-04-21 18:10:45 +00:00
private Object deviceIdleController ;
private Method getUserIdMethod ;
private Method addPowerSaveTempWhitelistAppMethod ;
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
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 {
2019-12-04 21:30:26 +00:00
String deviceIdleControllerName = " deviceidle " ;
try {
Field field = Context . class . getField ( " DEVICE_IDLE_CONTROLLER " ) ;
deviceIdleControllerName = ( String ) field . get ( null ) ;
} catch ( Exception ignored ) {
}
2019-04-21 18:10:45 +00:00
IBinder binder = ( IBinder ) Class . forName ( " android.os.ServiceManager " )
2019-12-04 21:30:26 +00:00
. getMethod ( " getService " , String . class ) . invoke ( null , deviceIdleControllerName ) ;
2019-04-21 18:10:45 +00:00
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 ) ;
}
} 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 ( ) {
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 ;
}
// consider connection to be dead if we did not receive an ack within twice the heartbeat interval
2020-12-23 02:12:18 +00:00
int heartbeatMs = GcmPrefs . get ( context ) . getHeartbeatMsFor ( activeNetworkPref ) ;
2017-04-26 20:03:34 +00:00
if ( heartbeatMs < 0 ) {
closeAll ( ) ;
} else if ( SystemClock . elapsedRealtime ( ) - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs ) {
2020-09-11 08:11:10 +00:00
logd ( null , " No heartbeat for " + ( SystemClock . elapsedRealtime ( ) - lastHeartbeatAckElapsedRealtime ) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds " ) ;
2021-06-26 07:45:33 +00:00
GcmPrefs . get ( context ) . learnTimeout ( context , activeNetworkPref ) ;
2016-05-11 23:07:22 +00:00
return false ;
}
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 ) ;
}
}
2015-08-04 11:05:47 +00:00
private synchronized void connect ( ) {
2015-03-22 13:32:51 +00:00
try {
2016-12-23 17:59:29 +00:00
closeAll ( ) ;
2017-04-26 20:03:34 +00:00
ConnectivityManager cm = ( ConnectivityManager ) getSystemService ( Context . CONNECTIVITY_SERVICE ) ;
activeNetworkPref = GcmPrefs . get ( this ) . getNetworkPrefForInfo ( cm . getActiveNetworkInfo ( ) ) ;
if ( ! GcmPrefs . get ( this ) . isEnabledFor ( cm . getActiveNetworkInfo ( ) ) ) {
scheduleReconnect ( this ) ;
return ;
}
2020-09-11 08:11:10 +00:00
logd ( this , " Starting MCS connection... " ) ;
2015-04-02 21:46:47 +00:00
Socket socket = new Socket ( SERVICE_HOST , SERVICE_PORT ) ;
2020-09-11 08:11:10 +00:00
logd ( this , " Connected to " + SERVICE_HOST + " : " + SERVICE_PORT ) ;
2015-08-04 11:05:47 +00:00
sslSocket = SSLContext . getDefault ( ) . getSocketFactory ( ) . createSocket ( socket , SERVICE_HOST , SERVICE_PORT , true ) ;
2020-09-11 08:11:10 +00:00
logd ( this , " Activated SSL with " + SERVICE_HOST + " : " + SERVICE_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 ( ) ;
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 ) {
2015-08-04 11:05:47 +00:00
Log . w ( TAG , " Exception while connecting! " , e ) ;
2015-10-10 22:46:58 +00:00
rootHandler . sendMessage ( rootHandler . obtainMessage ( MSG_TEARDOWN , e ) ) ;
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 ) {
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . M ) {
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 ) ;
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 ( ) ) ;
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 ;
}
Log . w ( TAG , " Unknown message: " + msg ) ;
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 ) {
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 ( ) {
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 ) {
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
}