/* * This is the source code of Telegram for Android v. 1.3.2. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2013. */ package org.telegram.messenger; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; import android.util.Base64; import org.telegram.TL.TLClassStore; import org.telegram.TL.TLObject; import org.telegram.TL.TLRPC; import org.telegram.ui.ApplicationLoader; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.TcpConnectionDelegate { public static boolean DEBUG_VERSION = false; public static int APP_ID = 2458; public static String APP_HASH = "5bce48dc7d331e62c955669eb7233217"; public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; private HashMap datacenters = new HashMap(); private HashMap> processedMessageIdsSet = new HashMap>(); private HashMap nextSeqNoInSession = new HashMap(); private ArrayList sessionsToDestroy = new ArrayList(); private ArrayList destroyingSessions = new ArrayList(); private HashMap> quickAckIdToRequestIds = new HashMap>(); private HashMap> messagesIdsForConfirmation = new HashMap>(); private HashMap> processedSessionChanges = new HashMap>(); private HashMap pingIdToDate = new HashMap(); private ConcurrentHashMap> requestsByGuids = new ConcurrentHashMap>(100, 1.0f, 2); private ConcurrentHashMap requestsByClass = new ConcurrentHashMap(100, 1.0f, 2); public volatile int connectionState = 2; private ArrayList requestQueue = new ArrayList(); private ArrayList runningRequests = new ArrayList(); private ArrayList actionQueue = new ArrayList(); private TLRPC.TL_auth_exportedAuthorization movingAuthorization; public static final int DEFAULT_DATACENTER_ID = Integer.MAX_VALUE; public static final int DC_UPDATE_TIME = 60 * 60; public int currentDatacenterId; public int movingToDatacenterId; private long lastOutgoingMessageId = 0; private int useDifferentBackend = 0; private final int SESSION_VERSION = 2; public int timeDifference = 0; public int currentPingTime; private int lastDestroySessionRequestTime; private final boolean isDebugSession = false; private boolean updatingDcSettings = false; private int updatingDcStartTime = 0; private int lastDcUpdateTime = 0; private int currentAppVersion = 0; public static ConnectionsManager Instance = new ConnectionsManager(); private boolean paused = false; private Runnable stageRunnable; private Runnable pingRunnable; private long lastPingTime = System.currentTimeMillis(); private int nextWakeUpTimeout = 60000; private int nextSleepTimeout = 60000; public ConnectionsManager() { currentAppVersion = ApplicationLoader.getAppVersion(); lastOutgoingMessageId = 0; movingToDatacenterId = DEFAULT_DATACENTER_ID; loadSession(); if (!isNetworkOnline()) { connectionState = 1; } Timer serviceTimer = new Timer(); serviceTimer.schedule(new TimerTask() { @Override public void run() { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { long currentTime = System.currentTimeMillis(); if (ApplicationLoader.lastPauseTime != 0 && ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout) { if (!paused) { FileLog.e("tmessages", "pausing network and timers by sleep time = " + nextSleepTimeout); for (Datacenter datacenter : datacenters.values()) { if (datacenter.connection != null) { datacenter.connection.suspendConnection(true); } if (datacenter.uploadConnection != null) { datacenter.uploadConnection.suspendConnection(true); } if (datacenter.downloadConnection != null) { datacenter.downloadConnection.suspendConnection(true); } } } try { paused = true; if (ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout - nextWakeUpTimeout) { ApplicationLoader.lastPauseTime = currentTime; nextSleepTimeout = 30000; FileLog.e("tmessages", "wakeup network in background by wakeup time = " + nextWakeUpTimeout); if (nextWakeUpTimeout < 30 * 60 * 1000) { nextWakeUpTimeout *= 2; } } else { Thread.sleep(500); return; } } catch (Exception e) { FileLog.e("tmessages", e); } } if (paused) { paused = false; FileLog.e("tmessages", "resume network and timers"); } if (datacenters != null) { MessagesController.Instance.updateTimerProc(); if (datacenterWithId(currentDatacenterId).authKey != null) { if (lastPingTime < System.currentTimeMillis() - 30000) { lastPingTime = System.currentTimeMillis(); generatePing(); } if (!updatingDcSettings && lastDcUpdateTime < (int)(System.currentTimeMillis() / 1000) - DC_UPDATE_TIME) { updateDcSettings(); } processRequestQueue(0, 0); } } } }); } }, 1000, 1000); } public void resumeNetworkMaybe() { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { if (paused) { ApplicationLoader.lastPauseTime = System.currentTimeMillis(); nextWakeUpTimeout = 60000; nextSleepTimeout = 30000; FileLog.e("tmessages", "wakeup network in background by recieved push"); } else if (ApplicationLoader.lastPauseTime != 0) { ApplicationLoader.lastPauseTime = System.currentTimeMillis(); FileLog.e("tmessages", "reset sleep timeout by recieved push"); } } }); } public void applicationMovedToForeground() { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { if (paused) { nextSleepTimeout = 60000; nextWakeUpTimeout = 60000; FileLog.e("tmessages", "reset timers by application moved to foreground"); } } }); } //================================================================================ // Config and session manage //================================================================================ public Datacenter datacenterWithId(int datacenterId) { if (datacenterId == DEFAULT_DATACENTER_ID) { return datacenters.get(currentDatacenterId); } return datacenters.get(datacenterId); } void setTimeDifference(int diff) { boolean store = Math.abs(diff - timeDifference) > 25; timeDifference = diff; if (store) { saveSession(); } } void loadSession() { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { File configFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "config.dat"); if (configFile.exists()) { try { SerializedData data = new SerializedData(configFile); int datacenterSetId = data.readInt32(); int version = data.readInt32(); if (datacenterSetId == useDifferentBackend && version == SESSION_VERSION) { sessionsToDestroy.clear(); int count = data.readInt32(); for (int a = 0; a < count; a++) { sessionsToDestroy.add(data.readInt64()); } timeDifference = data.readInt32(); count = data.readInt32(); for (int a = 0; a < count; a++) { Datacenter datacenter = new Datacenter(data, 0); datacenters.put(datacenter.datacenterId, datacenter); } currentDatacenterId = data.readInt32(); } else { UserConfig.clearConfig(); } } catch (Exception e) { UserConfig.clearConfig(); } } else { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dataconfig", Context.MODE_PRIVATE); int datacenterSetId = preferences.getInt("datacenterSetId", 0); if (datacenterSetId == useDifferentBackend) { currentDatacenterId = preferences.getInt("currentDatacenterId", 0); timeDifference = preferences.getInt("timeDifference", 0); lastDcUpdateTime = preferences.getInt("lastDcUpdateTime", 0); //TODO uncomment //lastDcUpdateTime = 0; try { sessionsToDestroy.clear(); String sessionsString = preferences.getString("sessionsToDestroy", null); if (sessionsString != null) { byte[] sessionsBytes = Base64.decode(sessionsString, Base64.DEFAULT); if (sessionsBytes != null) { SerializedData data = new SerializedData(sessionsBytes); int count = data.readInt32(); for (int a = 0; a < count; a++) { sessionsToDestroy.add(data.readInt64()); } } } } catch (Exception e) { FileLog.e("tmessages", e); } try { String datacentersString = preferences.getString("datacenters", null); if (datacentersString != null) { byte[] datacentersBytes = Base64.decode(datacentersString, Base64.DEFAULT); if (datacentersBytes != null) { SerializedData data = new SerializedData(datacentersBytes); int count = data.readInt32(); for (int a = 0; a < count; a++) { Datacenter datacenter = new Datacenter(data, 1); datacenters.put(datacenter.datacenterId, datacenter); } } } } catch (Exception e) { FileLog.e("tmessages", e); } } } if (currentDatacenterId != 0 && UserConfig.clientActivated) { Datacenter datacenter = datacenterWithId(currentDatacenterId); if (datacenter.authKey == null) { currentDatacenterId = 0; datacenters.clear(); UserConfig.clearConfig(); } } if (datacenters.size() == 0) { if (useDifferentBackend == 0) { Datacenter datacenter = new Datacenter(); datacenter.datacenterId = 1; datacenter.addAddressAndPort("173.240.5.1", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 2; datacenter.addAddressAndPort("95.142.192.66", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 3; datacenter.addAddressAndPort("174.140.142.6", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 4; datacenter.addAddressAndPort("31.210.235.12", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 5; datacenter.addAddressAndPort("116.51.22.2", 443); datacenters.put(datacenter.datacenterId, datacenter); } else { Datacenter datacenter = new Datacenter(); datacenter.datacenterId = 1; datacenter.addAddressAndPort("173.240.5.253", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 2; datacenter.addAddressAndPort("95.142.192.65", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 3; datacenter.addAddressAndPort("174.140.142.5", 443); datacenters.put(datacenter.datacenterId, datacenter); } } else if (datacenters.size() == 1) { Datacenter datacenter = new Datacenter(); datacenter.datacenterId = 2; datacenter.addAddressAndPort("95.142.192.66", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 3; datacenter.addAddressAndPort("174.140.142.6", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 4; datacenter.addAddressAndPort("31.210.235.12", 443); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 5; datacenter.addAddressAndPort("116.51.22.2", 443); datacenters.put(datacenter.datacenterId, datacenter); } for (Datacenter datacenter : datacenters.values()) { datacenter.authSessionId = getNewSessionId(); } if (datacenters.size() != 0 && currentDatacenterId == 0) { currentDatacenterId = 1; saveSession(); } movingToDatacenterId = DEFAULT_DATACENTER_ID; } }); } void saveSession() { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { try { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dataconfig", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("datacenterSetId", useDifferentBackend); Datacenter currentDatacenter = datacenterWithId(currentDatacenterId); if (currentDatacenter != null) { editor.putInt("currentDatacenterId", currentDatacenterId); editor.putInt("timeDifference", timeDifference); editor.putInt("lastDcUpdateTime", lastDcUpdateTime); ArrayList sessions = new ArrayList(); if (currentDatacenter.authSessionId != 0) { sessions.add(currentDatacenter.authSessionId); } if (currentDatacenter.authDownloadSessionId != 0) { sessions.add(currentDatacenter.authDownloadSessionId); } if (currentDatacenter.authUploadSessionId != 0) { sessions.add(currentDatacenter.authUploadSessionId); } if (!sessions.isEmpty()) { SerializedData data = new SerializedData(sessions.size() * 8 + 4); data.writeInt32(sessions.size()); for (long session : sessions) { data.writeInt64(session); } editor.putString("sessionsToDestroy", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); } else { editor.remove("sessionsToDestroy"); } if (!datacenters.isEmpty()) { SerializedData data = new SerializedData(); data.writeInt32(datacenters.size()); for (Datacenter datacenter : datacenters.values()) { datacenter.SerializeToStream(data); } editor.putString("datacenters", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); } else { editor.remove("datacenters"); } } else { editor.remove("datacenters"); editor.remove("sessionsToDestroy"); editor.remove("currentDatacenterId"); editor.remove("timeDifference"); } editor.commit(); File configFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "config.dat"); if (configFile.exists()) { configFile.delete(); } } catch (Exception e) { FileLog.e("tmessages", e); } } }); } void clearRequestsForRequestClass(int requestClass, Datacenter datacenter) { for (RPCRequest request : runningRequests) { Datacenter dcenter = datacenterWithId(request.runningDatacenterId); if ((request.flags & requestClass) != 0 && dcenter != null && dcenter.datacenterId == datacenter.datacenterId) { request.runningMessageId = 0; request.runningMessageSeqNo = 0; request.runningStartTime = 0; request.runningMinStartTime = 0; request.transportChannelToken = 0; } } } public void cleanUp() { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { Datacenter datacenter = datacenterWithId(currentDatacenterId); recreateSession(datacenter.authSessionId, datacenter); } }); } void recreateSession(long sessionId, Datacenter datacenter) { messagesIdsForConfirmation.remove(sessionId); processedMessageIdsSet.remove(sessionId); nextSeqNoInSession.remove(sessionId); processedSessionChanges.remove(sessionId); pingIdToDate.remove(sessionId); if (sessionId == datacenter.authSessionId) { clearRequestsForRequestClass(RPCRequest.RPCRequestClassGeneric, datacenter); FileLog.d("tmessages", "***** Recreate generic session"); datacenter.authSessionId = getNewSessionId(); } } long getNewSessionId() { long newSessionId = (long)(MessagesController.random.nextDouble() * Long.MAX_VALUE); return isDebugSession ? (0xabcd000000000000L | (newSessionId & 0x0000ffffffffffffL)) : newSessionId; } long generateMessageId() { long messageId = (long)((((double)System.currentTimeMillis() + ((double)timeDifference) * 1000) * 4294967296.0) / 1000.0); if (messageId <= lastOutgoingMessageId) { messageId = lastOutgoingMessageId + 1; } while (messageId % 4 != 0) { messageId++; } lastOutgoingMessageId = messageId; return messageId; } long getTimeFromMsgId(long messageId) { return (long)(messageId / 4294967296.0 * 1000); } int generateMessageSeqNo(long session, boolean increment) { int value = 0; if (nextSeqNoInSession.containsKey(session)) { value = nextSeqNoInSession.get(session); } if (increment) { nextSeqNoInSession.put(session, value + 1); } return value * 2 + (increment ? 1 : 0); } boolean isMessageIdProcessed(long sessionId, long messageId) { ArrayList set = processedMessageIdsSet.get(sessionId); return set != null && set.contains(messageId); } void addProcessedMessageId(long sessionId, long messageId) { ArrayList set = processedMessageIdsSet.get(sessionId); if (set != null) { final int eraseLimit = 1000; final int eraseThreshold = 224; if (set.size() > eraseLimit + eraseThreshold) { for (int a = 0; a < Math.min(set.size(), eraseThreshold + 1); a++) { set.remove(0); } } set.add(messageId); } else { ArrayList sessionMap = new ArrayList(); sessionMap.add(messageId); processedMessageIdsSet.put(sessionId, sessionMap); } } //================================================================================ // Requests manage //================================================================================ int lastClassGuid = 1; public int generateClassGuid() { int guid = lastClassGuid++; ArrayList requests = new ArrayList(); requestsByGuids.put(guid, requests); return guid; } public void cancelRpcsForClassGuid(int guid) { ArrayList requests = requestsByGuids.get(guid); if (requests != null) { for (Long request : requests) { cancelRpc(request, true); } requestsByGuids.remove(guid); } } public void bindRequestToGuid(final Long request, final int guid) { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { ArrayList requests = requestsByGuids.get(guid); if (requests != null) { requests.add(request); requestsByClass.put(request, guid); } } }); } public void removeRequestInClass(final Long request) { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { Integer guid = requestsByClass.get(request); if (guid != null) { ArrayList requests = requestsByGuids.get(guid); if (requests != null) { requests.remove(request); } } } }); } public void updateDcSettings() { if (updatingDcSettings) { return; } updatingDcStartTime = (int)(System.currentTimeMillis() / 1000); updatingDcSettings = true; TLRPC.TL_help_getConfig getConfig = new TLRPC.TL_help_getConfig(); ConnectionsManager.Instance.performRpc(getConfig, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { if (!updatingDcSettings) { return; } if (error == null) { lastDcUpdateTime = (int)(System.currentTimeMillis() / 1000); TLRPC.TL_config config = (TLRPC.TL_config)response; ArrayList datacentersArr = new ArrayList(); HashMap datacenterMap = new HashMap(); for (TLRPC.TL_dcOption datacenterDesc : config.dc_options) { Datacenter existing = datacenterMap.get(datacenterDesc.id); if (existing == null) { existing = new Datacenter(); existing.datacenterId = datacenterDesc.id; existing.authSessionId = (long)(MessagesController.random.nextDouble() * Long.MAX_VALUE); datacentersArr.add(existing); datacenterMap.put(existing.datacenterId, existing); } existing.addAddressAndPort(datacenterDesc.ip_address, datacenterDesc.port); } if (!datacentersArr.isEmpty()) { for (Datacenter datacenter : datacentersArr) { Datacenter exist = datacenterWithId(datacenter.datacenterId); if (exist == null) { datacenters.put(datacenter.datacenterId, datacenter); } else { exist.replaceAddressesAndPorts(datacenter.addresses, datacenter.ports); } if (datacenter.datacenterId == movingToDatacenterId) { movingToDatacenterId = DEFAULT_DATACENTER_ID; moveToDatacenter(datacenter.datacenterId); } } saveSession(); processRequestQueue(RPCRequest.RPCRequestClassTransportMask, 0); } } updatingDcSettings = false; } }, null, true, RPCRequest.RPCRequestClassEnableUnauthorized | RPCRequest.RPCRequestClassGeneric, currentDatacenterId); } public long performRpc(final TLObject rpc, final RPCRequest.RPCRequestDelegate completionBlock, final RPCRequest.RPCProgressDelegate progressBlock, boolean requiresCompletion, int requestClass) { return performRpc(rpc, completionBlock, progressBlock, requiresCompletion, requestClass, DEFAULT_DATACENTER_ID); } public long performRpc(final TLObject rpc, final RPCRequest.RPCRequestDelegate completionBlock, final RPCRequest.RPCProgressDelegate progressBlock, boolean requiresCompletion, int requestClass, int datacenterId) { return performRpc(rpc, completionBlock, progressBlock, null, requiresCompletion, requestClass, datacenterId); } TLObject wrapInLayer(TLObject object, int datacenterId, RPCRequest request) { if (object.layer() > 0) { Datacenter datacenter = datacenterWithId(datacenterId); if (datacenter.lastInitVersion != currentAppVersion) { request.initRequest = true; TLRPC.initConnection invoke = new TLRPC.initConnection(); invoke.query = object; invoke.api_id = APP_ID; try { invoke.lang_code = Locale.getDefault().getCountry(); invoke.device_model = Build.MANUFACTURER + Build.MODEL; if (invoke.device_model == null) { invoke.device_model = "Android unknown"; } PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); invoke.app_version = pInfo.versionName; if (invoke.app_version == null) { invoke.app_version = "App version unknown"; } invoke.system_version = "SDK " + Build.VERSION.SDK_INT; } catch (Exception e) { FileLog.e("tmessages", e); invoke.lang_code = "en"; invoke.device_model = "Android unknown"; invoke.app_version = "App version unknown"; invoke.system_version = "SDK " + Build.VERSION.SDK_INT; } if (invoke.lang_code == null || invoke.lang_code.length() == 0) { invoke.lang_code = "en"; } if (invoke.device_model == null || invoke.device_model.length() == 0) { invoke.device_model = "Android unknown"; } if (invoke.app_version == null || invoke.app_version.length() == 0) { invoke.app_version = "App version unknown"; } if (invoke.system_version == null || invoke.system_version.length() == 0) { invoke.system_version = "SDK Unknown"; } object = invoke; } TLRPC.invokeWithLayer11 invoke = new TLRPC.invokeWithLayer11(); invoke.query = object; FileLog.d("wrap in layer", "" + object); return invoke; } return object; } public static volatile long nextCallToken = 0; long performRpc(final TLObject rpc, final RPCRequest.RPCRequestDelegate completionBlock, final RPCRequest.RPCProgressDelegate progressBlock, final RPCRequest.RPCQuickAckDelegate quickAckBlock, final boolean requiresCompletion, final int requestClass, final int datacenterId) { final long requestToken = nextCallToken++; Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { RPCRequest request = new RPCRequest(); request.token = requestToken; request.flags = requestClass; request.runningDatacenterId = datacenterId; request.rawRequest = rpc; request.rpcRequest = wrapInLayer(rpc, datacenterId, request); request.completionBlock = completionBlock; request.progressBlock = progressBlock; request.quickAckBlock = quickAckBlock; request.requiresCompletion = requiresCompletion; requestQueue.add(request); processRequestQueue(0, 0); } }); return requestToken; } public void cancelRpc(final long token, final boolean notifyServer) { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { boolean found = false; for (int i = 0; i < requestQueue.size(); i++) { RPCRequest request = requestQueue.get(i); if (request.token == token) { found = true; request.cancelled = true; FileLog.d("tmessages", "===== Cancelled queued rpc request " + request.rawRequest); requestQueue.remove(i); break; } } for (int i = 0; i < runningRequests.size(); i++) { RPCRequest request = runningRequests.get(i); if (request.token == token) { found = true; FileLog.d("tmessages", "===== Cancelled running rpc request " + request.rawRequest); if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { if (notifyServer) { TLRPC.TL_rpc_drop_answer dropAnswer = new TLRPC.TL_rpc_drop_answer(); dropAnswer.req_msg_id = request.runningMessageId; performRpc(dropAnswer, null, null, false, request.flags); } } request.cancelled = true; runningRequests.remove(i); break; } } if (!found) { FileLog.d("tmessages", "***** Warning: cancelling unknown request"); } } }); } public static boolean isNetworkOnline() { boolean status = false; try { ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getNetworkInfo(0); if (netInfo != null && netInfo.getState() == NetworkInfo.State.CONNECTED) { status = true; } else { netInfo = cm.getNetworkInfo(1); if(netInfo != null && netInfo.getState() == NetworkInfo.State.CONNECTED) { status = true; } } } catch(Exception e) { FileLog.e("tmessages", e); return false; } return status; } public int getCurrentTime() { return (int)(System.currentTimeMillis() / 1000) + timeDifference; } private void processRequestQueue(int requestClass, int _datacenterId) { final HashMap activeTransportTokens = new HashMap(); final ArrayList transportsToResume = new ArrayList(); final HashMap activeDownloadTransportTokens = new HashMap(); final ArrayList downloadTransportsToResume = new ArrayList(); final HashMap activeUploadTransportTokens = new HashMap(); final ArrayList uploadTransportsToResume = new ArrayList(); for (Datacenter datacenter : datacenters.values()) { if (datacenter.connection != null) { int channelToken = datacenter.connection.channelToken; if (channelToken != 0) { activeTransportTokens.put(datacenter.datacenterId, channelToken); } } if (datacenter.downloadConnection != null) { int channelToken = datacenter.downloadConnection.channelToken; if (channelToken != 0) { activeDownloadTransportTokens.put(datacenter.datacenterId, channelToken); } } if (datacenter.uploadConnection != null) { int channelToken = datacenter.uploadConnection.channelToken; if (channelToken != 0) { activeUploadTransportTokens.put(datacenter.datacenterId, channelToken); } } } for (RPCRequest request : runningRequests) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); if (requestDatacenter != null && !activeTransportTokens.containsKey(requestDatacenter.datacenterId) && !transportsToResume.contains(requestDatacenter.datacenterId)) { transportsToResume.add(requestDatacenter.datacenterId); } } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); if (requestDatacenter != null && !activeDownloadTransportTokens.containsKey(requestDatacenter.datacenterId) && !downloadTransportsToResume.contains(requestDatacenter.datacenterId)) { downloadTransportsToResume.add(requestDatacenter.datacenterId); } } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); if (requestDatacenter != null && !activeUploadTransportTokens.containsKey(requestDatacenter.datacenterId) && !uploadTransportsToResume.contains(requestDatacenter.datacenterId)) { uploadTransportsToResume.add(requestDatacenter.datacenterId); } } } for (RPCRequest request : requestQueue) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); if (requestDatacenter != null && !activeTransportTokens.containsKey(requestDatacenter.datacenterId) && !transportsToResume.contains(requestDatacenter.datacenterId)) { transportsToResume.add(requestDatacenter.datacenterId); } } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); if (requestDatacenter != null && !activeDownloadTransportTokens.containsKey(requestDatacenter.datacenterId) && !downloadTransportsToResume.contains(requestDatacenter.datacenterId)) { downloadTransportsToResume.add(requestDatacenter.datacenterId); } } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); if (requestDatacenter != null && !activeUploadTransportTokens.containsKey(requestDatacenter.datacenterId) && !uploadTransportsToResume.contains(requestDatacenter.datacenterId)) { uploadTransportsToResume.add(requestDatacenter.datacenterId); } } } boolean haveNetwork = true;//activeTransportTokens.size() != 0 || isNetworkOnline(); if (!activeTransportTokens.containsKey(currentDatacenterId) && !transportsToResume.contains(currentDatacenterId)) { transportsToResume.add(currentDatacenterId); } for (int it : transportsToResume) { Datacenter datacenter = datacenterWithId(it); if (datacenter.authKey != null) { if (datacenter.connection == null) { datacenter.connection = new TcpConnection(datacenter.datacenterId); datacenter.connection.delegate = this; datacenter.connection.transportRequestClass = RPCRequest.RPCRequestClassGeneric; } datacenter.connection.connect(); } } for (int it : downloadTransportsToResume) { Datacenter datacenter = datacenterWithId(it); if (datacenter.authKey != null) { if (datacenter.downloadConnection == null) { datacenter.downloadConnection = new TcpConnection(datacenter.datacenterId); datacenter.downloadConnection.delegate = this; datacenter.downloadConnection.transportRequestClass = RPCRequest.RPCRequestClassDownloadMedia; datacenter.authDownloadSessionId = getNewSessionId(); } datacenter.downloadConnection.connect(); } } for (int it : uploadTransportsToResume) { Datacenter datacenter = datacenterWithId(it); if (datacenter.authKey != null) { if (datacenter.uploadConnection == null) { datacenter.uploadConnection = new TcpConnection(datacenter.datacenterId); datacenter.uploadConnection.delegate = this; datacenter.uploadConnection.transportRequestClass = RPCRequest.RPCRequestClassUploadMedia; datacenter.authUploadSessionId = getNewSessionId(); } datacenter.uploadConnection.connect(); } } final HashMap> genericMessagesToDatacenters = new HashMap>(); final ArrayList unknownDatacenterIds = new ArrayList(); final ArrayList neededDatacenterIds = new ArrayList(); final ArrayList unauthorizedDatacenterIds = new ArrayList(); int currentTime = (int)(System.currentTimeMillis() / 1000); for (RPCRequest request : runningRequests) { if (updatingDcSettings && datacenters.size() > 1 && request.rawRequest instanceof TLRPC.TL_help_getConfig) { if (updatingDcStartTime < currentTime - 60) { updatingDcStartTime = currentTime; ArrayList allDc = new ArrayList(datacenters.values()); for (int a = 0; a < allDc.size(); a++) { Datacenter dc = allDc.get(a); if (dc.datacenterId == request.runningDatacenterId) { allDc.remove(a); break; } } Datacenter newDc = allDc.get(Math.abs(MessagesController.random.nextInt()) % allDc.size()); request.runningDatacenterId = newDc.datacenterId; } } int datacenterId = request.runningDatacenterId; if (datacenterId == DEFAULT_DATACENTER_ID) { if (movingToDatacenterId != DEFAULT_DATACENTER_ID) { continue; } datacenterId = currentDatacenterId; } Datacenter requestDatacenter = datacenterWithId(datacenterId); if (requestDatacenter == null) { if (!unknownDatacenterIds.contains(datacenterId)) { unknownDatacenterIds.add(datacenterId); } continue; } else if (requestDatacenter.authKey == null) { if (!neededDatacenterIds.contains(datacenterId)) { neededDatacenterIds.add(datacenterId); } continue; } else if (!requestDatacenter.authorized && request.runningDatacenterId != DEFAULT_DATACENTER_ID && request.runningDatacenterId != currentDatacenterId && (request.flags & RPCRequest.RPCRequestClassEnableUnauthorized) == 0) { if (!unauthorizedDatacenterIds.contains(datacenterId)) { unauthorizedDatacenterIds.add(datacenterId); } continue; } Integer tokenIt = activeTransportTokens.get(requestDatacenter.datacenterId); int datacenterTransportToken = tokenIt != null ? tokenIt : 0; double maxTimeout = 8.0; if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { if (datacenterTransportToken == 0) { continue; } } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { if (!haveNetwork) { FileLog.d("tmessages", "Don't have any network connection, skipping download request"); continue; } maxTimeout = 40.0; } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { if (!haveNetwork) { FileLog.d("tmessages", "Don't have any network connection, skipping upload request"); continue; } maxTimeout = 30.0; } long sessionId = 0; if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { sessionId = requestDatacenter.authSessionId; } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { sessionId = requestDatacenter.authDownloadSessionId; } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0 ) { sessionId = requestDatacenter.authUploadSessionId; } boolean forceThisRequest = (request.flags & requestClass) != 0 && (_datacenterId == Integer.MIN_VALUE || requestDatacenter.datacenterId == _datacenterId); if (request.rawRequest instanceof TLRPC.TL_get_future_salts || request.rawRequest instanceof TLRPC.TL_destroy_session) { if (request.runningMessageId != 0) { request.addRespondMessageId(request.runningMessageId); } request.runningMessageId = 0; request.runningMessageSeqNo = 0; request.transportChannelToken = 0; forceThisRequest = false; } if (((Math.abs(currentTime - request.runningStartTime) > maxTimeout) && (currentTime > request.runningMinStartTime || Math.abs(currentTime - request.runningMinStartTime) > 60.0)) || forceThisRequest) { if (!forceThisRequest && request.transportChannelToken > 0) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0 && datacenterTransportToken == request.transportChannelToken) { FileLog.d("tmessages", "Request token is valid, not retrying " + request.rawRequest); continue; } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { int downloadToken = requestDatacenter.downloadConnection.channelToken; if (downloadToken != 0 && request.transportChannelToken == downloadToken) { FileLog.d("tmessages", "Request download token is valid, not retrying " + request.rawRequest); continue; } } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { int uploadToken = requestDatacenter.uploadConnection.channelToken; if (uploadToken != 0 && request.transportChannelToken == uploadToken) { FileLog.d("tmessages", "Request upload token is valid, not retrying " + request.rawRequest); continue; } } } NetworkMessage networkMessage = new NetworkMessage(); networkMessage.protoMessage = new TLRPC.TL_protoMessage(); if (request.runningMessageSeqNo == 0) { request.runningMessageSeqNo = generateMessageSeqNo(sessionId, true); request.runningMessageId = generateMessageId(); } networkMessage.protoMessage.msg_id = request.runningMessageId; networkMessage.protoMessage.seqno = request.runningMessageSeqNo; networkMessage.protoMessage.bytes = request.serializedLength; networkMessage.protoMessage.body = request.rpcRequest; networkMessage.rawRequest = request.rawRequest; networkMessage.requestId = request.token; request.runningStartTime = currentTime; if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { request.transportChannelToken = datacenterTransportToken; addMessageToDatacenter(genericMessagesToDatacenters, requestDatacenter.datacenterId, networkMessage); } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { ArrayList arr = new ArrayList(); arr.add(networkMessage); proceedToSendingMessages(arr, sessionId, requestDatacenter.downloadConnection, false, false); } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { ArrayList arr = new ArrayList(); arr.add(networkMessage); proceedToSendingMessages(arr, sessionId, requestDatacenter.uploadConnection, false, false); } } } boolean updatingState = MessagesController.Instance.updatingState; if (activeTransportTokens.get(currentDatacenterId) != null) { if (!updatingState) { Datacenter currentDatacenter = datacenterWithId(currentDatacenterId); for (Long it : sessionsToDestroy) { if (destroyingSessions.contains(it)) { continue; } if (System.currentTimeMillis() / 1000 - lastDestroySessionRequestTime > 2.0) { lastDestroySessionRequestTime = (int)(System.currentTimeMillis() / 1000); TLRPC.TL_destroy_session destroySession = new TLRPC.TL_destroy_session(); destroySession.session_id = it; destroyingSessions.add(it); NetworkMessage networkMessage = new NetworkMessage(); networkMessage.protoMessage = wrapMessage(destroySession, currentDatacenter.authSessionId, false); if (networkMessage.protoMessage != null) { addMessageToDatacenter(genericMessagesToDatacenters, currentDatacenter.datacenterId, networkMessage); } } } } } int genericRunningRequestCount = 0; int uploadRunningRequestCount = 0; int downloadRunningRequestCount = 0; for (RPCRequest request : runningRequests) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { genericRunningRequestCount++; } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { uploadRunningRequestCount++; } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { downloadRunningRequestCount++; } } for (int i = 0; i < requestQueue.size(); i++) { RPCRequest request = requestQueue.get(i); if (request.cancelled) { requestQueue.remove(i); i--; continue; } if (updatingDcSettings && datacenters.size() > 1 && request.rawRequest instanceof TLRPC.TL_help_getConfig) { if (updatingDcStartTime < currentTime - 60) { updatingDcStartTime = currentTime; ArrayList allDc = new ArrayList(datacenters.values()); for (int a = 0; a < allDc.size(); a++) { Datacenter dc = allDc.get(a); if (dc.datacenterId == request.runningDatacenterId) { allDc.remove(a); break; } } Datacenter newDc = allDc.get(Math.abs(MessagesController.random.nextInt()) % allDc.size()); request.runningDatacenterId = newDc.datacenterId; } } int datacenterId = request.runningDatacenterId; if (datacenterId == DEFAULT_DATACENTER_ID) { if (movingToDatacenterId != DEFAULT_DATACENTER_ID && (request.flags & RPCRequest.RPCRequestClassEnableUnauthorized) == 0) { continue; } datacenterId = currentDatacenterId; } Datacenter requestDatacenter = datacenterWithId(datacenterId); if (requestDatacenter == null) { unknownDatacenterIds.add(datacenterId); continue; } else if (requestDatacenter.authKey == null) { neededDatacenterIds.add(datacenterId); continue; } else if (!requestDatacenter.authorized && request.runningDatacenterId != DEFAULT_DATACENTER_ID && request.runningDatacenterId != currentDatacenterId && (request.flags & RPCRequest.RPCRequestClassEnableUnauthorized) == 0) { unauthorizedDatacenterIds.add(datacenterId); continue; } if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0 && activeTransportTokens.get(requestDatacenter.datacenterId) == null) { continue; } if (updatingState && (request.rawRequest instanceof TLRPC.TL_account_updateStatus || request.rawRequest instanceof TLRPC.TL_account_registerDevice)) { continue; } if (request.requiresCompletion) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { if (genericRunningRequestCount >= 60) continue; genericRunningRequestCount++; Integer tokenIt = activeTransportTokens.get(requestDatacenter.datacenterId); request.transportChannelToken = tokenIt != null ? tokenIt : 0; } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { if (uploadRunningRequestCount >= 20) continue; if (!haveNetwork) { FileLog.d("tmessages", "Don't have any network connection, skipping upload request"); continue; } if (uploadRunningRequestCount >= 5) { continue; } uploadRunningRequestCount++; } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { if (!haveNetwork) { FileLog.d("tmessages", "Don't have any network connection, skipping download request"); continue; } if (downloadRunningRequestCount >= 5) { continue; } downloadRunningRequestCount++; } } long messageId = generateMessageId(); SerializedData os = new SerializedData(); request.rpcRequest.serializeToStream(os); if (os.length() != 0) { long sessionId = 0; if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { sessionId = requestDatacenter.authSessionId; } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { sessionId = requestDatacenter.authDownloadSessionId; } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { sessionId = requestDatacenter.authUploadSessionId; } NetworkMessage networkMessage = new NetworkMessage(); networkMessage.protoMessage = new TLRPC.TL_protoMessage(); networkMessage.protoMessage.msg_id = messageId; networkMessage.protoMessage.seqno = generateMessageSeqNo(sessionId, true); networkMessage.protoMessage.bytes = os.length(); networkMessage.protoMessage.body = request.rpcRequest; networkMessage.rawRequest = request.rawRequest; networkMessage.requestId = request.token; request.runningMessageId = messageId; request.runningMessageSeqNo = networkMessage.protoMessage.seqno; request.serializedLength = os.length(); request.runningStartTime = (int)(System.currentTimeMillis() / 1000); if (request.requiresCompletion) { runningRequests.add(request); } if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { addMessageToDatacenter(genericMessagesToDatacenters, requestDatacenter.datacenterId, networkMessage); } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { ArrayList arr = new ArrayList(); arr.add(networkMessage); proceedToSendingMessages(arr, sessionId, requestDatacenter.downloadConnection, false, false); } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { ArrayList arr = new ArrayList(); arr.add(networkMessage); proceedToSendingMessages(arr, sessionId, requestDatacenter.uploadConnection, false, false); } else { FileLog.e("tmessages", "***** Error: request " + request.rawRequest + " has undefined session"); } } else { FileLog.e("tmessages", "***** Couldn't serialize " + request.rawRequest); } requestQueue.remove(i); i--; } for (Datacenter datacenter : datacenters.values()) { if (genericMessagesToDatacenters.get(datacenter.datacenterId) == null && datacenter.connection != null && datacenter.connection.channelToken != 0) { ArrayList arr = messagesIdsForConfirmation.get(datacenter.authSessionId); if (arr != null && arr.size() != 0) { genericMessagesToDatacenters.put(datacenter.datacenterId, new ArrayList()); } } } for (int iter : genericMessagesToDatacenters.keySet()) { Datacenter datacenter = datacenterWithId(iter); if (datacenter != null) { boolean scannedPreviousRequests = false; long lastSendMessageRpcId = 0; boolean hasSendMessage = false; ArrayList arr = genericMessagesToDatacenters.get(iter); for (NetworkMessage networkMessage : arr) { TLRPC.TL_protoMessage message = networkMessage.protoMessage; Object rawRequest = networkMessage.rawRequest; if (rawRequest != null && (rawRequest instanceof TLRPC.TL_messages_sendMessage || rawRequest instanceof TLRPC.TL_messages_sendMedia || rawRequest instanceof TLRPC.TL_messages_forwardMessages || rawRequest instanceof TLRPC.TL_messages_sendEncrypted)) { if (rawRequest instanceof TLRPC.TL_messages_sendMessage) { hasSendMessage = true; } if (!scannedPreviousRequests) { scannedPreviousRequests = true; ArrayList currentRequests = new ArrayList(); for (NetworkMessage currentNetworkMessage : arr) { TLRPC.TL_protoMessage currentMessage = currentNetworkMessage.protoMessage; Object currentRawRequest = currentNetworkMessage.rawRequest; if (currentRawRequest instanceof TLRPC.TL_messages_sendMessage || currentRawRequest instanceof TLRPC.TL_messages_sendMedia || currentRawRequest instanceof TLRPC.TL_messages_forwardMessages || currentRawRequest instanceof TLRPC.TL_messages_sendEncrypted) { currentRequests.add(currentMessage.msg_id); } } long maxRequestId = 0; for (RPCRequest request : runningRequests) { if (request.rawRequest instanceof TLRPC.TL_messages_sendMessage || request.rawRequest instanceof TLRPC.TL_messages_sendMedia || request.rawRequest instanceof TLRPC.TL_messages_forwardMessages || request.rawRequest instanceof TLRPC.TL_messages_sendEncrypted) { if (!currentRequests.contains(request.runningMessageId)) { maxRequestId = Math.max(maxRequestId, request.runningMessageId); } } } lastSendMessageRpcId = maxRequestId; } if (lastSendMessageRpcId != 0 && lastSendMessageRpcId != message.msg_id) { TLRPC.TL_invokeAfterMsg invokeAfterMsg = new TLRPC.TL_invokeAfterMsg(); invokeAfterMsg.msg_id = lastSendMessageRpcId; invokeAfterMsg.query = message.body; message.body = invokeAfterMsg; message.bytes = message.bytes + 4 + 8; } lastSendMessageRpcId = message.msg_id; } } if (datacenter.connection == null) { datacenter.connection = new TcpConnection(datacenter.datacenterId); datacenter.connection.delegate = this; datacenter.connection.transportRequestClass = RPCRequest.RPCRequestClassGeneric; } proceedToSendingMessages(arr, datacenter.authSessionId, datacenter.connection, hasSendMessage, arr.size() != 0); } } if ((requestClass & RPCRequest.RPCRequestClassGeneric) != 0) { if (_datacenterId == Integer.MIN_VALUE) { for (Datacenter datacenter : datacenters.values()) { ArrayList messagesIt = genericMessagesToDatacenters.get(datacenter.datacenterId); if (messagesIt == null || messagesIt.size() == 0) { generatePing(datacenter); } } } else { ArrayList messagesIt = genericMessagesToDatacenters.get(_datacenterId); if (messagesIt == null || messagesIt.size() == 0) { generatePing(); } } } if (!unknownDatacenterIds.isEmpty() && !updatingDcSettings) { updateDcSettings(); } for (int num : neededDatacenterIds) { if (num != movingToDatacenterId) { boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof HandshakeAction) { HandshakeAction eactor = (HandshakeAction)actor; if (eactor.datacenter.datacenterId == num) { notFound = false; break; } } } if (notFound) { HandshakeAction actor = new HandshakeAction(datacenterWithId(num)); actor.delegate = this; dequeueActor(actor, true); } } } for (int num : unauthorizedDatacenterIds) { if (num != currentDatacenterId && num != movingToDatacenterId && UserConfig.clientUserId != 0) { boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof ExportAuthorizationAction) { ExportAuthorizationAction eactor = (ExportAuthorizationAction)actor; if (eactor.datacenter.datacenterId == num) { notFound = false; break; } } } if (notFound) { ExportAuthorizationAction actor = new ExportAuthorizationAction(datacenterWithId(num)); actor.delegate = this; dequeueActor(actor, true); } } } } void addMessageToDatacenter(HashMap> pMap, int datacenterId, NetworkMessage message) { ArrayList arr = pMap.get(datacenterId); if (arr == null) { arr = new ArrayList(); pMap.put(datacenterId, arr); } arr.add(message); } TLRPC.TL_protoMessage wrapMessage(TLObject message, long sessionId, boolean meaningful) { SerializedData os = new SerializedData(); message.serializeToStream(os); if (os.length() != 0) { TLRPC.TL_protoMessage protoMessage = new TLRPC.TL_protoMessage(); protoMessage.msg_id = generateMessageId(); protoMessage.bytes = os.length(); protoMessage.body = message; protoMessage.seqno = generateMessageSeqNo(sessionId, meaningful); return protoMessage; } else { FileLog.e("tmessages", "***** Couldn't serialize " + message); return null; } } void proceedToSendingMessages(ArrayList messageList, long sessionId, TcpConnection connection, boolean reportAck, boolean requestShortTimeout) { if (sessionId == 0) { return; } ArrayList messages = new ArrayList(); if(messageList != null) { messages.addAll(messageList); } final ArrayList arr = messagesIdsForConfirmation.get(sessionId); if (arr != null && arr.size() != 0) { TLRPC.TL_msgs_ack msgAck = new TLRPC.TL_msgs_ack(); msgAck.msg_ids = new ArrayList(); msgAck.msg_ids.addAll(arr); SerializedData os = new SerializedData(); msgAck.serializeToStream(os); if (os.length() != 0) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.protoMessage = new TLRPC.TL_protoMessage(); networkMessage.protoMessage.msg_id = generateMessageId(); networkMessage.protoMessage.seqno = generateMessageSeqNo(sessionId, false); networkMessage.protoMessage.bytes = os.length(); networkMessage.protoMessage.body = msgAck; messages.add(networkMessage); } else { FileLog.e("tmessages", "***** Couldn't serialize "); } arr.clear(); } sendMessagesToTransport(messages, connection, sessionId, reportAck, requestShortTimeout); } void sendMessagesToTransport(ArrayList messagesToSend, TcpConnection connection, long sessionId, boolean reportAck, boolean requestShortTimeout) { if (messagesToSend.size() == 0) { return; } if (connection == null) { FileLog.e("tmessages", String.format("***** Transport for session 0x%x not found", sessionId)); return; } ArrayList currentMessages = new ArrayList(); int currentSize = 0; for (int a = 0; a < messagesToSend.size(); a++) { NetworkMessage networkMessage = messagesToSend.get(a); currentMessages.add(networkMessage); TLRPC.TL_protoMessage protoMessage = networkMessage.protoMessage; currentSize += protoMessage.bytes; if (currentSize >= 3 * 1024 || a == messagesToSend.size() - 1) { ArrayList quickAckId = new ArrayList(); byte[] transportData = createConnectionData(currentMessages, sessionId, quickAckId, connection); if (transportData != null) { if (reportAck && quickAckId.size() != 0) { ArrayList requestIds = new ArrayList(); for (NetworkMessage message : messagesToSend) { if (message.requestId != 0) { requestIds.add(message.requestId); } } if (requestIds.size() != 0) { int ack = quickAckId.get(0); ArrayList arr = quickAckIdToRequestIds.get(ack); if (arr == null) { arr = new ArrayList(); quickAckIdToRequestIds.put(ack, arr); } arr.addAll(requestIds); } } connection.sendData(transportData, reportAck, requestShortTimeout); } else { FileLog.e("tmessages", "***** Transport data is nil"); } currentSize = 0; currentMessages.clear(); } } } @SuppressWarnings("unused") byte[] createConnectionData(ArrayList messages, long sessionId, ArrayList quickAckId, TcpConnection connection) { Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); if (datacenter.authKey == null) { return null; } long messageId; TLObject messageBody; int messageSeqNo; if (messages.size() == 1) { NetworkMessage networkMessage = messages.get(0); TLRPC.TL_protoMessage message = networkMessage.protoMessage; FileLog.d("tmessages", sessionId + ":Send message " + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); long msg_time = getTimeFromMsgId(message.msg_id); long currentTime = System.currentTimeMillis() + ((long)timeDifference) * 1000; if (msg_time < currentTime - 30000 || msg_time > currentTime + 25000) { FileLog.d("tmessages", "wrap in messages continaer"); TLRPC.TL_msg_container messageContainer = new TLRPC.TL_msg_container(); messageContainer.messages = new ArrayList(); messageContainer.messages.add(message); messageId = generateMessageId(); messageBody = messageContainer; messageSeqNo = generateMessageSeqNo(sessionId, false); } else { messageId = message.msg_id; messageBody = message.body; messageSeqNo = message.seqno; } } else { TLRPC.TL_msg_container messageContainer = new TLRPC.TL_msg_container(); ArrayList containerMessages = new ArrayList(messages.size()); for (NetworkMessage networkMessage : messages) { TLRPC.TL_protoMessage message = networkMessage.protoMessage; containerMessages.add(message); FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); } messageContainer.messages = containerMessages; messageId = generateMessageId(); messageBody = messageContainer; messageSeqNo = generateMessageSeqNo(sessionId, false); } SerializedData innerMessageOs = new SerializedData(); messageBody.serializeToStream(innerMessageOs); byte[] messageData = innerMessageOs.toByteArray(); SerializedData innerOs = new SerializedData(8 + 8 + 8 + 4 + 4 + messageData.length); long serverSalt = datacenter.selectServerSalt(getCurrentTime()); if (serverSalt == 0) { innerOs.writeInt64(0); } else { innerOs.writeInt64(serverSalt); } innerOs.writeInt64(sessionId); innerOs.writeInt64(messageId); innerOs.writeInt32(messageSeqNo); innerOs.writeInt32(messageData.length); innerOs.writeRaw(messageData); byte[] innerData = innerOs.toByteArray(); byte[] messageKeyFull = Utilities.computeSHA1(innerData); byte[] messageKey = new byte[16]; System.arraycopy(messageKeyFull, messageKeyFull.length - 16, messageKey, 0, 16); if (quickAckId != null) { SerializedData data = new SerializedData(messageKeyFull); quickAckId.add(data.readInt32() & 0x7fffffff); } MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, false); SerializedData dataForEncryption = new SerializedData(innerData.length + (innerData.length % 16)); dataForEncryption.writeRaw(innerData); byte[] b = new byte[1]; while (dataForEncryption.length() % 16 != 0) { MessagesController.random.nextBytes(b); dataForEncryption.writeByte(b[0]); } byte[] encryptedData = Utilities.aesIgeEncryption(dataForEncryption.toByteArray(), keyData.aesKey, keyData.aesIv, true, false); try { SerializedData data = new SerializedData(datacenter.authKeyId.length + messageKey.length + encryptedData.length); data.writeRaw(datacenter.authKeyId); data.writeRaw(messageKey); data.writeRaw(encryptedData); return data.toByteArray(); } catch (Exception e) { FileLog.e("tmessages", e); innerData = null; messageData = null; System.gc(); SerializedData data = new SerializedData(); data.writeRaw(datacenter.authKeyId); data.writeRaw(messageKey); data.writeRaw(encryptedData); return data.toByteArray(); } } void refillSaltSet(final Datacenter datacenter) { for (RPCRequest request : requestQueue) { if (request.rawRequest instanceof TLRPC.TL_get_future_salts) { return; } } for (RPCRequest request : runningRequests) { if (request.rawRequest instanceof TLRPC.TL_get_future_salts) { return; } } TLRPC.TL_get_future_salts getFutureSalts = new TLRPC.TL_get_future_salts(); getFutureSalts.num = 64; performRpc(getFutureSalts, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { TLRPC.TL_futuresalts res = (TLRPC.TL_futuresalts)response; if (error == null) { int currentTime = getCurrentTime(); datacenter.mergeServerSalts(currentTime, res.salts); saveSession(); } } }, null, true, RPCRequest.RPCRequestClassGeneric, datacenter.datacenterId); } void messagesConfirmed(final long requestMsgId) { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { for (RPCRequest request : runningRequests) { if (requestMsgId == request.runningMessageId) { request.confirmed = true; } } } }); } void rpcCompleted(final long requestMsgId) { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { for (int i = 0; i < runningRequests.size(); i++) { RPCRequest request = runningRequests.get(i); removeRequestInClass(request.token); if (request.respondsToMessageId(requestMsgId)) { runningRequests.remove(i); i--; } } } }); } void processMessage(TLObject message, long messageId, int messageSeqNo, long messageSalt, TcpConnection connection, long sessionId, long innerMsgId, long containerMessageId) { if (message == null) { FileLog.e("tmessages", "message is null"); return; } Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); if (message instanceof TLRPC.TL_new_session_created) { TLRPC.TL_new_session_created newSession = (TLRPC.TL_new_session_created)message; ArrayList arr = processedSessionChanges.get(sessionId); if (arr == null) { arr = new ArrayList(); processedSessionChanges.put(sessionId, arr); } if (!arr.contains(newSession.unique_id)) { FileLog.d("tmessages", "New session:"); FileLog.d("tmessages", String.format(" first message id: %d", newSession.first_msg_id)); FileLog.d("tmessages", String.format(" server salt: %d", newSession.server_salt)); FileLog.d("tmessages", String.format(" unique id: %d", newSession.unique_id)); long serverSalt = newSession.server_salt; ServerSalt serverSaltDesc = new ServerSalt(); serverSaltDesc.validSince = getCurrentTime(); serverSaltDesc.validUntil = getCurrentTime() + 30 * 60; serverSaltDesc.value = serverSalt; datacenter.addServerSalt(serverSaltDesc); for (RPCRequest request : runningRequests) { Datacenter dcenter = datacenterWithId(request.runningDatacenterId); if (request.runningMessageId < newSession.first_msg_id && (request.flags & connection.transportRequestClass) != 0 && dcenter != null && dcenter.datacenterId == datacenter.datacenterId) { request.runningMessageId = 0; request.runningMessageSeqNo = 0; request.runningStartTime = 0; request.runningMinStartTime = 0; request.transportChannelToken = 0; } } saveSession(); if (sessionId == datacenter.authSessionId && datacenter.datacenterId == currentDatacenterId && UserConfig.clientActivated) { MessagesController.Instance.getDifference(); } arr.add(newSession.unique_id); } } else if (message instanceof TLRPC.TL_msg_container) { /*if (messageId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); }*/ TLRPC.TL_msg_container messageContainer = (TLRPC.TL_msg_container)message; for (TLRPC.TL_protoMessage innerMessage : messageContainer.messages) { long innerMessageId = innerMessage.msg_id; if (innerMessage.seqno % 2 != 0) { ArrayList set = messagesIdsForConfirmation.get(sessionId); if (set == null) { set = new ArrayList(); messagesIdsForConfirmation.put(sessionId, set); } set.add(innerMessageId); } if (isMessageIdProcessed(sessionId, innerMessageId)) { continue; } processMessage(innerMessage.body, 0, innerMessage.seqno, messageSalt, connection, sessionId, innerMessageId, messageId); addProcessedMessageId(sessionId, innerMessageId); } } else if (message instanceof TLRPC.TL_pong) { TLRPC.TL_pong pong = (TLRPC.TL_pong)message; long pingId = pong.ping_id; ArrayList itemsToDelete = new ArrayList(); for (Long pid : pingIdToDate.keySet()) { if (pid == pingId) { int time = pingIdToDate.get(pid); int pingTime = (int)(System.currentTimeMillis() / 1000) - time; if (Math.abs(pingTime) < 10) { currentPingTime = (pingTime + currentPingTime) / 2; if (messageId != 0) { long timeMessage = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); timeDifference = (int)((timeMessage - currentTime) / 1000 - currentPingTime / 2.0); } } itemsToDelete.add(pid); } else if (pid < pingId) { itemsToDelete.add(pid); } } for (Long pid : itemsToDelete) { pingIdToDate.remove(pid); } } else if (message instanceof TLRPC.TL_futuresalts) { TLRPC.TL_futuresalts futureSalts = (TLRPC.TL_futuresalts)message; long requestMid = futureSalts.req_msg_id; for (RPCRequest request : runningRequests) { if (request.respondsToMessageId(requestMid)) { if (request.completionBlock != null) { request.completionBlock.run(futureSalts, null); } messagesConfirmed(requestMid); rpcCompleted(requestMid); break; } } } else if (message instanceof TLRPC.DestroySessionRes) { TLRPC.DestroySessionRes res = (TLRPC.DestroySessionRes)message; ArrayList lst = new ArrayList(); lst.addAll(sessionsToDestroy); destroyingSessions.remove(res.session_id); for (long session : lst) { if (session == res.session_id) { sessionsToDestroy.remove(session); FileLog.d("tmessages", String.format("Destroyed session %d (%s)", res.session_id, res instanceof TLRPC.TL_destroy_session_ok ? "ok" : "not found")); break; } } } else if (message instanceof TLRPC.TL_rpc_result) { TLRPC.TL_rpc_result resultContainer = (TLRPC.TL_rpc_result)message; long resultMid = resultContainer.req_msg_id; boolean ignoreResult = false; FileLog.d("tmessages", "object in rpc_result is " + resultContainer.result); if (resultContainer.result instanceof TLRPC.RpcError) { String errorMessage = ((TLRPC.RpcError)resultContainer.result).error_message; FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError)resultContainer.result).error_code, errorMessage)); int migrateToDatacenterId = DEFAULT_DATACENTER_ID; if (((TLRPC.RpcError)resultContainer.result).error_code == 303) { ArrayList migrateErrors = new ArrayList(); migrateErrors.add("NETWORK_MIGRATE_"); migrateErrors.add("PHONE_MIGRATE_"); migrateErrors.add("USER_MIGRATE_"); for (String possibleError : migrateErrors) { if (errorMessage.contains(possibleError)) { String errorMsg = errorMessage.replace(possibleError, ""); Pattern pattern = Pattern.compile("[0-9]+"); Matcher matcher = pattern.matcher(errorMsg); if (matcher.find()) { errorMsg = matcher.group(0); } Integer val; try { val = Integer.parseInt(errorMsg); } catch (Exception e) { val = null; } if (val != null) { migrateToDatacenterId = val; } else { migrateToDatacenterId = DEFAULT_DATACENTER_ID; } } } } if (migrateToDatacenterId != DEFAULT_DATACENTER_ID) { ignoreResult = true; moveToDatacenter(migrateToDatacenterId); } } int retryRequestsFromDatacenter = -1; int retryRequestsClass = 0; if (!ignoreResult) { boolean found = false; for (RPCRequest request : runningRequests) { if (request.respondsToMessageId(resultMid)) { found = true; boolean discardResponse = false; boolean isError = false; if (request.completionBlock != null) { TLRPC.TL_error implicitError = null; if (resultContainer.result instanceof TLRPC.TL_gzip_packed) { TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)resultContainer.result; TLObject uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); if (uncomressed == null) { System.gc(); uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); } if (uncomressed == null) { throw new RuntimeException("failed to decomress responce for " + request.rawRequest); } resultContainer.result = uncomressed; } if (resultContainer.result instanceof TLRPC.RpcError) { String errorMessage = ((TLRPC.RpcError) resultContainer.result).error_message; FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError) resultContainer.result).error_code, errorMessage)); int errorCode = ((TLRPC.RpcError) resultContainer.result).error_code; if (errorCode == 500 || errorCode < 0) { if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) != 0) { if (request.serverFailureCount < 1) { discardResponse = true; request.runningMinStartTime = request.runningStartTime + 1; request.serverFailureCount++; } } else { discardResponse = true; request.runningMinStartTime = request.runningStartTime + 1; request.confirmed = false; } } else if (errorCode == 420) { if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) == 0) { double waitTime = 2.0; if (errorMessage.contains("FLOOD_WAIT_")) { String errorMsg = errorMessage.replace("FLOOD_WAIT_", ""); Pattern pattern = Pattern.compile("[0-9]+"); Matcher matcher = pattern.matcher(errorMsg); if (matcher.find()) { errorMsg = matcher.group(0); } Integer val; try { val = Integer.parseInt(errorMsg); } catch (Exception e) { val = null; } if (val != null) { waitTime = val; } } waitTime = Math.min(30, waitTime); discardResponse = true; request.runningMinStartTime = (int)(System.currentTimeMillis() / 1000 + waitTime); request.confirmed = false; } } implicitError = new TLRPC.TL_error(); implicitError.code = ((TLRPC.RpcError)resultContainer.result).error_code; implicitError.text = ((TLRPC.RpcError)resultContainer.result).error_message; } else if (!(resultContainer.result instanceof TLRPC.TL_error)) { if (request.rawRequest == null || !request.rawRequest.responseClass().isAssignableFrom(resultContainer.result.getClass())) { if (request.rawRequest == null) { FileLog.e("tmessages", "rawRequest is null"); } else { FileLog.e("tmessages", "***** RPC error: invalid response class " + resultContainer.result + " (" + request.rawRequest.responseClass() + " expected)"); } implicitError = new TLRPC.TL_error(); implicitError.code = -1000; } } if (!discardResponse) { if (implicitError != null || resultContainer.result instanceof TLRPC.TL_error) { isError = true; request.completionBlock.run(null, implicitError != null ? implicitError : (TLRPC.TL_error) resultContainer.result); } else { request.completionBlock.run(resultContainer.result, null); } } if (implicitError != null && implicitError.code == 401) { isError = true; if (datacenter.datacenterId == currentDatacenterId || datacenter.datacenterId == movingToDatacenterId) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { NotificationCenter.Instance.postNotificationName(1234); UserConfig.clearConfig(); } }); } } else { datacenter.authorized = false; saveSession(); discardResponse = true; if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { retryRequestsFromDatacenter = datacenter.datacenterId; retryRequestsClass = request.flags; } } } } if (!discardResponse) { if (request.initRequest && !isError) { if (datacenter.lastInitVersion != currentAppVersion) { datacenter.lastInitVersion = currentAppVersion; saveSession(); FileLog.e("tmessages", "init connection completed"); } else { FileLog.e("tmessages", "rpc is init, but init connection already completed"); } } rpcCompleted(resultMid); } else { request.runningMessageId = 0; request.runningMessageSeqNo = 0; request.transportChannelToken = 0; } break; } } if (!found) { FileLog.d("tmessages", "Response received, but request wasn't found."); rpcCompleted(resultMid); } messagesConfirmed(resultMid); } if (retryRequestsFromDatacenter >= 0) { processRequestQueue(retryRequestsClass, retryRequestsFromDatacenter); } else { processRequestQueue(0, 0); } } else if (message instanceof TLRPC.TL_msgs_ack) { } else if (message instanceof TLRPC.TL_ping) { } else if (message instanceof TLRPC.TL_bad_msg_notification) { TLRPC.TL_bad_msg_notification badMsgNotification = (TLRPC.TL_bad_msg_notification)message; FileLog.e("tmessages", String.format("***** Bad message: %d", badMsgNotification.error_code)); if (badMsgNotification.error_code == 16 || badMsgNotification.error_code == 17 || badMsgNotification.error_code == 19 || badMsgNotification.error_code == 32 || badMsgNotification.error_code == 33 || badMsgNotification.error_code == 64) { long realId = messageId != 0 ? messageId : containerMessageId; if (realId == 0) { realId = innerMsgId; } if (realId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); } recreateSession(datacenter.authSessionId, datacenter); saveSession(); lastOutgoingMessageId = 0; clearRequestsForRequestClass(connection.transportRequestClass, datacenter); } } else if (message instanceof TLRPC.TL_bad_server_salt) { if (messageId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); lastOutgoingMessageId = Math.max(messageId, lastOutgoingMessageId); } datacenter.clearServerSalts(); ServerSalt serverSaltDesc = new ServerSalt(); serverSaltDesc.validSince = getCurrentTime(); serverSaltDesc.validUntil = getCurrentTime() + 30 * 60; serverSaltDesc.value = messageSalt; datacenter.addServerSalt(serverSaltDesc); saveSession(); refillSaltSet(datacenter); if (datacenter.authKey != null) { processRequestQueue(RPCRequest.RPCRequestClassTransportMask, datacenter.datacenterId); } } else if (message instanceof TLRPC.MsgDetailedInfo) { TLRPC.MsgDetailedInfo detailedInfo = (TLRPC.MsgDetailedInfo)message; boolean requestResend = false; if (detailedInfo instanceof TLRPC.TL_msg_detailed_info) { long requestMid = ((TLRPC.TL_msg_detailed_info)detailedInfo).msg_id; for (RPCRequest request : runningRequests) { if (request.respondsToMessageId(requestMid)) { requestResend = true; break; } } } else { if (!isMessageIdProcessed(sessionId, messageId)) { requestResend = true; } } if (requestResend) { TLRPC.TL_msg_resend_req resendReq = new TLRPC.TL_msg_resend_req(); resendReq.msg_ids.add(detailedInfo.answer_msg_id); NetworkMessage networkMessage = new NetworkMessage(); networkMessage.protoMessage = wrapMessage(resendReq, sessionId, false); ArrayList arr = new ArrayList(); arr.add(networkMessage); sendMessagesToTransport(arr, connection, sessionId, false, true); } else { ArrayList set = messagesIdsForConfirmation.get(sessionId); if (set == null) { set = new ArrayList(); messagesIdsForConfirmation.put(sessionId, set); } set.add(detailedInfo.answer_msg_id); } } else if (message instanceof TLRPC.TL_gzip_packed) { TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)message; TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId)); processMessage(result, messageId, messageSeqNo, messageSalt, connection, sessionId, innerMsgId, containerMessageId); } else if (message instanceof TLRPC.Updates) { MessagesController.Instance.processUpdates((TLRPC.Updates)message); } else { FileLog.e("tmessages", "***** Error: unknown message class " + message); } } void generatePing() { for (Datacenter datacenter : datacenters.values()) { if (datacenter.datacenterId == currentDatacenterId) { generatePing(datacenter); } } } static long nextPingId = 0; byte[] generatePingData(Datacenter datacenter, boolean recordTime) { long sessionId = datacenter.authSessionId; if (sessionId == 0) { return null; } TLRPC.TL_ping ping = new TLRPC.TL_ping(); ping.ping_id = nextPingId++; if (recordTime && sessionId == datacenter.authSessionId) { pingIdToDate.put(ping.ping_id, (int)(System.currentTimeMillis() / 1000)); } NetworkMessage networkMessage = new NetworkMessage(); networkMessage.protoMessage = wrapMessage(ping, sessionId, false); ArrayList arr = new ArrayList(); arr.add(networkMessage); return createConnectionData(arr, sessionId, null, datacenter.connection); } void generatePing(Datacenter datacenter) { if (datacenter.connection == null || datacenter.connection.channelToken == 0) { return; } byte[] transportData = generatePingData(datacenter, true); if (transportData != null) { datacenter.connection.sendData(transportData, false, true); } } public long needsToDecodeMessageIdFromPartialData(TcpConnection connection, byte[] data) { if (data == null) { return -1; } Datacenter datacenter = datacenters.get(connection.getDatacenterId()); SerializedData is = new SerializedData(data); byte[] keyId = is.readData(8); SerializedData keyIdData = new SerializedData(keyId); if (keyIdData.readInt64() == 0) { return -1; } else { if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { FileLog.e("tmessages", "Error: invalid auth key id " + connection); return -1; } byte[] messageKey = is.readData(16); MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); byte[] messageData = is.readData(data.length - 24); messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); if (messageData == null) { return -1; } SerializedData messageIs = new SerializedData(messageData); long messageServerSalt = messageIs.readInt64(); long messageSessionId = messageIs.readInt64(); if (messageSessionId != datacenter.authSessionId && messageSessionId != datacenter.authDownloadSessionId && messageSessionId != datacenter.authUploadSessionId) { FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, datacenter.authSessionId)); finishUpdatingState(connection); return -1; } long messageId = messageIs.readInt64(); int messageSeqNo = messageIs.readInt32(); int messageLength = messageIs.readInt32(); boolean[] stop = new boolean[1]; long[] reqMsgId = new long[1]; stop[0] = false; reqMsgId[0] = 0; while (!stop[0] && reqMsgId[0] == 0) { int signature = messageIs.readInt32(stop); if (stop[0]) { break; } findReqMsgId(messageIs, signature, reqMsgId, stop); } return reqMsgId[0]; } } private void findReqMsgId(SerializedData is, int signature, long[] reqMsgId, boolean[] failed) { if (signature == 0x73f1f8dc) { if (is.length() < 4) { failed[0] = true; return; } int count = is.readInt32(failed); if (failed[0]) { return; } for (int i = 0; i < count; i++) { is.readInt64(failed); if (failed[0]) { return; } is.readInt32(failed); if (failed[0]) { return; } is.readInt32(failed); if (failed[0]) { return; } int innerSignature = is.readInt32(failed); if (failed[0]) { return; } findReqMsgId(is, innerSignature, reqMsgId, failed); if (failed[0] || reqMsgId[0] != 0) { return; } } } else if (signature == 0xf35c6d01) { long value = is.readInt64(failed); if (failed[0]) { return; } reqMsgId[0] = value; } else if (signature == 0x62d6b459) { is.readInt32(failed); if (failed[0]) { return; } int count = is.readInt32(failed); if (failed[0]) { return; } for (int i = 0; i < count; i++) { is.readInt32(failed); if (failed[0]) { return; } } } else if (signature == 0x347773c5) { is.readInt64(failed); if (failed[0]) { return; } is.readInt64(failed); } } //================================================================================ // TCPConnection delegate //================================================================================ @Override public void tcpConnectionProgressChanged(TcpConnection connection, long messageId, int currentSize, int length) { for (RPCRequest request : runningRequests) { if (request.respondsToMessageId(messageId)) { if (request.progressBlock != null) { request.progressBlock.progress(length, currentSize); } break; } } } @Override public void tcpConnectionClosed(TcpConnection connection) { if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { if (isNetworkOnline()) { connectionState = 2; } else { connectionState = 1; } final int stateCopy = connectionState; Utilities.RunOnUIThread(new Runnable() { @Override public void run() { NotificationCenter.Instance.postNotificationName(703, stateCopy); } }); } } @Override public void tcpConnectionConnected(TcpConnection connection) { Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); if (datacenter.authKey != null) { processRequestQueue(connection.transportRequestClass, connection.getDatacenterId()); } } @Override public void tcpConnectionQuiackAckReceived(TcpConnection connection, int ack) { ArrayList arr = quickAckIdToRequestIds.get(ack); if (arr != null) { for (RPCRequest request : runningRequests) { if (arr.contains(request.token)) { if (request.quickAckBlock != null) { request.quickAckBlock.quickAck(); } } } quickAckIdToRequestIds.remove(ack); } } private void finishUpdatingState(TcpConnection connection) { if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { if (ConnectionsManager.Instance.connectionState == 3 && !MessagesController.Instance.gettingDifference && !MessagesController.Instance.gettingDifferenceAgain) { ConnectionsManager.Instance.connectionState = 0; final int stateCopy = ConnectionsManager.Instance.connectionState; Utilities.RunOnUIThread(new Runnable() { @Override public void run() { NotificationCenter.Instance.postNotificationName(703, stateCopy); } }); } } } @Override public void tcpConnectionReceivedData(TcpConnection connection, byte[] data) { if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { if (connectionState == 1 || connectionState == 2) { connectionState = 3; final int stateCopy = connectionState; Utilities.RunOnUIThread(new Runnable() { @Override public void run() { NotificationCenter.Instance.postNotificationName(703, stateCopy); } }); } } Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); SerializedData is = new SerializedData(data); byte[] keyId = is.readData(8); SerializedData keyIdData = new SerializedData(keyId); if (keyIdData.readInt64() == 0) { long messageId = is.readInt64(); if (isMessageIdProcessed(0, messageId)) { finishUpdatingState(connection); return; } int messageLength = is.readInt32(); int constructor = is.readInt32(); TLObject object = TLClassStore.Instance().TLdeserialize(is, constructor, getRequestWithMessageId(messageId)); processMessage(object, messageId, 0, 0, connection, 0, 0, 0); if (object != null) { addProcessedMessageId(0, messageId); } } else { if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { FileLog.e("tmessages", "Error: invalid auth key id " + connection); return; } byte[] messageKey = is.readData(16); MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); byte[] messageData = is.readData(data.length - 24); messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); if (messageData == null) { FileLog.e("tmessages", "Error: can't decrypt message data " + connection); return; } SerializedData messageIs = new SerializedData(messageData); long messageServerSalt = messageIs.readInt64(); long messageSessionId = messageIs.readInt64(); if (messageSessionId != datacenter.authSessionId && messageSessionId != datacenter.authDownloadSessionId && messageSessionId != datacenter.authUploadSessionId) { FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, datacenter.authSessionId)); finishUpdatingState(connection); return; } boolean doNotProcess = false; long messageId = messageIs.readInt64(); int messageSeqNo = messageIs.readInt32(); int messageLength = messageIs.readInt32(); if (isMessageIdProcessed(messageSessionId, messageId)) { doNotProcess = true; } if (messageSeqNo % 2 != 0) { ArrayList set = messagesIdsForConfirmation.get(messageSessionId); if (set == null) { set = new ArrayList(); messagesIdsForConfirmation.put(messageSessionId, set); } set.add(messageId); } byte[] realMessageKeyFull = Utilities.computeSHA1(messageData, 0, Math.min(messageLength + 32, messageData.length)); if (realMessageKeyFull == null) { return; } byte[] realMessageKey = new byte[16]; System.arraycopy(realMessageKeyFull, realMessageKeyFull.length - 16, realMessageKey, 0, 16); if (!Arrays.equals(messageKey, realMessageKey)) { FileLog.e("tmessages", "***** Error: invalid message key"); return; } if (!doNotProcess) { int constructor = messageIs.readInt32(); TLObject message = TLClassStore.Instance().TLdeserialize(messageIs, constructor, getRequestWithMessageId(messageId)); if (message == null) { FileLog.e("tmessages", "***** Error parsing message: " + constructor); } else { processMessage(message, messageId, messageSeqNo, messageServerSalt, connection, messageSessionId, 0, 0); addProcessedMessageId(messageSessionId, messageId); } } else { proceedToSendingMessages(null, messageSessionId, connection, false, false); } finishUpdatingState(connection); } } public TLObject getRequestWithMessageId(long msgId) { for (RPCRequest request : runningRequests) { if (msgId == request.runningMessageId) { return request.rawRequest; } } return null; } //================================================================================ // Move to datacenter manage //================================================================================ void moveToDatacenter(final int datacenterId) { if (movingToDatacenterId == datacenterId) { return; } movingToDatacenterId = datacenterId; Datacenter currentDatacenter = datacenterWithId(currentDatacenterId); clearRequestsForRequestClass(RPCRequest.RPCRequestClassGeneric, currentDatacenter); clearRequestsForRequestClass(RPCRequest.RPCRequestClassDownloadMedia, currentDatacenter); clearRequestsForRequestClass(RPCRequest.RPCRequestClassUploadMedia, currentDatacenter); if (UserConfig.clientUserId != 0) { TLRPC.TL_auth_exportAuthorization exportAuthorization = new TLRPC.TL_auth_exportAuthorization(); exportAuthorization.dc_id = datacenterId; performRpc(exportAuthorization, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { movingAuthorization = (TLRPC.TL_auth_exportedAuthorization)response; authorizeOnMovingDatacenter(); } else { Utilities.globalQueue.postRunnable(new Runnable() { @Override public void run() { moveToDatacenter(datacenterId); } }, 1000); } } }, null, true, RPCRequest.RPCRequestClassGeneric, currentDatacenterId); } else { authorizeOnMovingDatacenter(); } } void authorizeOnMovingDatacenter() { Datacenter datacenter = datacenterWithId(movingToDatacenterId); if (datacenter == null) { if (!updatingDcSettings) { updateDcSettings(); } return; } recreateSession(datacenter.authSessionId, datacenter); if (datacenter.authKey == null) { datacenter.clearServerSalts(); HandshakeAction actor = new HandshakeAction(datacenter); actor.delegate = this; dequeueActor(actor, true); } if (movingAuthorization != null) { TLRPC.TL_auth_importAuthorization importAuthorization = new TLRPC.TL_auth_importAuthorization(); importAuthorization.id = UserConfig.clientUserId; importAuthorization.bytes = movingAuthorization.bytes; performRpc(importAuthorization, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { movingAuthorization = null; if (error == null) { authorizedOnMovingDatacenter(); } else { moveToDatacenter(movingToDatacenterId); } } }, null, true, RPCRequest.RPCRequestClassGeneric, datacenter.datacenterId); } else { authorizedOnMovingDatacenter(); } } void authorizedOnMovingDatacenter() { Datacenter datacenter = datacenterWithId(currentDatacenterId); if (datacenter != null && datacenter.connection != null) { datacenter.connection.suspendConnection(true); } movingAuthorization = null; currentDatacenterId = movingToDatacenterId; movingToDatacenterId = DEFAULT_DATACENTER_ID; saveSession(); processRequestQueue(0, 0); } //================================================================================ // Actors manage //================================================================================ public void dequeueActor(final Action actor, final boolean execute) { if (actionQueue.size() == 0 || execute) { actor.execute(null); } actionQueue.add(actor); } public void cancelActor(final Action actor) { if (actor != null) { actionQueue.remove(actor); } } @Override public void ActionDidFinishExecution(final Action action, HashMap params) { if (action instanceof HandshakeAction) { HandshakeAction eactor = (HandshakeAction)action; eactor.datacenter.connection.delegate = this; saveSession(); if (eactor.datacenter.datacenterId == currentDatacenterId || eactor.datacenter.datacenterId == movingToDatacenterId) { timeDifference = (Integer)params.get("timeDifference"); recreateSession(eactor.datacenter.authSessionId, eactor.datacenter); } processRequestQueue(RPCRequest.RPCRequestClassTransportMask, eactor.datacenter.datacenterId); } else if (action instanceof ExportAuthorizationAction) { ExportAuthorizationAction eactor = (ExportAuthorizationAction)action; Datacenter datacenter = eactor.datacenter; datacenter.authorized = true; saveSession(); processRequestQueue(RPCRequest.RPCRequestClassTransportMask, datacenter.datacenterId); } Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { actionQueue.remove(action); action.delegate = null; } }); } @Override public void ActionDidFailExecution(final Action action) { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { actionQueue.remove(action); action.delegate = null; } }); } }