├── .gitignore ├── AndroidManifest.xml ├── README.md ├── res ├── drawable-hdpi │ ├── app_icon.png │ └── ic_contact_picture.png ├── drawable-mdpi │ ├── app_icon.png │ └── ic_contact_picture.png ├── drawable-xhdpi │ ├── app_icon.png │ ├── ic_contact_picture.png │ ├── ic_google_selected.png │ ├── notification_system_update_available.png │ ├── notification_system_update_download_failure.png │ ├── stat_notify_msg.png │ └── stat_notify_talk_text.png ├── layout │ └── first_time_config.xml └── values │ └── strings.xml └── src ├── com └── google │ └── android │ ├── gsf │ ├── checkin │ │ ├── CheckinService.java │ │ └── EventLogService.java │ ├── gservices │ │ ├── DatabaseHelper.java │ │ └── GservicesProvider.java │ ├── gtalkservice │ │ ├── PushMessagingRegistrar.java │ │ ├── PushMessagingRegistrarProxy.java │ │ └── service │ │ │ ├── Account.java │ │ │ ├── BackendHandler.java │ │ │ ├── GTalkConnection.java │ │ │ ├── GTalkService.java │ │ │ ├── GTalkServiceBinder.java │ │ │ ├── ImSession.java │ │ │ └── Notifier.java │ ├── loginservice │ │ ├── MigrateToAccountManagerBroadcastReceiver.java │ │ └── ServicesWatcher.java │ ├── settings │ │ ├── DatabaseHelper.java │ │ ├── GoogleSettingsProvider.java │ │ ├── SqlArguments.java │ │ └── UseLocationForServicesActivity.java │ ├── subscribedfeeds │ │ ├── GCMIntentService.java │ │ ├── SubscribedFeedsGCMBroadcastReceiver.java │ │ └── SubscribedFeedsProvider.java │ └── talk │ │ └── TalkProvider.java │ └── gtalkservice │ ├── ConnectionError.aidl │ ├── ConnectionError.java │ ├── ConnectionState.aidl │ ├── ConnectionState.java │ ├── GroupChatInvitation.aidl │ ├── GroupChatInvitation.java │ ├── IChatListener.aidl │ ├── IChatSession.aidl │ ├── IConnectionStateListener.aidl │ ├── IGTalkConnection.aidl │ ├── IGTalkConnectionListener.aidl │ ├── IGTalkService.aidl │ ├── IGroupChatInvitationListener.aidl │ ├── IHttpRequestCallback.aidl │ ├── IImSession.aidl │ ├── IJingleInfoStanzaListener.aidl │ ├── IRosterListener.aidl │ ├── ISessionStanzaListener.aidl │ ├── Presence.aidl │ └── Presence.java └── org └── microg └── gsf └── deviceid ├── DatabaseHelper.java ├── FirstTimeConfig.java └── ManagerService.java /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 16 | 22 | 28 | 34 | 40 | 46 | 52 | 58 | 64 | 70 | 76 | 82 | 88 | 94 | 100 | 106 | 112 | 118 | 124 | 130 | 136 | 142 | 148 | 154 | 160 | 166 | 172 | 178 | 184 | 190 | 196 | 202 | 208 | 214 | 220 | 226 | 232 | 238 | 244 | 250 | 256 | 262 | 268 | 274 | 280 | 286 | 292 | 297 | 302 | 307 | 313 | 318 | 321 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 389 | 390 | 393 | 396 | 397 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 412 | 413 | 416 | 417 | 420 | 421 | 422 | 423 | 424 | 425 | 432 | 435 | 436 | 437 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 493 | 494 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GoogleServicesFramework 2 | ======================= 3 | 4 | Clone of Google's proprietary GoogleServicesFramework for Android. Part of the μg Project. 5 | 6 | ###License 7 | > Copyright 2012 μg Project Team 8 | 9 | > Licensed under the Apache License, Version 2.0 (the "License"); 10 | > you may not use this file except in compliance with the License. 11 | > You may obtain a copy of the License at 12 | 13 | > http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | > Unless required by applicable law or agreed to in writing, software 16 | > distributed under the License is distributed on an "AS IS" BASIS, 17 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | > See the License for the specific language governing permissions and 19 | > limitations under the License. -------------------------------------------------------------------------------- /res/drawable-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-hdpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_contact_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-hdpi/ic_contact_picture.png -------------------------------------------------------------------------------- /res/drawable-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-mdpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_contact_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-mdpi/ic_contact_picture.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_contact_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/ic_contact_picture.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_google_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/ic_google_selected.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/notification_system_update_available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/notification_system_update_available.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/notification_system_update_download_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/notification_system_update_download_failure.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/stat_notify_msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/stat_notify_msg.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/stat_notify_talk_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microg/GoogleServicesFramework/92855818450652eb25683f29fe37df2e6301ac37/res/drawable-xhdpi/stat_notify_talk_text.png -------------------------------------------------------------------------------- /res/layout/first_time_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 21 | 22 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | android-google 4 | GoogleServicesFramework 5 | View Google accounts 6 | Allows app to view the email address of all Google accounts associated with this device. 7 | All Google services 8 | Allows app to access all Google services through any associated Google account. 9 | Google Mail 10 | Allows app to access Google Mail through any associated Google account. 11 | Google Calendar 12 | Allows app to access Google Calendar through any associated Google account. 13 | Android services 14 | Allows app to access Android services through any associated Google account. 15 | YouTube 16 | Allows app to access YouTube through any associated Google account. 17 | Google Talk 18 | Allows app to access Google Talk through any associated Google account. 19 | iGoogle 20 | Allows app to access iGoogle through any associated Google account. 21 | Picasa Web Albums 22 | Allows app to access Picasa Web Albums through any associated Google account. 23 | AdSense 24 | Allows app to access AdSense through any associated Google account. 25 | AdWords 26 | Allows app to access AdWords through any associated Google account. 27 | Blogger 28 | Allows app to access Blogger through any associated Google account. 29 | 30 | 31 | read instant messages 32 | Allows apps to read data from the Google Talk content provider. 33 | write instant messages 34 | Allows apps to write data to the Google Talk content provider. 35 | Exchanges messages and receives sync notifications from Google servers. 36 | Used for server cloud to device messages and for sync notifications. Google Talk uses this service to exchange messages and to synchronize presence status. Malicious apps could use this service to transmit excess data. 37 | receive data from Internet 38 | "Allows apps to accept cloud to device messages sent by the app's service. Using this service will incur data usage. Malicious apps could cause excess data usage." 39 | Broadcast data messages to apps. 40 | Can broadcast data messages received from the Internet to apps registered to listen for them. 41 | send and receive XMPP messages to and from Google servers 42 | Allows Google apps to send and receive XMPP messages to and from Google servers. 43 | Broadcast XMPP messages to apps. 44 | Can broadcast XMPP messages received from the Google servers to apps registered to listen for them. 45 | send and receive XMPP messages to and from Google servers 46 | Allows Google apps to send and receive XMPP messages to and from Google servers. 47 | Broadcast XMPP messages to apps. 48 | Can broadcast XMPP messages received from the Google servers to apps. 49 | Send broadcasts to Android Market. 50 | Can send broadcasts to Android Market requesting app installation and removal. 51 | Send heartbeat to Google Talk server 52 | Can send a heartbeat packet to the Google Talk server to ensure the health of the connection. 53 | Read Google settings 54 | Allows this app to read Google settings. 55 | Modify Google settings 56 | Allows this app to modify Google settings. 57 | read Google service configuration 58 | Allows this app to read Google service configuration data. 59 | Modify Google service configuration 60 | Allows this app to modify Google service configuration data. 61 | view configured accounts 62 | Allows apps to see the usernames (email addresses) of the Google account(s) you have configured. 63 | access all Google services 64 | Allows apps to sign in to ALL Google services using the account(s) stored on this Android device. 65 | access other Google services 66 | Allows apps to sign in to unspecified Google services using the account(s) stored on this Android device. 67 | Google mail 68 | Allows apps to sign in to Google mail services using the account(s) stored on this Android device. 69 | Google Calendar 70 | Allows apps to sign in to Google Calendar using the account(s) stored on this Android device. 71 | Android services 72 | Allows apps to sign in to Android services using the account(s) stored on this Android device. 73 | Google Checkout accounts 74 | Allows apps to sign in to Google Checkout (and potentially make purchases) using the account(s) stored on this Android device. 75 | Google Checkout QA accounts 76 | Allows apps to sign in to Google Checkout QA using the account(s) stored on this Android device. 77 | Google Checkout Sandbox accounts 78 | Allows apps to sign in to Google Checkout Sandbox using the account(s) stored on this Android device. 79 | YouTube 80 | Allows apps to sign in to YouTube using the account(s) stored on this Android device. 81 | YouTube usernames 82 | Allows apps to see the YouTube username(s) associated with the Google account(s) stored on this Android device. 83 | Google Talk 84 | Allows apps to sign in to Google Talk using the account(s) stored on this Android device. 85 | iGoogle accounts 86 | Allows apps to sign in to iGoogle using the account(s) stored on this Android device. 87 | Picasa Web Albums 88 | Allows apps to sign in to Picasa Web Albums using the account(s) stored on this Android device. 89 | Google mobile apps 90 | Allows apps to sign in to Google mobile apps using the account(s) stored on this Android device. 91 | contacts data in Google accounts 92 | Allows apps to access the contacts and profile information of account(s) stored on this Android device. 93 | AdSense 94 | Allows apps to sign in to Google AdSense using the account(s) stored on this Android device. 95 | AdWords 96 | Allows apps to sign in to Google AdWords using the account(s) stored on this Android device. 97 | Blogger 98 | Allows apps to sign in to Blogger using the account(s) stored on this Android device. 99 | Dodgeball 100 | Allows apps to sign in to Dodgeball using the account(s) stored on this Android device. 101 | Google Base 102 | Allows apps to sign in to Google Base using the account(s) stored on this Android device. 103 | Google Groups 104 | Allows apps to sign in to Google Groups using the account(s) stored on this Android device 105 | Google Health 106 | Allows apps to sign in to Google Health using the account(s) stored on this Android device. 107 | JotSpot 108 | Allows apps to sign in to JotSpot using the account(s) stored on this Android device. 109 | Knol 110 | Allows apps to sign in to Knol using the account(s) stored on this Android device. 111 | Google News 112 | Allows apps to sign in to Google News using the account(s) stored on this Android device. 113 | Orkut 114 | Allows apps to sign in to Orkut using the account(s) stored on this Android device. 115 | Google Book Search 116 | Allows apps to sign in to Google Book Search using the account(s) stored on this Android device. 117 | Google Webmaster Tools 118 | Allows apps to sign in to Google Webmaster Tools using the account(s) stored on this Android device. 119 | Google Wi-Fi 120 | Allows apps to sign in to Google Wi-Fi using the account(s) stored on this Android device. 121 | Google Docs 122 | Allows apps to sign in to Google Docs using the account(s) stored on this Android device. 123 | Google Spreadsheets 124 | Allows apps to sign in to Google Spreadsheets using the account(s) stored on this Android device. 125 | Google Finance 126 | Allows apps to sign in to Google Finance using the account(s) stored on this Android device. 127 | Google Maps 128 | Allows apps to sign in to Google Maps using the account(s) stored on this Android device. 129 | Google App Engine 130 | Allows apps to sign in to Google App Engine using the account(s) stored on this Android device. 131 | Google Notebook 132 | Allows apps to sign in to Google Notebook using the account(s) stored on this Android device. 133 | Google Voice 134 | Allows apps to sign in to Google Voice using the account(s) stored on this Android device. 135 | Personalized Speech Recognition 136 | Allows apps to sign in to the Personalized Speech Recognition service using the account(s) stored on this Android device. 137 | Google Voice Search 138 | Allows apps to sign in to Google Voice Search using the account(s) stored on this Android device. 139 | 140 | µg legal 141 | µg location settings 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/checkin/CheckinService.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.checkin; 2 | 3 | import android.accounts.Account; 4 | import android.accounts.AccountManager; 5 | import android.app.*; 6 | import android.content.BroadcastReceiver; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.net.ConnectivityManager; 11 | import android.net.NetworkInfo; 12 | import android.os.Build; 13 | import android.os.IBinder; 14 | import android.util.Log; 15 | import com.google.android.AndroidContext; 16 | import com.google.android.gsf.GservicesContract; 17 | import com.google.android.gsf.PrivacyExtension; 18 | import com.google.checkin.CheckinClient; 19 | import com.google.checkin.CheckinResponse; 20 | import com.google.tools.Client; 21 | 22 | import java.util.concurrent.atomic.AtomicBoolean; 23 | 24 | public class CheckinService extends Service { 25 | 26 | private static final String TAG = "CheckinService"; 27 | private static final String PREFERENCES_NAME = "CheckinService"; 28 | 29 | private static final int DEFAULT_INTERVAL = 43200; 30 | private static final int DEFAULT_MIN_TRIGGER = 30; 31 | 32 | public static final String KEY_LAST_FINGERPRINT = "checkin_last_build_fingerprint"; 33 | public static final String KEY_CHECKIN_ENABLED = "checkin_enabled"; 34 | public static final String KEY_LAST_TIME = "checkin_last_time_millis"; 35 | public static final String KEY_MIN_TRIGGER = "checkin_min_trigger_interval"; 36 | public static final String KEY_INTERVAL = "checkin_interval"; 37 | public static final String KEY_SECURITY_TOKEN = "checkin_security_token"; 38 | 39 | private static AtomicBoolean active = new AtomicBoolean(false); 40 | private static AtomicBoolean force = new AtomicBoolean(false); 41 | private static AtomicBoolean notify = new AtomicBoolean(false); 42 | 43 | public static class Receiver extends BroadcastReceiver { 44 | @Override 45 | public void onReceive(Context context, Intent intent) { 46 | if (active.compareAndSet(false, true)) { 47 | Log.d(TAG, "Triggered by: " + intent); 48 | context.startService(new Intent(context, CheckinService.class)); 49 | } 50 | } 51 | } 52 | 53 | public static class SecretCodeReceiver extends BroadcastReceiver { 54 | @Override 55 | public void onReceive(Context context, Intent intent) { 56 | if (active.getAndSet(true) & force.getAndSet(true) & notify.getAndSet(true)) { 57 | // Nothing to do 58 | } else { 59 | // send checkin request 60 | Log.d(TAG, "Triggered by: " + intent); 61 | context.startService(new Intent(context, CheckinService.class)); 62 | } 63 | } 64 | } 65 | 66 | public static class TriggerReceiver extends BroadcastReceiver { 67 | @Override 68 | public void onReceive(Context context, Intent intent) { 69 | boolean doForce = intent.getBooleanExtra("force", false); 70 | if (doForce) { 71 | getPreferences(context).edit().putBoolean(KEY_CHECKIN_ENABLED, true).apply(); 72 | } 73 | if (active.getAndSet(true) & (!doForce || force.getAndSet(true))) { 74 | // Nothing to do 75 | } else { 76 | // send checkin request 77 | Log.d(TAG, "Triggered by: " + intent); 78 | context.startService(new Intent(context, CheckinService.class)); 79 | } 80 | } 81 | } 82 | 83 | @Override 84 | public int onStartCommand(Intent intent, int flags, int startId) { 85 | Log.d(TAG, "Incoming request on CheckinService"); 86 | synchronized (CheckinService.class) { 87 | if (active.get()) { 88 | if (!executeCheckin()) { 89 | active.set(false); 90 | } 91 | } 92 | } 93 | return START_NOT_STICKY; 94 | } 95 | 96 | private long calculateWaitMillis() { 97 | if (!getPreferences().getBoolean(KEY_CHECKIN_ENABLED, false)) { 98 | Log.d(TAG, "Ignoring request on CheckinService, checkin is disabled!"); 99 | return Long.MAX_VALUE; 100 | } 101 | ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); 102 | if (cm == null) { 103 | Log.d(TAG, "ConnectivityManager not accessible, stop checkin!"); 104 | return Long.MAX_VALUE; 105 | } 106 | NetworkInfo info = cm.getActiveNetworkInfo(); 107 | if (info == null || !info.isConnected()) { 108 | Log.d(TAG, "No active network connection, stop checkin!"); 109 | return Long.MAX_VALUE; 110 | } 111 | long lastCheckin = getPreferences().getLong(KEY_LAST_TIME, 0); 112 | long interval = 1000 * getPreferences().getLong(KEY_INTERVAL, DEFAULT_INTERVAL); 113 | long minTrigger = 1000 * getPreferences().getLong(KEY_MIN_TRIGGER, DEFAULT_MIN_TRIGGER); 114 | long time = System.currentTimeMillis(); 115 | long regularWait = lastCheckin + interval - time; 116 | long forceWait = lastCheckin + minTrigger - time; 117 | if (force.getAndSet(forceWait > 0)) { 118 | return forceWait; 119 | } 120 | if (!Build.FINGERPRINT.equals(getPreferences().getString(KEY_LAST_FINGERPRINT, null))) { 121 | Log.d(TAG, "Fingerprint changed, forcing checkin"); 122 | return forceWait; 123 | } 124 | return regularWait; 125 | } 126 | 127 | private boolean executeCheckin() { 128 | long waitTime = calculateWaitMillis(); 129 | if (waitTime == Long.MAX_VALUE) { 130 | return false; 131 | } else if (waitTime > 0) { 132 | PendingIntent pi = PendingIntent.getBroadcast(this, 0, new Intent(this, Receiver.class), 0); 133 | AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); 134 | am.set(AlarmManager.RTC, System.currentTimeMillis() + waitTime, pi); 135 | return false; 136 | } else { 137 | Log.d(TAG, "Starting checkin in new thread..."); 138 | new Thread(new Runnable() { 139 | @Override 140 | public void run() { 141 | doCheckin(); 142 | getPreferences().edit() 143 | .putString(KEY_LAST_FINGERPRINT, Build.FINGERPRINT) 144 | .putLong(KEY_LAST_TIME, System.currentTimeMillis()).apply(); 145 | active.set(false); 146 | } 147 | }).start(); 148 | return true; 149 | } 150 | } 151 | 152 | private void doCheckin() { 153 | Log.d(TAG, "Starting checkin"); 154 | long securityToken = getPreferences().getLong(KEY_SECURITY_TOKEN, 0); 155 | AndroidContext info = PrivacyExtension.getAndroidContext(this); 156 | for (String s : info.toString().split("\r\n")) { 157 | Log.d(TAG, s); 158 | } 159 | if (securityToken != 0) { 160 | info.set(AndroidContext.KEY_CHECKIN_SECURITY_TOKEN, securityToken); 161 | } else { 162 | info.unset(AndroidContext.KEY_ANDROID_ID_HEX); 163 | info.unset(AndroidContext.KEY_ANDROID_ID_LONG); 164 | } 165 | AccountManager am = AccountManager.get(this); 166 | Account[] accounts = am.getAccountsByType("com.google"); 167 | String token = null; 168 | if (accounts.length > 0) { 169 | Account a = accounts[0]; 170 | try { 171 | token = am.blockingGetAuthToken(a, "ac2dm", true); 172 | } catch (Exception e) { 173 | } 174 | if (token == null || token.isEmpty()) { 175 | try { 176 | token = am.blockingGetAuthToken(a, "SID", true); 177 | } catch (Exception e) { 178 | } 179 | } 180 | info.set(AndroidContext.KEY_EMAIL, a.name); 181 | info.set(AndroidContext.KEY_AUTHORIZATION_TOKEN, token); 182 | } 183 | Client.DEBUG = true; 184 | Client.DEBUG_HEADER = true; 185 | CheckinResponse response = new CheckinClient().checkin(info); 186 | Log.d(TAG, "Checkin success: id:" + response.getAndroidIdHex() + ", token:" + response.getSecurityToken() + 187 | ", digest:" + response.getDigest() + ", market:" + response.isMarketEnabled()); 188 | for (String key : response.getSettings().keySet()) { 189 | GservicesContract.setString(getContentResolver(), key, response.getSettings().get(key)); 190 | } 191 | getPreferences().edit().putLong(KEY_SECURITY_TOKEN, response.getSecurityToken()).apply(); 192 | if (notify.getAndSet(false)) { 193 | Notification.Builder n = new Notification.Builder(this); 194 | n.setAutoCancel(true); 195 | n.setSmallIcon(android.R.drawable.stat_notify_sync); 196 | n.setContentTitle("Checkin success"); 197 | n.setContentText("Android ID: " + response.getAndroidIdHex()); 198 | ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(1, fromBuilder(n)); 199 | } 200 | } 201 | 202 | private SharedPreferences getPreferences() { 203 | return getPreferences(this); 204 | } 205 | 206 | public static SharedPreferences getPreferences(Context context) { 207 | return context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE); 208 | } 209 | 210 | private static Notification fromBuilder(Notification.Builder n) { 211 | if (Build.VERSION.SDK_INT < 16) { 212 | return n.getNotification(); 213 | } else { 214 | return n.build(); 215 | } 216 | } 217 | 218 | 219 | @Override 220 | public IBinder onBind(Intent intent) { 221 | Log.d(TAG, "CheckinService.onBind: huh, we shouldn't get called!"); 222 | return null; 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/checkin/EventLogService.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.checkin; 2 | 3 | import android.app.Service; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.IBinder; 8 | import android.util.Log; 9 | 10 | public class EventLogService extends Service { 11 | 12 | private static final String TAG = "EventLogService"; 13 | 14 | public static class Receiver extends BroadcastReceiver { 15 | 16 | @Override 17 | public void onReceive(Context context, Intent intent) { 18 | // I never log! 19 | 20 | } 21 | 22 | } 23 | 24 | @Override 25 | public IBinder onBind(Intent intent) { 26 | // TODO Auto-generated method stub 27 | Log.w(TAG, "Not yet implemented: EventLogService.onBind"); 28 | return null; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gservices/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gservices; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | public class DatabaseHelper extends SQLiteOpenHelper { 8 | private static final int DB_VERSION = 3; 9 | private static final int DB_VERSION_OLD = 1; 10 | 11 | public DatabaseHelper(final Context context) { 12 | super(context, "gservices.db", null, DB_VERSION); 13 | } 14 | 15 | @Override 16 | public void onCreate(final SQLiteDatabase db) { 17 | db.execSQL("CREATE TABLE main (name TEXT PRIMARY KEY, value TEXT)"); 18 | db.execSQL("CREATE TABLE overrides (name TEXT PRIMARY KEY, value TEXT)"); 19 | db.execSQL("CREATE TABLE saved_system (name TEXT PRIMARY KEY, value TEXT)"); 20 | db.execSQL("CREATE TABLE saved_secure (name TEXT PRIMARY KEY, value TEXT)"); 21 | } 22 | 23 | @Override 24 | public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { 25 | if (oldVersion == DB_VERSION_OLD) { 26 | db.execSQL("DROP TABLE IF EXISTS main"); 27 | db.execSQL("DROP TABLE IF EXISTS overrides"); 28 | onCreate(db); 29 | } 30 | db.setVersion(newVersion); 31 | } 32 | } -------------------------------------------------------------------------------- /src/com/google/android/gsf/gservices/GservicesProvider.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gservices; 2 | 3 | import android.content.*; 4 | import android.content.pm.PackageManager; 5 | import android.content.pm.Signature; 6 | import android.database.Cursor; 7 | import android.database.MatrixCursor; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.net.Uri; 10 | import android.os.Binder; 11 | import android.os.Build; 12 | import android.os.Bundle; 13 | import android.os.Process; 14 | import android.util.Log; 15 | 16 | import java.io.ByteArrayInputStream; 17 | import java.math.BigInteger; 18 | import java.security.MessageDigest; 19 | import java.security.cert.CertificateFactory; 20 | import java.security.cert.X509Certificate; 21 | import java.util.ArrayList; 22 | import java.util.Map.Entry; 23 | import java.util.SortedMap; 24 | import java.util.TreeMap; 25 | 26 | public class GservicesProvider extends ContentProvider { 27 | 28 | private static final BigInteger GOOGLE_SERIAL = new BigInteger("1228183678"); 29 | private static final char[] HEX_CHARS = 30 | new char[]{'0', '1', '2', '3', '4', '3', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 31 | private static final int KEY_COLUMN = 0; 32 | private static final String TAG = "GoogleServicesProvider"; 33 | public static final Uri UPDATE_MAIN_DIFF_URI = Uri.parse("content://com.google.android.gsf.gservices/main_diff"); 34 | public static final Uri UPDATE_MAIN_URI = Uri.parse("content://com.google.android.gsf.gservices/main"); 35 | public static final Uri UPDATE_OVERRIDE_URI = Uri.parse("content://com.google.android.gsf.gservices/override"); 36 | private static final int VALUE_COLUMN = 1; 37 | 38 | private DatabaseHelper dbHelper; 39 | private boolean pushToSecure = false; 40 | private boolean pushToSystem = false; 41 | private TreeMap values; 42 | 43 | private final Object valuesLock = new Object(); 44 | private MessageDigest md; 45 | 46 | private boolean checkCallingPermission(final String permission) { 47 | return getContext().checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED; 48 | } 49 | 50 | private boolean checkPermissionOrSignature(final String permission, final BigInteger... serials) { 51 | if (checkCallingPermission(permission)) { 52 | return true; 53 | } 54 | for (BigInteger serial : serials) { 55 | if (checkSignature(serial)) { 56 | return true; 57 | } 58 | } 59 | return false; 60 | } 61 | 62 | private boolean checkReadPermission() { 63 | return checkPermissionOrSignature("com.google.android.providers.gsf.permission.READ_GSERVICES", 64 | getSignatureSerials("com.google.android.gsf", GOOGLE_SERIAL)); 65 | } 66 | 67 | private BigInteger[] getSignatureSerials(String packageName, BigInteger additionalSerial) { 68 | ArrayList serials = new ArrayList(); 69 | try { 70 | final PackageManager pm = getContext().getPackageManager(); 71 | final Signature[] sigs = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures; 72 | final CertificateFactory factory = CertificateFactory.getInstance("X509"); 73 | for (final Signature sig : sigs) { 74 | try { 75 | final X509Certificate cert = 76 | (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(sig.toByteArray())); 77 | if (cert.getSerialNumber() != null) { 78 | serials.add(cert.getSerialNumber()); 79 | } 80 | } catch (Exception e) { 81 | // Try next 82 | } 83 | } 84 | } catch (Exception e) { 85 | // Ignore 86 | } 87 | if (additionalSerial == null) { 88 | return serials.toArray(new BigInteger[serials.size()]); 89 | } else { 90 | BigInteger[] result = serials.toArray(new BigInteger[serials.size() + 1]); 91 | result[serials.size()] = additionalSerial; 92 | return result; 93 | } 94 | } 95 | 96 | private boolean checkSignature(final BigInteger serial) { 97 | try { 98 | final PackageManager pm = getContext().getPackageManager(); 99 | final String packageName = pm.getNameForUid(Binder.getCallingUid()); 100 | final BigInteger[] serials = getSignatureSerials(packageName, null); 101 | for (BigInteger s : serials) { 102 | if (s.equals(serial)) { 103 | return true; 104 | } 105 | } 106 | } catch (final Throwable t) { 107 | } 108 | return false; 109 | } 110 | 111 | private boolean checkWritePermission() { 112 | return checkPermissionOrSignature("com.google.android.providers.gsf.permission.WRITE_GSERVICES", 113 | getSignatureSerials("com.google.android.gsf", GOOGLE_SERIAL)); 114 | } 115 | 116 | private boolean computeLocalDigestAndUpdateValues() { 117 | final SQLiteDatabase db = dbHelper.getWritableDatabase(); 118 | final TreeMap map = new TreeMap(); 119 | md.reset(); 120 | String oldDigest = null; 121 | db.beginTransaction(); 122 | Cursor cursor = db.rawQuery("SELECT name, value FROM main ORDER BY name", null); 123 | try { 124 | while (cursor.moveToNext()) { 125 | final String key = cursor.getString(KEY_COLUMN); 126 | final String value = cursor.getString(VALUE_COLUMN); 127 | if (!key.equals("digest")) { 128 | md.update(key.getBytes()); 129 | md.update((byte) 0); 130 | md.update(value.getBytes()); 131 | md.update((byte) 0); 132 | } else { 133 | oldDigest = value; 134 | } 135 | map.put(key, value); 136 | } 137 | } finally { 138 | cursor.close(); 139 | } 140 | final StringBuilder sb = new StringBuilder("1-"); 141 | final byte[] hash = md.digest(); 142 | for (final byte element : hash) { 143 | sb.append(HEX_CHARS[0xf & element >> 4]); 144 | sb.append(HEX_CHARS[element & 0xf]); 145 | } 146 | final String digest = sb.toString(); 147 | map.put("digest", digest); 148 | 149 | if (!digest.equals(oldDigest)) { 150 | ContentValues contentValues = new ContentValues(); 151 | contentValues.put("name", "digest"); 152 | contentValues.put("value", digest); 153 | db.insertWithOnConflict("main", null, contentValues, SQLiteDatabase.CONFLICT_REPLACE); 154 | } 155 | cursor = db.rawQuery("SELECT name, value FROM overrides", null); 156 | try { 157 | while (cursor.moveToNext()) { 158 | map.put(cursor.getString(KEY_COLUMN), cursor.getString(VALUE_COLUMN)); 159 | } 160 | } finally { 161 | cursor.close(); 162 | } 163 | synchronized (valuesLock) { 164 | values = map; 165 | } 166 | db.setTransactionSuccessful(); 167 | db.endTransaction(); 168 | return !digest.equals(oldDigest); 169 | } 170 | 171 | @Override 172 | public int delete(final Uri arg0, final String arg1, final String[] arg2) { 173 | throw new UnsupportedOperationException(); 174 | } 175 | 176 | private String getPrefixLimit(final String string) { 177 | for (int i = string.length() - 1; i > 0; i--) { 178 | final char c = string.charAt(i); 179 | if (c < '\uFFFF') { 180 | return string.substring(0, i) + (char) (c + 1); 181 | } 182 | } 183 | return null; 184 | } 185 | 186 | @Override 187 | public String getType(final Uri uri) { 188 | return null; 189 | } 190 | 191 | @Override 192 | public Uri insert(final Uri uri, final ContentValues values) { 193 | throw new UnsupportedOperationException(); 194 | } 195 | 196 | @Override 197 | public boolean onCreate() { 198 | try { 199 | md = MessageDigest.getInstance("SHA-1"); 200 | } catch (Exception e) { 201 | Log.w(TAG, "Can't hash digest, this will cause problems!", e); 202 | } 203 | dbHelper = new DatabaseHelper(getContext()); 204 | final int pid = Process.myPid(); 205 | final int uid = Process.myUid(); 206 | if (getContext().checkPermission("android.permission.WRITE_SETTINGS", pid, uid) == PackageManager.PERMISSION_GRANTED) { 207 | pushToSystem = true; 208 | } 209 | if (getContext().checkPermission("android.permission.WRITE_SECURE_SETTINGS", pid, uid) == PackageManager.PERMISSION_GRANTED) { 210 | pushToSecure = true; 211 | } 212 | computeLocalDigestAndUpdateValues(); 213 | return true; 214 | } 215 | 216 | @Override 217 | public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, 218 | final String sortOrder) { 219 | if (!checkReadPermission()) { 220 | Log.d(TAG, "no permission to read during query(" + uri + ", " + projection + ", " + selection + ", " + 221 | selectionArgs + ", " + sortOrder + ")"); 222 | throw new UnsupportedOperationException(); 223 | } 224 | final MatrixCursor cursor = new MatrixCursor(new String[]{"key", "value"}); 225 | if (selectionArgs != null) { 226 | final String lastSegment = uri.getLastPathSegment(); 227 | if (lastSegment == null) { 228 | querySimple(cursor, selectionArgs); 229 | } else if (lastSegment.equals("prefix")) { 230 | queryPrefix(cursor, selectionArgs); 231 | } 232 | } 233 | return cursor; 234 | } 235 | 236 | private void queryPrefix(final MatrixCursor cursor, final String... selectionArgs) { 237 | for (final String arg : selectionArgs) { 238 | final String limit = getPrefixLimit(arg); 239 | SortedMap sortedmap; 240 | if (limit == null) { 241 | sortedmap = values.tailMap(arg); 242 | } else { 243 | sortedmap = values.subMap(arg, limit); 244 | } 245 | for (final Entry entry : sortedmap.entrySet()) { 246 | cursor.addRow(new String[]{entry.getKey(), entry.getValue()}); 247 | } 248 | } 249 | } 250 | 251 | private void querySimple(final MatrixCursor cursor, final String[] keys) { 252 | synchronized (valuesLock) { 253 | for (final String key : keys) { 254 | cursor.addRow(new String[]{key, values.get(key)}); 255 | } 256 | } 257 | } 258 | 259 | private void syncAllSettings() { 260 | if (pushToSystem) { 261 | syncSettings(android.provider.Settings.System.CONTENT_URI, "system:", "saved_system"); 262 | } 263 | if (pushToSecure) { 264 | syncSettings(android.provider.Settings.Secure.CONTENT_URI, "secure:", "saved_secure"); 265 | } 266 | } 267 | 268 | private void syncSettings(final Uri uri, final String prefix, final String table) { 269 | final MatrixCursor cursor = new MatrixCursor(new String[]{"key", "value"}); 270 | queryPrefix(cursor, prefix); 271 | // TODO Auto-generated method stub 272 | Log.w(TAG, "Not yet implemented: GservicesProvider.syncSettings"); 273 | } 274 | 275 | @Override 276 | public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { 277 | if (!checkWritePermission()) { 278 | Log.d(TAG, "no permission to write during update(" + uri + ", " + values + ", " + selection + ", " + 279 | selectionArgs + ")"); 280 | throw new UnsupportedOperationException(); 281 | } 282 | final String lastSegment = uri.getLastPathSegment(); 283 | if (lastSegment.equals("main") && updateMain(values) || 284 | lastSegment.equals("main_diff") && updateMainDiff(values) || 285 | lastSegment.equals("override") && updateOverride(values)) { 286 | getContext().sendBroadcast(new Intent("com.google.gservices.intent.action.GSERVICES_CHANGED")); 287 | return 1; 288 | } 289 | return 0; 290 | } 291 | 292 | private boolean updateMain(final ContentValues values) { 293 | SQLiteDatabase db = dbHelper.getWritableDatabase(); 294 | db.insertWithOnConflict("main", null, values, SQLiteDatabase.CONFLICT_REPLACE); 295 | db.close(); 296 | if (computeLocalDigestAndUpdateValues()) { 297 | Log.d(TAG, "changed " + values.get("name") + " to " + values.get("value") + " and digest is now " + this.values.get("digest")); 298 | return true; 299 | } 300 | return false; 301 | } 302 | 303 | private boolean updateMainDiff(final ContentValues values) { 304 | Log.w(TAG, "Not yet implemented: GservicesProvider.updateMainDiff: " + values); 305 | return false; 306 | } 307 | 308 | private boolean updateOverride(final ContentValues values) { 309 | Log.w(TAG, "Not yet implemented: GservicesProvider.updateOverride: " + values); 310 | return false; 311 | } 312 | 313 | public static class OverrideReceiver extends BroadcastReceiver { 314 | 315 | @Override 316 | public void onReceive(final Context context, final Intent intent) { 317 | final Bundle bundle = intent.getExtras(); 318 | if (bundle != null) { 319 | final ContentValues values = new ContentValues(); 320 | for (final String key : bundle.keySet()) { 321 | values.put(key, bundle.getString(key)); 322 | } 323 | context.getContentResolver().update(UPDATE_OVERRIDE_URI, values, null, null); 324 | } 325 | } 326 | 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/PushMessagingRegistrar.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice; 2 | 3 | import android.accounts.Account; 4 | import android.accounts.AccountManager; 5 | import android.annotation.SuppressLint; 6 | import android.app.IntentService; 7 | import android.app.PendingIntent; 8 | import android.content.Intent; 9 | import android.content.pm.PackageInfo; 10 | import android.content.pm.PackageManager; 11 | import android.hardware.location.GeofenceHardware; 12 | import android.os.Build; 13 | import android.os.Bundle; 14 | import android.util.Log; 15 | import com.google.android.AndroidContext; 16 | import com.google.c2dm.C2DMClient; 17 | import com.google.android.gsf.PrivacyExtension; 18 | import com.google.tools.SignatureTools; 19 | 20 | import java.io.IOException; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | public class PushMessagingRegistrar extends IntentService { 25 | 26 | private static final String TAG = "PushMessagingRegistrar"; 27 | 28 | public PushMessagingRegistrar() { 29 | super(TAG); 30 | } 31 | 32 | @Override 33 | protected void onHandleIntent(Intent intent) { 34 | Log.d(TAG, "onHandleIntent: " + intent); 35 | try { 36 | if (intent != null && intent.getAction() != null) { 37 | if (intent.getAction().equalsIgnoreCase("com.google.android.c2dm.intent.REGISTER")) { 38 | register(intent); 39 | } else if (intent.getAction().equalsIgnoreCase("com.google.android.c2dm.intent.UNREGISTER")) { 40 | unregister(intent); 41 | } 42 | } 43 | } catch (Exception e) { 44 | Log.w(TAG, e); 45 | } 46 | } 47 | 48 | private void unregister(Intent intent) { 49 | // TODO Auto-generated method stub 50 | Log.w(TAG, "Not yet implemented: PushMessagingRegistrar.unregister: " + intent); 51 | 52 | } 53 | 54 | private static Map mapFromBundle(Bundle bundle) { 55 | HashMap result = new HashMap(); 56 | for (String key : bundle.keySet()) { 57 | Log.d(TAG, "extra:: " + key + "=" + bundle.get(key)); 58 | if (!key.equalsIgnoreCase("sender") && !key.equalsIgnoreCase("app")) { 59 | result.put(key, bundle.get(key).toString()); 60 | } 61 | } 62 | return result; 63 | } 64 | 65 | @SuppressLint("NewApi") 66 | @SuppressWarnings("deprecation") 67 | private String packageFromPendingIntent(PendingIntent pi) { 68 | if (Build.VERSION.SDK_INT < 17) { 69 | return pi.getTargetPackage(); 70 | } else { 71 | return pi.getCreatorPackage(); 72 | } 73 | } 74 | 75 | private void register(Intent intent) throws IOException { 76 | Log.d(TAG, "register: " + intent); 77 | Intent outIntent = new Intent("com.google.android.c2dm.intent.REGISTRATION"); 78 | PendingIntent pendingIntent = intent.getParcelableExtra("app"); 79 | String sender = intent.getStringExtra("sender"); 80 | String app = packageFromPendingIntent(pendingIntent); 81 | AccountManager am = AccountManager.get(this); 82 | Account[] accounts = am.getAccountsByType("com.google"); 83 | String token = null; 84 | if (accounts.length>=1) { 85 | try { 86 | token = am.blockingGetAuthToken(accounts[0], "ac2dm", true); 87 | } catch (Exception e) { 88 | } 89 | } 90 | if (token != null) { 91 | AndroidContext info = PrivacyExtension.getAndroidContext(this); 92 | info.set(AndroidContext.KEY_AUTHORIZATION_TOKEN, token); 93 | Log.d(TAG, app + ", " + getSignatureFingerprint(app) + ", " + sender); 94 | String regId = 95 | new C2DMClient(info).registerC2DM(app, getSignatureFingerprint(app), sender, mapFromBundle(intent.getExtras())); 96 | outIntent.setPackage(app); 97 | if (regId != null) { 98 | Log.d(TAG, "Success: regId for " + app + " is " + regId); 99 | outIntent.putExtra("registration_id", regId); 100 | } else { 101 | Log.d(TAG, "Error: no regId for " + app + "!"); 102 | outIntent.putExtra("error", "SERVICE_NOT_AVAILABLE"); 103 | } 104 | } else { 105 | outIntent.putExtra("error", "CANT_LOGIN"); 106 | } 107 | sendOrderedBroadcast(outIntent, null); 108 | } 109 | 110 | private String getSignatureFingerprint(String s) { 111 | PackageManager pm = getPackageManager(); 112 | try { 113 | if (pm.getApplicationInfo(s, 0) == null) { 114 | return null; 115 | } 116 | PackageInfo packageinfo = pm.getPackageInfo(s, PackageManager.GET_SIGNATURES); 117 | if (packageinfo == null || packageinfo.signatures == null || packageinfo.signatures.length == 0 || 118 | packageinfo.signatures[0] == null) { 119 | return null; 120 | } 121 | return SignatureTools.signatureDigest(packageinfo.signatures[0].toByteArray()); 122 | } catch (Exception e) { 123 | return null; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/PushMessagingRegistrarProxy.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice; 2 | 3 | import android.app.IntentService; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | public class PushMessagingRegistrarProxy extends IntentService { 8 | 9 | private static final String TAG = "PushMessagingRegistrarProxy"; 10 | 11 | public PushMessagingRegistrarProxy() { 12 | super(TAG); 13 | } 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | } 19 | 20 | @Override 21 | protected void onHandleIntent(Intent intent) { 22 | Log.d(TAG, "onHandleIntent: " + intent); 23 | intent.setClass(this, PushMessagingRegistrar.class); 24 | startService(intent); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/Account.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice.service; 2 | 3 | import android.accounts.AccountManager; 4 | import android.accounts.AccountManagerFuture; 5 | import android.content.ContentUris; 6 | import android.content.ContentValues; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.database.Cursor; 10 | import android.os.Bundle; 11 | import android.util.Log; 12 | import com.google.android.gsf.talk.TalkProvider; 13 | 14 | public class Account { 15 | 16 | private final static String TAG = "GoogleTalkAccount"; 17 | 18 | public static Account getAccount(final String accountIdent, final Context context) { 19 | if (!accountIdent.contains(":")) { 20 | return null; 21 | } 22 | final String[] split = accountIdent.split(":"); 23 | final String accountType = split[0]; 24 | final String accountName = split[1]; 25 | if (accountType.equals("com.google")) { 26 | return getGoogleAccount(accountName, context); 27 | } else { 28 | return null; 29 | } 30 | } 31 | 32 | public static long getAccountIdForUser(final String username, final Context context) { 33 | final Cursor c = context.getContentResolver() 34 | .query(TalkProvider.ACCOUNTS_URI, new String[]{"_id"}, "username=?", 35 | new String[]{username}, null); 36 | if (c.moveToFirst()) { 37 | final long id = c.getLong(0); 38 | c.close(); 39 | return id; 40 | } 41 | c.close(); 42 | final ContentValues values = new ContentValues(); 43 | values.put("name", username); 44 | values.put("username", username); 45 | return ContentUris.parseId(context.getContentResolver().insert(TalkProvider.ACCOUNTS_URI, values)); 46 | } 47 | 48 | public static Account getGoogleAccount(final String username, final Context context) { 49 | Log.d(TAG, username + " seems to be a google account. search it in local database..."); 50 | final AccountManager am = AccountManager.get(context); 51 | final android.accounts.Account[] as = am.getAccountsByType("com.google"); 52 | String auth = null; 53 | final long id = getAccountIdForUser(username, context); 54 | for (final android.accounts.Account a : as) { 55 | if (a.name.equalsIgnoreCase(username)) { 56 | int waitForIntent = 0; 57 | do { 58 | if (waitForIntent > 0) { 59 | try { 60 | synchronized (context) { 61 | context.wait(1000); 62 | } 63 | } catch (final InterruptedException e) { 64 | Log.w(TAG, e); 65 | } 66 | } 67 | waitForIntent--; 68 | Log.d(TAG, "found " + username + " in AccountManager as " + a + ". Trying to get auth token"); 69 | final AccountManagerFuture af = am.getAuthToken(a, "mail", null, null, null, null); 70 | Bundle b = null; 71 | try { 72 | b = af.getResult(); 73 | } catch (final Exception e) { 74 | Log.w(TAG, e); 75 | } 76 | if (b != null) { 77 | auth = b.getString(AccountManager.KEY_AUTHTOKEN); 78 | Log.d(TAG, "authToken is " + auth); 79 | if (auth != null) { 80 | break; 81 | } else { 82 | final Intent intent = b.getParcelable(AccountManager.KEY_INTENT); 83 | if (intent != null && waitForIntent == -1) { 84 | Log.d(TAG, "Starting intent: " + intent); 85 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 86 | context.startActivity(intent); 87 | waitForIntent = 10; 88 | } 89 | } 90 | } 91 | } while (waitForIntent > 0); 92 | } 93 | } 94 | return new Account("com.google", "talk.google.com", username.split("@")[1], username, auth, id, username); 95 | } 96 | 97 | private final String auth; 98 | 99 | private final long id; 100 | private final String jid; 101 | private final String serviceHost; 102 | private final String serviceName; 103 | private final String type; 104 | private final String username; 105 | 106 | public Account(final String type, final String serviceHost, final String serviceName, final String jid, 107 | final String auth, final long id, final String username) { 108 | this.type = type; 109 | this.jid = jid; 110 | this.auth = auth; 111 | this.serviceName = serviceName; 112 | this.serviceHost = serviceHost; 113 | this.id = id; 114 | this.username = username; 115 | } 116 | 117 | public String getAuth() { 118 | return auth; 119 | } 120 | 121 | public long getId() { 122 | return id; 123 | } 124 | 125 | public String getJid() { 126 | return jid; 127 | } 128 | 129 | public String getServiceHost() { 130 | return serviceHost; 131 | } 132 | 133 | public String getServiceName() { 134 | return serviceName; 135 | } 136 | 137 | public String getType() { 138 | return type; 139 | } 140 | 141 | public String getUsername() { 142 | return username; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/BackendHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 μg Project Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.android.gsf.gtalkservice.service; 18 | 19 | import com.google.android.gtalkservice.Presence; 20 | 21 | public interface BackendHandler { 22 | void presence(Presence presence); 23 | 24 | Xmpp getXmpp(); 25 | 26 | void requestRoster(); 27 | 28 | void login(); 29 | 30 | void logout(); 31 | 32 | public interface Xmpp { 33 | String getUser(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/GTalkConnection.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice.service; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.os.Looper; 7 | import android.os.RemoteException; 8 | import android.util.Log; 9 | import com.google.android.gsf.talk.TalkProvider; 10 | import com.google.android.gtalkservice.*; 11 | import com.google.android.gtalkservice.Presence.Show; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class GTalkConnection extends IGTalkConnection.Stub { 17 | 18 | private final static String RESOURCE = "androidv2"; 19 | private static final String TAG = "GoogleTalkConnection"; 20 | private final static int XMPP_PORT = 5222; 21 | 22 | private final Account account; 23 | private final BackendHandler backendHandler; 24 | private final Map chatSessions = new HashMap(); 25 | private ConnectionError connectionError = new ConnectionError(ConnectionError.NO_ERROR); 26 | private ConnectionState connectionState = new ConnectionState(ConnectionState.IDLE); 27 | private final ImSession imSession; 28 | private final Looper looper; 29 | private final Notifier notifier; 30 | private Presence presence = new Presence(); 31 | 32 | private final GTalkService service; 33 | 34 | public GTalkConnection(final GTalkService service, final Account account, final Looper looper, 35 | final boolean autoLogin) { 36 | Log.d(TAG, "GTalkConnection.ctor(" + service + ", " + account + ", " + looper + ")"); 37 | this.service = service; 38 | this.account = account; 39 | this.looper = looper; 40 | notifier = new Notifier(this); 41 | imSession = new ImSession(this); 42 | readAccount(); 43 | backendHandler = null; 44 | /*backendHandler = new SmackHandler(this.looper, this); 45 | if (autoLogin) { 46 | backendHandler.connectAndlogin(); 47 | } else { 48 | backendHandler.connect(); 49 | }*/ 50 | } 51 | 52 | public void addChatSession(final String user, final IChatSession chatSession) { 53 | String useri = user; 54 | if (useri.contains("/")) { 55 | useri = useri.split("/")[0]; 56 | } 57 | synchronized (chatSessions) { 58 | chatSessions.put(useri, chatSession); 59 | } 60 | } 61 | 62 | @Override 63 | public void clearConnectionStatistics() throws RemoteException { 64 | // TODO Auto-generated method stub 65 | Log.w(TAG, "Not yet implemented: GTalkConnection.clearConnectionStatistics"); 66 | 67 | } 68 | 69 | public void closeAllChats() { 70 | synchronized (chatSessions) { 71 | for (final IChatSession chatSession : chatSessions.values()) { 72 | try { 73 | chatSession.leave(); 74 | } catch (final RemoteException e) { 75 | // never happens as chatsessions are not remote... 76 | } 77 | } 78 | } 79 | } 80 | 81 | @Override 82 | public IImSession createImSession() throws RemoteException { 83 | // TODO Auto-generated method stub 84 | Log.w(TAG, "Not yet implemented: GTalkConnection.createImSession"); 85 | return null; 86 | } 87 | 88 | public Account getAccount() { 89 | return account; 90 | } 91 | 92 | public long getAccountId() { 93 | return account.getId(); 94 | } 95 | 96 | public String getAuth() { 97 | return account.getAuth(); 98 | } 99 | 100 | public BackendHandler getBackend() { 101 | return backendHandler; 102 | } 103 | 104 | public IChatSession getChatSession(final String user, final boolean create) { 105 | String useri = user; 106 | if (useri.contains("/")) { 107 | useri = useri.split("/")[0]; 108 | } 109 | synchronized (chatSessions) { 110 | if (chatSessions.containsKey(useri)) { 111 | Log.d(TAG, "GTalkConnection.getChatSession(" + user + ", " + create + ")=" + chatSessions.get(useri)); 112 | return chatSessions.get(useri); 113 | } else if (create) { 114 | //final IChatSession session = new BackendChatSession(this, user); 115 | final IChatSession session = null; 116 | chatSessions.put(useri, session); 117 | Log.d(TAG, "GTalkConnection.getChatSession(" + user + ", " + create + ")=" + session); 118 | return session; 119 | } else { 120 | Log.d(TAG, "GTalkConnection.getChatSession(" + user + ", " + create + ")=null"); 121 | return null; 122 | } 123 | } 124 | } 125 | 126 | public ConnectionError getConnectionError() { 127 | return connectionError; 128 | } 129 | 130 | public ConnectionState getConnectionState() { 131 | return connectionState; 132 | } 133 | 134 | @Override 135 | public int getConnectionUptime() throws RemoteException { 136 | // TODO Auto-generated method stub 137 | Log.w(TAG, "Not yet implemented: GTalkConnection.getConnectionUptime"); 138 | return 0; 139 | } 140 | 141 | public Context getContext() { 142 | return service; 143 | } 144 | 145 | @Override 146 | public IImSession getDefaultImSession() throws RemoteException { 147 | return imSession; 148 | } 149 | 150 | @Override 151 | public String getDeviceId() throws RemoteException { 152 | // TODO Auto-generated method stub 153 | Log.w(TAG, "Not yet implemented: GTalkConnection.getDeviceId"); 154 | return ""; 155 | } 156 | 157 | public ImSession getImSession() { 158 | return imSession; 159 | } 160 | 161 | @Override 162 | public IImSession getImSessionForAccountId(final long l) throws RemoteException { 163 | if (l == account.getId()) { 164 | Log.d(TAG, "IGTalkConnection.getImSessionForAccountId(" + l + ")=" + imSession); 165 | return imSession; 166 | } else { 167 | Log.d(TAG, "IGTalkConnection.getImSessionForAccountId(" + l + ")=null"); 168 | return null; 169 | } 170 | } 171 | 172 | @Override 173 | public String getJid() throws RemoteException { 174 | return account.getJid(); 175 | } 176 | 177 | @Override 178 | public long getLastActivityFromServerTime() throws RemoteException { 179 | // TODO Auto-generated method stub 180 | Log.w(TAG, "Not yet implemented: GTalkConnection.getLastActivityFromServerTime"); 181 | return 0; 182 | } 183 | 184 | @Override 185 | public long getLastActivityToServerTime() throws RemoteException { 186 | // TODO Auto-generated method stub 187 | Log.w(TAG, "Not yet implemented: GTalkConnection.getLastActivityToServerTime"); 188 | return 0; 189 | } 190 | 191 | public Notifier getNotifier() { 192 | return notifier; 193 | } 194 | 195 | @Override 196 | public int getNumberOfConnectionsAttempted() throws RemoteException { 197 | // TODO Auto-generated method stub 198 | Log.w(TAG, "Not yet implemented: GTalkConnection.getNumberOfConnectionsAttempted"); 199 | return 0; 200 | } 201 | 202 | @Override 203 | public int getNumberOfConnectionsMade() throws RemoteException { 204 | // TODO Auto-generated method stub 205 | Log.w(TAG, "Not yet implemented: GTalkConnection.getNumberOfConnectionsMade"); 206 | return 0; 207 | } 208 | 209 | public Presence getPresence() { 210 | return presence; 211 | } 212 | 213 | public String getResource() { 214 | if (getBackend() != null && getBackend().getXmpp() != null && getBackend().getXmpp().getUser() != null && 215 | getBackend().getXmpp().getUser().contains("/")) { 216 | return getBackend().getXmpp().getUser().split("/")[1]; 217 | } 218 | return RESOURCE; 219 | } 220 | 221 | public String getServiceHost() { 222 | return account.getServiceHost(); 223 | } 224 | 225 | public String getServiceName() { 226 | Log.d(TAG, "GTalkConnection.getServiceName=" + account.getServiceName()); 227 | return account.getServiceName(); 228 | } 229 | 230 | public int getServicePort() { 231 | return XMPP_PORT; 232 | } 233 | 234 | @Override 235 | public String getUsername() throws RemoteException { 236 | return account.getUsername(); 237 | } 238 | 239 | @Override 240 | public boolean isConnected() throws RemoteException { 241 | // TODO Auto-generated method stub 242 | Log.w(TAG, "Not yet implemented: GTalkConnection.isConnected"); 243 | return false; 244 | } 245 | 246 | public void onChatSessionAvailable(final String user, final IChatSession chatSession) { 247 | imSession.onChatSessionAvailable(user, chatSession); 248 | } 249 | 250 | public void onPresenceChanged(final String user) { 251 | imSession.onPresenceChanged(user); 252 | } 253 | 254 | public void onRosterChanged() { 255 | imSession.onRosterChanged(); 256 | } 257 | 258 | public void onSelfPresenceChanged() { 259 | imSession.onSelfPresenceChanged(); 260 | saveAccount(); 261 | saveAccountStatus(); 262 | } 263 | 264 | public void readAccount() { 265 | final Cursor c = getContext().getContentResolver() 266 | .query(TalkProvider.ACCOUNTS_URI, new String[]{"last_login_state"}, "username LIKE ?", 267 | new String[]{getAccount().getUsername()}, null); 268 | if (c.moveToFirst()) { 269 | presence = new Presence(c.getInt(0)); 270 | } 271 | c.close(); 272 | } 273 | 274 | public void saveAccount() { 275 | final ContentValues values = new ContentValues(); 276 | values.put("name", getAccount().getUsername()); 277 | values.put("username", getAccount().getJid()); 278 | values.put("last_login_state", getPresence().getNumeric()); 279 | getContext().getContentResolver().insert(TalkProvider.ACCOUNTS_URI, values); 280 | } 281 | 282 | public void saveAccountStatus() { 283 | final ContentValues values = new ContentValues(); 284 | values.put("account", getAccountId()); 285 | values.put("presenceStatus", getPresence().getNumeric()); 286 | values.put("connStatus", getConnectionState().getState()); 287 | getContext().getContentResolver().insert(TalkProvider.ACCOUNT_STATUS_URI, values); 288 | } 289 | 290 | @Override 291 | public void sendHeartbeat() throws RemoteException { 292 | // TODO Auto-generated method stub 293 | Log.w(TAG, "Not yet implemented: GTalkConnection.sendHeartbeat"); 294 | 295 | } 296 | 297 | @Override 298 | public void sendHttpRequest(final byte[] bytes, final IHttpRequestCallback httprequestcallback) 299 | throws RemoteException { 300 | // TODO Auto-generated method stub 301 | Log.w(TAG, "Not yet implemented: GTalkConnection.sendHttpRequest"); 302 | 303 | } 304 | 305 | public void setConnectionError(final ConnectionError connectionError) { 306 | Log.d(TAG, "GTalkConnection.setConnectionError(" + connectionError + ")"); 307 | this.connectionError = connectionError; 308 | imSession.onConnectionStateChanged(); 309 | } 310 | 311 | public void setConnectionError(final int connectionError) { 312 | Log.d(TAG, "GTalkConnection.setConnectionError(" + connectionError + ")"); 313 | setConnectionError(new ConnectionError(connectionError)); 314 | } 315 | 316 | public void setConnectionState(final ConnectionState connectionState) { 317 | this.connectionState = connectionState; 318 | imSession.onConnectionStateChanged(); 319 | saveAccountStatus(); 320 | } 321 | 322 | public void setConnectionState(final int connectionState) { 323 | setConnectionState(new ConnectionState(connectionState)); 324 | } 325 | 326 | public void setPresence(final Presence presence) { 327 | backendHandler.presence(presence); 328 | this.presence = presence; 329 | } 330 | 331 | public void setTalkForegroundState(final boolean talkIsForeground) { 332 | if (presence.isAvailable() && !presence.isInvisible()) { 333 | if (talkIsForeground && presence.getShow() == Show.AWAY) { 334 | presence.setShow(Show.AVAILABLE); 335 | setPresence(presence); 336 | } else if (!talkIsForeground && presence.getShow() == Show.AVAILABLE) { 337 | presence.setShow(Show.AWAY); 338 | setPresence(presence); 339 | } 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/GTalkService.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice.service; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.database.Cursor; 6 | import android.os.IBinder; 7 | import android.os.Looper; 8 | import android.os.Process; 9 | import android.os.RemoteException; 10 | import android.util.Log; 11 | import com.google.android.gsf.talk.TalkProvider; 12 | import com.google.android.gtalkservice.IGTalkConnection; 13 | import com.google.android.gtalkservice.IGTalkConnectionListener; 14 | import com.google.android.gtalkservice.IGTalkService; 15 | import com.google.android.gtalkservice.IImSession; 16 | 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | public class GTalkService extends Service { 23 | 24 | private final static String TAG = "GoogleTalkService"; 25 | 26 | private final Map accounts = new HashMap(); 27 | private final IGTalkService.Stub binder = new GTalkServiceBinder(this); 28 | private final Map gTalkConnections = new HashMap(); 29 | private final Thread talkForeground = new Thread(new Runnable() { 30 | 31 | @Override 32 | public void run() { 33 | boolean localTalkIsForeground = talkIsForeground; 34 | while (true) { 35 | synchronized (talkForegroundLock) { 36 | try { 37 | talkForegroundLock.wait(30000); 38 | } catch (final InterruptedException e) { 39 | return; 40 | } 41 | if (talkIsForeground) { 42 | talkIsForeground = false; 43 | localTalkIsForeground = true; 44 | } 45 | } 46 | for (final IGTalkConnection connection : gTalkConnections.values()) { 47 | final GTalkConnection c = (GTalkConnection) connection; 48 | c.setTalkForegroundState(localTalkIsForeground); 49 | } 50 | localTalkIsForeground = false; 51 | } 52 | } 53 | }); 54 | private final Object talkForegroundLock = new Object(); 55 | private boolean talkIsForeground = false; 56 | private Looper workerLooper; 57 | 58 | private Account createAccount(String username) { 59 | if (!username.contains(":")) { 60 | username = "com.google:" + username; 61 | } 62 | final Account account = Account.getAccount(username, this); 63 | accounts.put(account.getId(), account); 64 | return account; 65 | } 66 | 67 | private IGTalkConnection createGTalkConnection(final String username, final boolean autoLogin) { 68 | final IGTalkConnection connection = new GTalkConnection(this, createAccount(username), workerLooper, autoLogin); 69 | gTalkConnections.put(username, connection); 70 | return connection; 71 | } 72 | 73 | public void createGTalkConnection(final String username, final IGTalkConnectionListener listener) { 74 | try { 75 | final IGTalkConnection connection = createGTalkConnection(username, false); 76 | listener.onConnectionCreated(connection); 77 | } catch (final Throwable e) { 78 | Log.w(GTalkService.TAG, e); 79 | try { 80 | listener.onConnectionCreationFailed(username); 81 | } catch (final Exception e2) { 82 | } 83 | } 84 | } 85 | 86 | public void dismissNotificationFor(final String s, final long l) { 87 | ((GTalkConnection) getConnectionForAccountId(l)).getNotifier().dismissChatMessages(s); 88 | } 89 | 90 | public List getActiveConnections() { 91 | final List list = new ArrayList(); 92 | for (final IGTalkConnection con : gTalkConnections.values()) { 93 | list.add(con); 94 | } 95 | return list; 96 | } 97 | 98 | public IGTalkConnection getConnectionForAccountId(final long l) { 99 | final String user = getUserForAccountId(l); 100 | return getConnectionForUser(user); 101 | } 102 | 103 | public IGTalkConnection getConnectionForUser(final String username) { 104 | IGTalkConnection connection = null; 105 | synchronized (gTalkConnections) { 106 | connection = gTalkConnections.get(username); 107 | } 108 | if (connection == null) { 109 | connection = createGTalkConnection(username, true); 110 | } 111 | return connection; 112 | } 113 | 114 | public IImSession getImSessionForAccountId(final long l) throws RemoteException { 115 | final IGTalkConnection connection = getConnectionForAccountId(l); 116 | return connection.getImSessionForAccountId(l); 117 | } 118 | 119 | public String getUserForAccountId(final long id) { 120 | final Account account = accounts.get(id); 121 | if (account == null) { 122 | final Cursor c = getContentResolver() 123 | .query(TalkProvider.ACCOUNTS_URI, new String[]{"username"}, "_id=?", new String[]{id + ""}, null); 124 | if (c.moveToFirst()) { 125 | final String username = c.getString(0); 126 | c.close(); 127 | return username; 128 | } else { 129 | c.close(); 130 | return null; 131 | } 132 | } 133 | return account.getUsername(); 134 | } 135 | 136 | @Override 137 | public IBinder onBind(final Intent intent) { 138 | if (IGTalkService.class.getName().equals(intent.getAction())) { 139 | return binder; 140 | } 141 | return null; 142 | } 143 | 144 | @Override 145 | public void onCreate() { 146 | new Thread(new Runnable() { 147 | 148 | @Override 149 | public void run() { 150 | Looper.prepare(); 151 | workerLooper = Looper.myLooper(); 152 | Process.setThreadPriority(10); 153 | Looper.loop(); 154 | } 155 | }).start(); 156 | talkForeground.start(); 157 | super.onCreate(); 158 | } 159 | 160 | @Override 161 | public void onDestroy() { 162 | workerLooper.quit(); 163 | talkForeground.interrupt(); 164 | Log.w(TAG, "Not yet implemented: GTalkService.onDestroy"); 165 | super.onDestroy(); 166 | 167 | } 168 | 169 | public void setTalkForegroundState() { 170 | synchronized (talkForegroundLock) { 171 | talkIsForeground = true; 172 | talkForegroundLock.notify(); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/GTalkServiceBinder.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice.service; 2 | 3 | import android.os.RemoteException; 4 | import android.util.Log; 5 | import com.google.android.gtalkservice.IGTalkConnection; 6 | import com.google.android.gtalkservice.IGTalkConnectionListener; 7 | import com.google.android.gtalkservice.IGTalkService; 8 | import com.google.android.gtalkservice.IImSession; 9 | 10 | import java.util.List; 11 | 12 | public class GTalkServiceBinder extends IGTalkService.Stub { 13 | 14 | private static final String TAG = "GoogleTalkServiceBinder"; 15 | 16 | private final GTalkService service; 17 | 18 | public GTalkServiceBinder(final GTalkService service) { 19 | this.service = service; 20 | } 21 | 22 | @Override 23 | public void createGTalkConnection(final String username, final IGTalkConnectionListener listener) 24 | throws RemoteException { 25 | service.createGTalkConnection(username, listener); 26 | } 27 | 28 | @Override 29 | public void dismissAllNotifications() throws RemoteException { 30 | // TODO Auto-generated method stub 31 | Log.w(TAG, "Not yet implemented: IGTalkService.dismissAllNotifications"); 32 | } 33 | 34 | @Override 35 | public void dismissNotificationFor(final String s, final long l) throws RemoteException { 36 | service.dismissNotificationFor(s, l); 37 | } 38 | 39 | @Override 40 | public void dismissNotificationsForAccount(final long l) throws RemoteException { 41 | // TODO Auto-generated method stub 42 | Log.w(TAG, "Not yet implemented: IGTalkService.dismissNotificationsForAccount(" + l + ")"); 43 | } 44 | 45 | @Override 46 | public List getActiveConnections() throws RemoteException { 47 | return service.getActiveConnections(); 48 | } 49 | 50 | @Override 51 | public IGTalkConnection getConnectionForUser(final String s) throws RemoteException { 52 | return service.getConnectionForUser(s); 53 | } 54 | 55 | @Override 56 | public IGTalkConnection getDefaultConnection() throws RemoteException { 57 | // TODO Auto-generated method stub 58 | Log.w(TAG, "Not yet implemented: IGTalkService.getDefaultConnection"); 59 | return null; 60 | } 61 | 62 | @Override 63 | public boolean getDeviceStorageLow() throws RemoteException { 64 | // TODO Auto-generated method stub 65 | Log.w(TAG, "Not yet implemented: IGTalkService.getDeviceStorageLow"); 66 | return false; 67 | } 68 | 69 | @Override 70 | public IImSession getImSessionForAccountId(final long l) throws RemoteException { 71 | try { 72 | return service.getImSessionForAccountId(l); 73 | } catch (final RuntimeException e) { 74 | Log.w(TAG, e); 75 | throw e; 76 | } 77 | } 78 | 79 | @Override 80 | public String printDiagnostics() throws RemoteException { 81 | // TODO Auto-generated method stub 82 | Log.w(TAG, "Not yet implemented: IGTalkService.printDiagnostics"); 83 | return null; 84 | } 85 | 86 | @Override 87 | public void setTalkForegroundState() throws RemoteException { 88 | service.setTalkForegroundState(); 89 | } 90 | } -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/ImSession.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice.service; 2 | 3 | import android.graphics.Bitmap; 4 | import android.os.RemoteException; 5 | import android.util.Log; 6 | import com.google.android.gtalkservice.*; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ImSession extends IImSession.Stub { 12 | 13 | private final static String TAG = "GoogleImSession"; 14 | private final List chatListeners = new ArrayList(); 15 | private final GTalkConnection connection; 16 | private final List connectionStateListeners = new ArrayList(); 17 | private final List groupChatInvitationListeners = 18 | new ArrayList(); 19 | private final List rosterListeners = new ArrayList(); 20 | 21 | public ImSession(final GTalkConnection connection) { 22 | Log.w(TAG, "Not yet implemented: IImSession.ctor"); 23 | this.connection = connection; 24 | } 25 | 26 | @Override 27 | public void addConnectionStateListener(final IConnectionStateListener listener) throws RemoteException { 28 | Log.d(TAG, "IImSession.addConnectionStateListener(" + listener + ")"); 29 | connectionStateListeners.add(listener); 30 | } 31 | 32 | @Override 33 | public void addContact(final String s, final String s1, final String[] as) throws RemoteException { 34 | // TODO Auto-generated method stub 35 | Log.w(TAG, "Not yet implemented: IImSession.addContact"); 36 | 37 | } 38 | 39 | @Override 40 | public void addGroupChatInvitationListener(final IGroupChatInvitationListener listener) throws RemoteException { 41 | Log.d(TAG, "IImSession.addGroupChatInvitationListener(" + listener + ")"); 42 | groupChatInvitationListeners.add(listener); 43 | } 44 | 45 | @Override 46 | public void addRemoteChatListener(final IChatListener listener) throws RemoteException { 47 | Log.d(TAG, "IImSession.addRemoteChatListener(" + listener + ")"); 48 | chatListeners.add(listener); 49 | } 50 | 51 | @Override 52 | public void addRemoteJingleInfoStanzaListener(final IJingleInfoStanzaListener ijingleinfostanzalistener) 53 | throws RemoteException { 54 | // TODO Auto-generated method stub 55 | Log.w(TAG, "Not yet implemented: IImSession.addRemoteJingleInfoStanzaListener"); 56 | 57 | } 58 | 59 | @Override 60 | public void addRemoteRosterListener(final IRosterListener listener) throws RemoteException { 61 | Log.d(TAG, "IImSession.addRemoteRosterListener(" + listener + ")"); 62 | rosterListeners.add(listener); 63 | } 64 | 65 | @Override 66 | public void addRemoteSessionStanzaListener(final ISessionStanzaListener isessionstanzalistener) 67 | throws RemoteException { 68 | // TODO Auto-generated method stub 69 | Log.w(TAG, "Not yet implemented: IImSession.addRemoteSessionStanzaListener"); 70 | 71 | } 72 | 73 | @Override 74 | public void approveSubscriptionRequest(final String s, final String s1, final String[] as) throws RemoteException { 75 | // TODO Auto-generated method stub 76 | Log.w(TAG, "Not yet implemented: IImSession.approveSubscriptionRequest"); 77 | 78 | } 79 | 80 | @Override 81 | public void blockContact(final String s) throws RemoteException { 82 | // TODO Auto-generated method stub 83 | Log.w(TAG, "Not yet implemented: IImSession.blockContact"); 84 | 85 | } 86 | 87 | @Override 88 | public void clearContactFlags(final String s) throws RemoteException { 89 | // TODO Auto-generated method stub 90 | Log.w(TAG, "Not yet implemented: IImSession.clearContactFlags"); 91 | 92 | } 93 | 94 | @Override 95 | public void closeAllChatSessions() throws RemoteException { 96 | connection.closeAllChats(); 97 | } 98 | 99 | @Override 100 | public IChatSession createChatSession(final String user) throws RemoteException { 101 | return connection.getChatSession(user, true); 102 | } 103 | 104 | @Override 105 | public void createGroupChatSession(final String s, final String[] as) throws RemoteException { 106 | // TODO Auto-generated method stub 107 | Log.w(TAG, "Not yet implemented: IImSession.createGroupChatSession"); 108 | 109 | } 110 | 111 | @Override 112 | public void declineGroupChatInvitation(final String s, final String s1) throws RemoteException { 113 | // TODO Auto-generated method stub 114 | Log.w(TAG, "Not yet implemented: IImSession.declineGroupChatInvitation"); 115 | 116 | } 117 | 118 | @Override 119 | public void declineSubscriptionRequest(final String s) throws RemoteException { 120 | // TODO Auto-generated method stub 121 | Log.w(TAG, "Not yet implemented: IImSession.declineSubscriptionRequest"); 122 | 123 | } 124 | 125 | @Override 126 | public void editContact(final String s, final String s1, final String[] as) throws RemoteException { 127 | // TODO Auto-generated method stub 128 | Log.w(TAG, "Not yet implemented: IImSession.editContact"); 129 | 130 | } 131 | 132 | @Override 133 | public long getAccountId() throws RemoteException { 134 | return connection.getAccountId(); 135 | } 136 | 137 | @Override 138 | public IChatSession getChatSession(final String user) throws RemoteException { 139 | return connection.getChatSession(user, false); 140 | } 141 | 142 | @Override 143 | public ConnectionState getConnectionState() throws RemoteException { 144 | return connection.getConnectionState(); 145 | } 146 | 147 | @Override 148 | public String getJid() throws RemoteException { 149 | return connection.getJid(); 150 | } 151 | 152 | @Override 153 | public Presence getPresence() throws RemoteException { 154 | return connection.getPresence(); 155 | } 156 | 157 | @Override 158 | public String getUsername() throws RemoteException { 159 | return connection.getUsername(); 160 | } 161 | 162 | @Override 163 | public void goOffRecordInRoom(final String s, final boolean flag) throws RemoteException { 164 | // TODO Auto-generated method stub 165 | Log.w(TAG, "Not yet implemented: IImSession.goOffRecordInRoom"); 166 | 167 | } 168 | 169 | @Override 170 | public void goOffRecordWithContacts(final List list, final boolean flag) throws RemoteException { 171 | // TODO Auto-generated method stub 172 | Log.w(TAG, "Not yet implemented: IImSession.goOffRecordWithContacts"); 173 | 174 | } 175 | 176 | @Override 177 | public void hideContact(final String s) throws RemoteException { 178 | // TODO Auto-generated method stub 179 | Log.w(TAG, "Not yet implemented: IImSession.hideContact"); 180 | 181 | } 182 | 183 | @Override 184 | public void inviteContactsToGroupchat(final String groupChat, final String[] contacts) throws RemoteException { 185 | // TODO Auto-generated method stub 186 | Log.w(TAG, "Not yet implemented: IImSession.inviteContactsToGroupchat"); 187 | 188 | } 189 | 190 | @Override 191 | public boolean isOffRecordWithContact(final String contact) throws RemoteException { 192 | // TODO Auto-generated method stub 193 | Log.w(TAG, "Not yet implemented: IImSession.isOffRecordWithContact"); 194 | return false; 195 | } 196 | 197 | @Override 198 | public void joinGroupChatSession(final String s, final String s1, final String s2) throws RemoteException { 199 | // TODO Auto-generated method stub 200 | Log.w(TAG, "Not yet implemented: IImSession.joinGroupChatSession"); 201 | 202 | } 203 | 204 | @Override 205 | public void login(final String username, final boolean flag) throws RemoteException { 206 | // TODO Auto-generated method stub 207 | Log.d(TAG, "IImSession.login(" + username + ", " + flag + ")"); 208 | connection.getBackend().login(); 209 | } 210 | 211 | @Override 212 | public void logout() throws RemoteException { 213 | connection.getBackend().logout(); 214 | } 215 | 216 | public void onChatSessionAvailable(final String user, final IChatSession chatSession) { 217 | Log.d(TAG, "IImSession.onChatSessionAvailable(" + user + ", " + chatSession + ")"); 218 | synchronized (chatListeners) { 219 | final List toRemove = new ArrayList(); 220 | for (int i = 0; i < chatListeners.size(); i++) { 221 | final IChatListener listener = chatListeners.get(i); 222 | try { 223 | listener.chatRead(user); 224 | } catch (final RemoteException e) { 225 | Log.w(TAG, e); 226 | toRemove.add(listener); 227 | } 228 | } 229 | for (final IChatListener listener : toRemove) { 230 | chatListeners.remove(listener); 231 | } 232 | } 233 | } 234 | 235 | public void onConnectionStateChanged() { 236 | Log.d(TAG, "IImSession.onConnectionStateChanged"); 237 | synchronized (connectionStateListeners) { 238 | final List toRemove = new ArrayList(); 239 | for (int i = 0; i < connectionStateListeners.size(); i++) { 240 | final IConnectionStateListener listener = connectionStateListeners.get(i); 241 | try { 242 | listener.connectionStateChanged(connection.getConnectionState(), connection.getConnectionError(), 243 | connection.getAccountId(), connection.getUsername()); 244 | } catch (final RemoteException e) { 245 | Log.w(TAG, e); 246 | toRemove.add(listener); 247 | } 248 | } 249 | for (final IConnectionStateListener listener : toRemove) { 250 | connectionStateListeners.remove(listener); 251 | } 252 | } 253 | } 254 | 255 | public boolean onMessageReceived(final String user, final String message) { 256 | boolean notify = true; 257 | synchronized (chatListeners) { 258 | final List toRemove = new ArrayList(); 259 | for (int i = 0; i < chatListeners.size(); i++) { 260 | final IChatListener listener = chatListeners.get(i); 261 | try { 262 | listener.newMessageReceived(user, message, true); 263 | if (listener.useLightweightNotify()) { 264 | notify = false; 265 | } 266 | } catch (final RemoteException e) { 267 | Log.w(TAG, e); 268 | toRemove.add(listener); 269 | } 270 | } 271 | for (final IChatListener listener : toRemove) { 272 | chatListeners.remove(listener); 273 | } 274 | } 275 | return notify; 276 | } 277 | 278 | public void onPresenceChanged(final String user) { 279 | Log.d(TAG, "IImSession.onPresenceChanged(" + user + ")"); 280 | synchronized (rosterListeners) { 281 | final List toRemove = new ArrayList(); 282 | for (int i = 0; i < rosterListeners.size(); i++) { 283 | final IRosterListener listener = rosterListeners.get(i); 284 | try { 285 | listener.presenceChanged(user); 286 | } catch (final RemoteException e) { 287 | Log.w(TAG, e); 288 | toRemove.add(listener); 289 | } 290 | } 291 | for (final IRosterListener listener : toRemove) { 292 | rosterListeners.remove(listener); 293 | } 294 | } 295 | } 296 | 297 | public void onRosterChanged() { 298 | Log.d(TAG, "IImSession.onRosterChanged"); 299 | synchronized (rosterListeners) { 300 | final List toRemove = new ArrayList(); 301 | for (int i = 0; i < rosterListeners.size(); i++) { 302 | final IRosterListener listener = rosterListeners.get(i); 303 | try { 304 | listener.rosterChanged(); 305 | } catch (final RemoteException e) { 306 | Log.w(TAG, e); 307 | toRemove.add(listener); 308 | } 309 | } 310 | for (final IRosterListener listener : toRemove) { 311 | rosterListeners.remove(listener); 312 | } 313 | } 314 | } 315 | 316 | public void onSelfPresenceChanged() { 317 | Log.d(TAG, "IImSession.onSelfPresenceChanged"); 318 | synchronized (rosterListeners) { 319 | final List toRemove = new ArrayList(); 320 | for (int i = 0; i < rosterListeners.size(); i++) { 321 | final IRosterListener listener = rosterListeners.get(i); 322 | try { 323 | listener.selfPresenceChanged(); 324 | } catch (final RemoteException e) { 325 | Log.w(TAG, e); 326 | toRemove.add(listener); 327 | } 328 | } 329 | for (final IRosterListener listener : toRemove) { 330 | rosterListeners.remove(listener); 331 | } 332 | } 333 | } 334 | 335 | @Override 336 | public void pinContact(final String s) throws RemoteException { 337 | // TODO Auto-generated method stub 338 | Log.w(TAG, "Not yet implemented: IImSession.pinContact"); 339 | 340 | } 341 | 342 | @Override 343 | public void pruneOldChatSessions(final long l, final long l1, final long l2, final boolean flag) 344 | throws RemoteException { 345 | // TODO Auto-generated method stub 346 | Log.w(TAG, 347 | "Not yet implemented: IImSession.pruneOldChatSessions(" + l + ", " + l1 + ", " + l2 + ", " + flag + ")"); 348 | 349 | } 350 | 351 | @Override 352 | public void queryJingleInfo() throws RemoteException { 353 | // TODO Auto-generated method stub 354 | Log.w(TAG, "Not yet implemented: IImSession.queryJingleInfo"); 355 | 356 | } 357 | 358 | @Override 359 | public void removeConnectionStateListener(final IConnectionStateListener listener) throws RemoteException { 360 | connectionStateListeners.remove(listener); 361 | } 362 | 363 | @Override 364 | public void removeContact(final String s) throws RemoteException { 365 | // TODO Auto-generated method stub 366 | Log.w(TAG, "Not yet implemented: IImSession.removeContact"); 367 | 368 | } 369 | 370 | @Override 371 | public void removeGroupChatInvitationListener(final IGroupChatInvitationListener listener) throws RemoteException { 372 | groupChatInvitationListeners.remove(listener); 373 | } 374 | 375 | @Override 376 | public void removeRemoteChatListener(final IChatListener listener) throws RemoteException { 377 | chatListeners.remove(listener); 378 | } 379 | 380 | @Override 381 | public void removeRemoteJingleInfoStanzaListener(final IJingleInfoStanzaListener ijingleinfostanzalistener) 382 | throws RemoteException { 383 | // TODO Auto-generated method stub 384 | Log.w(TAG, "Not yet implemented: IImSession.removeRemoteJingleInfoStanzaListener"); 385 | 386 | } 387 | 388 | @Override 389 | public void removeRemoteRosterListener(final IRosterListener listener) throws RemoteException { 390 | rosterListeners.remove(listener); 391 | } 392 | 393 | @Override 394 | public void removeRemoteSessionStanzaListener(final ISessionStanzaListener isessionstanzalistener) 395 | throws RemoteException { 396 | // TODO Auto-generated method stub 397 | Log.w(TAG, "Not yet implemented: IImSession.removeRemoteSessionStanzaListener"); 398 | 399 | } 400 | 401 | @Override 402 | public void requestBatchedBuddyPresence() throws RemoteException { 403 | Log.d(TAG, "IImSession.requestBatchedBuddyPresence"); 404 | connection.getBackend().requestRoster(); 405 | } 406 | 407 | @Override 408 | public void sendCallPerfStatsStanza(final String s) throws RemoteException { 409 | // TODO Auto-generated method stub 410 | Log.w(TAG, "Not yet implemented: IImSession.sendCallPerfStatsStanza"); 411 | 412 | } 413 | 414 | @Override 415 | public void sendSessionStanza(final String s) throws RemoteException { 416 | // TODO Auto-generated method stub 417 | Log.w(TAG, "Not yet implemented: IImSession.sendSessionStanza"); 418 | 419 | } 420 | 421 | @Override 422 | public void setPresence(final Presence presence) throws RemoteException { 423 | connection.setPresence(presence); 424 | } 425 | 426 | @Override 427 | public void uploadAvatar(final Bitmap bitmap) throws RemoteException { 428 | // TODO Auto-generated method stub 429 | Log.w(TAG, "Not yet implemented: IImSession.uploadAvatar"); 430 | 431 | } 432 | 433 | @Override 434 | public void uploadAvatarFromDb() throws RemoteException { 435 | // TODO Auto-generated method stub 436 | Log.w(TAG, "Not yet implemented: IImSession.uploadAvatarFromDb"); 437 | 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/gtalkservice/service/Notifier.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.gtalkservice.service; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Notification; 5 | import android.app.NotificationManager; 6 | import android.content.Context; 7 | import android.graphics.Bitmap; 8 | import android.graphics.Bitmap.Config; 9 | import android.graphics.Canvas; 10 | import android.graphics.drawable.BitmapDrawable; 11 | import android.graphics.drawable.Drawable; 12 | import android.os.Build; 13 | import com.google.android.gsf.R; 14 | import com.google.android.gtalkservice.IChatSession; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | public class Notifier { 20 | 21 | private class NotifyChatMessage { 22 | private final String from; 23 | private final String message; 24 | 25 | public NotifyChatMessage(final String from, final String message) { 26 | this.from = from; 27 | this.message = message; 28 | } 29 | 30 | public String getFrom() { 31 | return from; 32 | } 33 | 34 | public String getMessage() { 35 | return message; 36 | } 37 | } 38 | 39 | private static final int NOTIFY_CHAT_MESSAGE = 12345; 40 | private static final String TAG = "GoogleTalkNotifier"; 41 | 42 | public static Bitmap drawableToBitmap(final Drawable drawable) { 43 | if (drawable instanceof BitmapDrawable) { 44 | return ((BitmapDrawable) drawable).getBitmap(); 45 | } 46 | 47 | final Bitmap bitmap = 48 | Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888); 49 | final Canvas canvas = new Canvas(bitmap); 50 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 51 | drawable.draw(canvas); 52 | 53 | return bitmap; 54 | } 55 | 56 | private final GTalkConnection connection; 57 | private final List currentChatMessages = new ArrayList(); 58 | 59 | private final NotificationManager nm; 60 | 61 | public Notifier(final GTalkConnection connection) { 62 | this.connection = connection; 63 | nm = (NotificationManager) connection.getContext().getSystemService(Context.NOTIFICATION_SERVICE); 64 | } 65 | 66 | public void dismissChatMessages() { 67 | currentChatMessages.clear(); 68 | notifyChatMessages(false); 69 | } 70 | 71 | public void dismissChatMessages(String user) { 72 | if (user.contains("/")) { 73 | user = user.split("/")[0]; 74 | } 75 | for (final NotifyChatMessage entry : currentChatMessages) { 76 | if (entry.getFrom().equalsIgnoreCase(user)) { 77 | currentChatMessages.remove(entry); 78 | } 79 | } 80 | notifyChatMessages(false); 81 | } 82 | 83 | public void notifyChatMessage(final IChatSession chatSession, final long contactId, final String user, 84 | final String message) { 85 | currentChatMessages.add(new NotifyChatMessage(user, message)); 86 | notifyChatMessages(true); 87 | } 88 | 89 | private void notifyChatMessages(final boolean ticker) { 90 | if (currentChatMessages.size() == 0) { 91 | nm.cancel(NOTIFY_CHAT_MESSAGE); 92 | return; 93 | } 94 | final Notification.Builder builder = new Notification.Builder(connection.getContext()); 95 | builder.setAutoCancel(false); 96 | builder.setSmallIcon(R.drawable.stat_notify_msg); 97 | builder.setNumber(currentChatMessages.size()); 98 | String user = null; 99 | String message = null; 100 | for (final NotifyChatMessage entry : currentChatMessages) { 101 | user = entry.getFrom(); 102 | message = entry.getMessage(); 103 | } 104 | if (ticker) { 105 | builder.setOnlyAlertOnce(false); 106 | builder.setTicker(user + ": " + message); 107 | } 108 | builder.setContentTitle(user); 109 | builder.setContentText(message); 110 | builder.setLargeIcon( 111 | drawableToBitmap(connection.getContext().getResources().getDrawable(R.drawable.ic_contact_picture))); 112 | nm.notify(NOTIFY_CHAT_MESSAGE, versionSpecific(builder)); 113 | } 114 | 115 | @SuppressLint("NewApi") 116 | @SuppressWarnings("deprecation") 117 | private Notification versionSpecific(Notification.Builder builder) { 118 | if (Build.VERSION.SDK_INT >= 16) { 119 | builder.setPriority(Notification.PRIORITY_HIGH); 120 | return builder.build(); 121 | } else { 122 | return builder.getNotification(); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/loginservice/MigrateToAccountManagerBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.loginservice; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | public class MigrateToAccountManagerBroadcastReceiver extends BroadcastReceiver { 9 | 10 | private static final String TAG = "MigrateToAccountManagerBroadcastReceiver"; 11 | 12 | @Override 13 | public void onReceive(Context context, Intent intent) { 14 | // TODO Auto-generated method stub 15 | Log.w(TAG, "Not yet implemented: MigrateToAccountManagerBroadcastReceiver.onReceive: " + intent); 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/loginservice/ServicesWatcher.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.loginservice; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | public class ServicesWatcher extends BroadcastReceiver { 9 | 10 | private static final String TAG = "ServicesWatcher"; 11 | 12 | @Override 13 | public void onReceive(Context context, Intent intent) { 14 | // TODO Auto-generated method stub 15 | Log.w(TAG, "Not yet implemented: ServicesWatcher.onReceive"); 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/settings/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.settings; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.database.sqlite.SQLiteOpenHelper; 7 | import android.database.sqlite.SQLiteStatement; 8 | import android.provider.Settings; 9 | import com.google.android.gsf.R; 10 | 11 | public class DatabaseHelper extends SQLiteOpenHelper { 12 | private static int DATABASE_VERSION = 2; 13 | 14 | private boolean assistedGpsSettingNeedsUpdate; 15 | private final Context context; 16 | 17 | public DatabaseHelper(final Context context) { 18 | super(context, "googlesettings.db", null, DATABASE_VERSION); 19 | this.context = context; 20 | } 21 | 22 | final boolean assistedGpsSettingNeedsUpdate() { 23 | return assistedGpsSettingNeedsUpdate; 24 | } 25 | 26 | private void insertDefaultPartnerSettings(final SQLiteDatabase db) { 27 | { 28 | ContentValues cv = new ContentValues(); 29 | cv.put("name", "client_id"); 30 | cv.put("value", "android-google"); 31 | db.insertWithOnConflict("partner", null, cv, SQLiteDatabase.CONFLICT_IGNORE); 32 | } 33 | 34 | final String s = Settings.Secure.getString(context.getContentResolver(), "logging_id2"); 35 | if (s != null) { 36 | ContentValues cv = new ContentValues(); 37 | cv.put("name", "logging_id2"); 38 | cv.put("value", s); 39 | db.insertWithOnConflict("partner", null, cv, SQLiteDatabase.CONFLICT_IGNORE); 40 | } 41 | } 42 | 43 | @Override 44 | public void onCreate(final SQLiteDatabase db) { 45 | db.execSQL("CREATE TABLE partner (_id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT UNIQUE ON CONFLICT REPLACE,value TEXT);"); 46 | insertDefaultPartnerSettings(db); 47 | } 48 | 49 | @Override 50 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/settings/GoogleSettingsProvider.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.settings; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentResolver; 5 | import android.content.ContentValues; 6 | import android.database.Cursor; 7 | import android.database.sqlite.SQLiteDatabase; 8 | import android.database.sqlite.SQLiteQueryBuilder; 9 | import android.net.Uri; 10 | import android.provider.Settings; 11 | import android.util.Log; 12 | import com.google.android.gsf.GoogleSettingsContract; 13 | 14 | public class GoogleSettingsProvider extends ContentProvider { 15 | 16 | private static final String TAG = "GoogleSettingsProvider"; 17 | 18 | private boolean linkAssistedGps = false; 19 | private DatabaseHelper openHelper; 20 | 21 | private void checkWritePermissions(final SqlArguments arguments) { 22 | // TODO Auto-generated method stub 23 | Log.w(TAG, "Not yet implemented: GoogleSettingsProvider.checkWritePermissions"); 24 | } 25 | 26 | @Override 27 | public int delete(final Uri uri, final String selection, final String[] selectionArgs) { 28 | final SqlArguments arguments = new SqlArguments(uri, selection, selectionArgs); 29 | checkWritePermissions(arguments); 30 | final int result = openHelper.getWritableDatabase().delete(arguments.table, arguments.where, arguments.args); 31 | if (result > 0) { 32 | sendNotify(uri); 33 | } 34 | return result; 35 | } 36 | 37 | @Override 38 | public String getType(final Uri uri) { 39 | final SqlArguments arguments = new SqlArguments(uri); 40 | if (arguments.where == null || arguments.where.isEmpty()) { 41 | return "vnd.android.cursor.dir/" + arguments.table; 42 | } 43 | return "vnd.android.cursor.item/" + arguments.table; 44 | } 45 | 46 | private Uri getUriFor(final Uri uri, final ContentValues values, final long l) { 47 | // TODO Auto-generated method stub 48 | Log.w(TAG, "Not yet implemented: GoogleSettingsProvider.getUriFor"); 49 | return null; 50 | } 51 | 52 | @Override 53 | public Uri insert(Uri uri, final ContentValues values) { 54 | final SqlArguments arguments = new SqlArguments(uri); 55 | checkWritePermissions(arguments); 56 | final long l = openHelper.getWritableDatabase().insert(arguments.table, null, values); 57 | if (l > 0) { 58 | uri = getUriFor(uri, values, l); 59 | sendNotify(uri); 60 | return uri; 61 | } 62 | return null; 63 | } 64 | 65 | public static String getSystemProperty(String name, String def) { 66 | try { 67 | return (String) Class.forName("android.os.SystemProperties").getMethod("get", String.class, String.class).invoke(null, name, def); 68 | } catch (Exception e) { 69 | Log.w("SystemPropertiesReflection", e); 70 | return def; 71 | } 72 | } 73 | 74 | @Override 75 | public boolean onCreate() { 76 | openHelper = new DatabaseHelper(getContext()); 77 | final String agps = getSystemProperty("ro.gps.agps_provider", null); 78 | if (agps == null || agps.isEmpty() || agps.toLowerCase().contains("google") || 79 | agps.toLowerCase().contains("μg")) { 80 | linkAssistedGps = true; 81 | } 82 | return true; 83 | } 84 | 85 | @Override 86 | public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, 87 | final String sortOrder) { 88 | /*Log.d(TAG, "query(" + uri + ", " + projection + ", " + selection + ", " + 89 | selectionArgs + ", " + sortOrder + ")");*/ 90 | final SqlArguments sqlargs = new SqlArguments(uri, selection, selectionArgs); 91 | final SQLiteDatabase db = openHelper.getReadableDatabase(); 92 | final ContentResolver resolver = getContext().getContentResolver(); 93 | if (openHelper.assistedGpsSettingNeedsUpdate() && linkAssistedGps) { 94 | final boolean localSetting = 95 | (GoogleSettingsContract.Partner.getInt(resolver, "network_location_opt_in", 0) == 1); 96 | final boolean secureSetting = (Settings.Secure.getInt(resolver, "assisted_gps_enabled", 1) != 0); 97 | if (localSetting != secureSetting) { 98 | try { 99 | Settings.Secure.putInt(resolver, "assisted_gps_enabled", localSetting ? 1 : 0); 100 | } catch (Exception e) { 101 | Log.w(TAG, "Should update assisted_gps_enabled, but got exception", e); 102 | } 103 | } 104 | } 105 | final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); 106 | queryBuilder.setTables(sqlargs.table); 107 | final Cursor cursor = queryBuilder.query(db, projection, sqlargs.where, sqlargs.args, null, null, sortOrder); 108 | cursor.setNotificationUri(resolver, uri); 109 | return cursor; 110 | } 111 | 112 | private void sendNotify(final Uri uri) { 113 | // TODO Auto-generated method stub 114 | Log.w(TAG, "Not yet implemented: GoogleSettingsProvider.sendNotify"); 115 | } 116 | 117 | @Override 118 | public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { 119 | //Log.d(TAG, "update(" + uri + ", " + values + ", " + selection + ", " + selectionArgs + ")"); 120 | final SqlArguments arguments = new SqlArguments(uri, selection, selectionArgs); 121 | checkWritePermissions(arguments); 122 | final int result = 123 | openHelper.getWritableDatabase().update(arguments.table, values, arguments.where, arguments.args); 124 | if (result > 0) { 125 | sendNotify(uri); 126 | } 127 | return result; 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/settings/SqlArguments.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.settings; 2 | 3 | import android.content.ContentUris; 4 | import android.net.Uri; 5 | 6 | class SqlArguments { 7 | 8 | public String[] args; 9 | public String table; 10 | public String where; 11 | 12 | SqlArguments(final Uri uri) { 13 | if (uri.getPathSegments().size() != 1) { 14 | throw new IllegalArgumentException("Invalid URI: " + uri); 15 | } 16 | table = uri.getPathSegments().get(0); 17 | where = null; 18 | args = null; 19 | } 20 | 21 | SqlArguments(final Uri uri, final String selection, final String[] selectionArgs) { 22 | table = uri.getPathSegments().get(0); 23 | if (uri.getPathSegments().size() == 1) { 24 | where = selection; 25 | args = selectionArgs; 26 | } else { 27 | if (uri.getPathSegments().size() != 2) { 28 | throw new IllegalArgumentException("Invalid URI: " + uri); 29 | } 30 | if (selection != null && !selection.isEmpty()) { 31 | throw new UnsupportedOperationException("WHERE clause not supported: " + uri); 32 | } 33 | where = "name=?"; 34 | args = new String[]{uri.getPathSegments().get(1)}; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/com/google/android/gsf/settings/UseLocationForServicesActivity.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.settings; 2 | 3 | import android.app.Activity; 4 | import android.content.ContentResolver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import com.google.android.gsf.GoogleSettingsContract; 8 | 9 | public class UseLocationForServicesActivity extends Activity { 10 | protected void onResume() { 11 | super.onResume(); 12 | setUseLocationForServices(this, !getIntent().getBooleanExtra("disable", false)); 13 | setResult(-1); 14 | finish(); 15 | } 16 | 17 | public static boolean setUseLocationForServices(Context context, boolean enable) { 18 | ContentResolver contentresolver = context.getContentResolver(); 19 | boolean result = GoogleSettingsContract.Partner.putInt(contentresolver, "use_location_for_services", (enable ? 1 : 0)); 20 | context.sendBroadcast(new Intent("com.google.android.gsf.settings.GoogleLocationSettings.UPDATE_LOCATION_SETTINGS")); 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/subscribedfeeds/GCMIntentService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 μg Project Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.android.gsf.subscribedfeeds; 18 | 19 | import android.app.IntentService; 20 | import android.content.Intent; 21 | import android.util.Log; 22 | 23 | public class GCMIntentService extends IntentService { 24 | private static final String TAG = "GSF." + GCMIntentService.class.getSimpleName(); 25 | 26 | public GCMIntentService() { 27 | super(TAG); 28 | } 29 | 30 | @Override 31 | protected void onHandleIntent(Intent intent) { 32 | Log.d(TAG, "Incoming intent: " + intent); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/subscribedfeeds/SubscribedFeedsGCMBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 μg Project Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.android.gsf.subscribedfeeds; 18 | 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.util.Log; 23 | 24 | public class SubscribedFeedsGCMBroadcastReceiver extends BroadcastReceiver { 25 | private static final String TAG = "GSF." + SubscribedFeedsGCMBroadcastReceiver.class.getSimpleName(); 26 | 27 | @Override 28 | public void onReceive(Context context, Intent intent) { 29 | Log.d(TAG, "Incoming intent: "+intent); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/subscribedfeeds/SubscribedFeedsProvider.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.subscribedfeeds; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.database.Cursor; 6 | import android.database.MatrixCursor; 7 | import android.net.Uri; 8 | import android.util.Log; 9 | 10 | public class SubscribedFeedsProvider extends ContentProvider { 11 | 12 | private static final String TAG = "GSF." + SubscribedFeedsProvider.class.getSimpleName(); 13 | 14 | @Override 15 | public int delete(Uri uri, String selection, String[] selectionArgs) { 16 | // TODO Auto-generated method stub 17 | Log.w(TAG, "Not yet implemented: delete: " + uri); 18 | return 0; 19 | } 20 | 21 | @Override 22 | public String getType(Uri uri) { 23 | // TODO Auto-generated method stub 24 | Log.w(TAG, "Not yet implemented: getType: " + uri); 25 | return null; 26 | } 27 | 28 | @Override 29 | public Uri insert(Uri uri, ContentValues values) { 30 | // TODO Auto-generated method stub 31 | Log.w(TAG, "Not yet implemented: insert: " + uri); 32 | return null; 33 | } 34 | 35 | @Override 36 | public boolean onCreate() { 37 | // TODO Auto-generated method stub 38 | Log.w(TAG, "Not yet implemented: onCreate"); 39 | return false; 40 | } 41 | 42 | @Override 43 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 44 | // TODO Auto-generated method stub 45 | Log.w(TAG, "Not yet implemented: query: " + uri); 46 | return new MatrixCursor(projection != null ? projection : new String[0]); 47 | } 48 | 49 | @Override 50 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 51 | // TODO Auto-generated method stub 52 | Log.w(TAG, "Not yet implemented: update: " + uri); 53 | return 0; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/com/google/android/gsf/talk/TalkProvider.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gsf.talk; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentUris; 5 | import android.content.ContentValues; 6 | import android.content.UriMatcher; 7 | import android.database.Cursor; 8 | import android.database.DatabaseUtils; 9 | import android.database.sqlite.SQLiteDatabase; 10 | import android.database.sqlite.SQLiteOpenHelper; 11 | import android.database.sqlite.SQLiteQueryBuilder; 12 | import android.net.Uri; 13 | import android.util.Log; 14 | 15 | import java.io.UnsupportedEncodingException; 16 | import java.net.URLDecoder; 17 | import java.util.HashMap; 18 | 19 | public class TalkProvider extends ContentProvider { 20 | 21 | private class DatabaseHelper extends SQLiteOpenHelper { 22 | 23 | public DatabaseHelper() { 24 | super(getContext(), databaseFile, null, databaseVersion); 25 | } 26 | 27 | @Override 28 | public void onCreate(final SQLiteDatabase db) { 29 | Log.i(TAG, "SQLiteOpenHelper.onCreate"); 30 | db.beginTransaction(); 31 | db.execSQL( 32 | "CREATE TABLE accounts (_id INTEGER PRIMARY KEY,name TEXT,username TEXT,locked INTEGER NOT NULL DEFAULT 0,keep_signed_in INTEGER NOT NULL DEFAULT 0,last_login_state INTEGER NOT NULL DEFAULT 0,UNIQUE (username));"); 33 | db.execSQL( 34 | "CREATE TABLE IF NOT EXISTS contacts (_id INTEGER PRIMARY KEY,username TEXT,nickname TEXT,account INTEGER,contactList INTEGER,type INTEGER,subscriptionStatus INTEGER,subscriptionType INTEGER,qc INTEGER,rejected INTEGER,otr INTEGER, UNIQUE(account, username));"); 35 | db.execSQL( 36 | "CREATE TABLE IF NOT EXISTS contactsEtag (_id INTEGER PRIMARY KEY,etag TEXT,otr_etag TEXT,account INTEGER UNIQUE);"); 37 | db.execSQL("CREATE INDEX contactsIndex ON contacts (account,username);"); 38 | db.execSQL( 39 | "CREATE TABLE IF NOT EXISTS messages (_id INTEGER PRIMARY KEY,thread_id INTEGER,nickname TEXT,body TEXT,date INTEGER,real_date INTEGER,type INTEGER,packet_id TEXT,err_code INTEGER NOT NULL DEFAULT 0,err_msg TEXT,is_muc INTEGER,show_ts INTEGER,consolidation_key INTEGER,message_read BOOLEAN,send_status INTEGER,UNIQUE(thread_id, real_date, type));"); 40 | db.execSQL( 41 | "CREATE TABLE IF NOT EXISTS chats (_id INTEGER PRIMARY KEY AUTOINCREMENT,contact_id INTEGER UNIQUE,jid_resource TEXT,groupchat INTEGER,last_unread_message TEXT,last_message_date INTEGER,unsent_composed_message TEXT,shortcut INTEGER,local INTEGER,otherClient INTEGER,is_active BOOLEAN,account_id INTEGER);"); 42 | db.execSQL( 43 | "CREATE TRIGGER IF NOT EXISTS contact_cleanup DELETE ON contacts BEGIN DELETE FROM chats WHERE contact_id = OLD._id;DELETE FROM messages WHERE thread_id = OLD._id;END"); 44 | db.execSQL("CREATE INDEX consolidationIndex ON messages (consolidation_key);"); 45 | db.execSQL( 46 | "CREATE TABLE avatars (_id INTEGER PRIMARY KEY,contact TEXT,account_id INTEGER,hash TEXT,data BLOB,UNIQUE (account_id, contact));"); 47 | db.execSQL( 48 | "CREATE TABLE accountSettings (_id INTEGER PRIMARY KEY,name TEXT,value TEXT,account_id INTEGER,UNIQUE (name, account_id));"); 49 | db.execSQL( 50 | "CREATE TRIGGER account_cleanup DELETE ON accounts BEGIN DELETE FROM avatars WHERE account_id= OLD._id;DELETE FROM accountSettings WHERE account_id= OLD._id;DELETE FROM contacts WHERE account= OLD._id;DELETE FROM contactsEtag WHERE account= OLD._id;END"); 51 | db.execSQL( 52 | "CREATE TABLE outgoingRmqMessages (_id INTEGER PRIMARY KEY,rmq_id INTEGER,type INTEGER,ts INTEGER,data BLOB,account INTEGER,packet_id TEXT);"); 53 | db.execSQL("CREATE TABLE lastrmqid (_id INTEGER PRIMARY KEY,rmq_id INTEGER);"); 54 | db.execSQL("CREATE TABLE s2dRmqIds (_id INTEGER PRIMARY KEY,rmq_id INTEGER);"); 55 | db.setTransactionSuccessful(); 56 | db.endTransaction(); 57 | } 58 | 59 | @Override 60 | public void onOpen(final SQLiteDatabase db) { 61 | if (!db.isReadOnly()) { 62 | db.execSQL("ATTACH DATABASE ':memory:' AS talk_transient;"); 63 | db.execSQL( 64 | "CREATE TABLE IF NOT EXISTS talk_transient.inMemoryMessages (_id INTEGER PRIMARY KEY,thread_id INTEGER,nickname TEXT,body TEXT,date INTEGER,real_date INTEGER,type INTEGER,packet_id TEXT,err_code INTEGER NOT NULL DEFAULT 0,err_msg TEXT,is_muc INTEGER,show_ts INTEGER,consolidation_key INTEGER,message_read BOOLEAN,send_status INTEGER,UNIQUE(thread_id, real_date, type));"); 65 | db.execSQL( 66 | "CREATE INDEX IF NOT EXISTS talk_transient.consolidationIndex ON inMemoryMessages (consolidation_key);"); 67 | db.execSQL( 68 | "CREATE TABLE IF NOT EXISTS talk_transient.presence (_id INTEGER PRIMARY KEY,contact_id INTEGER UNIQUE,jid_resource TEXT,client_type INTEGER,cap INTEGER,priority INTEGER,mode INTEGER,status TEXT);"); 69 | db.execSQL( 70 | "CREATE TABLE IF NOT EXISTS talk_transient.invitations (_id INTEGER PRIMARY KEY,accountId INTEGER,inviteId TEXT,sender TEXT,groupName TEXT,note TEXT,status INTEGER);"); 71 | db.execSQL( 72 | "CREATE TABLE IF NOT EXISTS talk_transient.groupMembers (_id INTEGER PRIMARY KEY,groupId INTEGER,username TEXT,nickname TEXT);"); 73 | db.execSQL( 74 | "CREATE TABLE IF NOT EXISTS talk_transient.accountStatus (_id INTEGER PRIMARY KEY,account INTEGER UNIQUE,presenceStatus INTEGER,connStatus INTEGER);"); 75 | } 76 | } 77 | 78 | @Override 79 | public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { 80 | // TODO Auto-generated method stub 81 | Log.w(TAG, "Not yet implemented: SQLiteOpenHelper.onUpgrade"); 82 | throw new RuntimeException("We can't update for now!!!"); 83 | } 84 | 85 | } 86 | 87 | public static final Uri ACCOUNT_SETTINGS_URI = 88 | Uri.parse("content://com.google.android.providers.talk/accountSettings"); 89 | private static final HashMap ACCOUNT_STATUS_PROJECTION = new HashMap(); 90 | public static final Uri ACCOUNT_STATUS_URI = Uri.parse("content://com.google.android.providers.talk/accountStatus"); 91 | public static final Uri ACCOUNTS_URI = Uri.parse("content://com.google.android.providers.talk/accounts"); 92 | public static final Uri CHATS_URI = Uri.parse("content://com.google.android.providers.talk/chats"); 93 | public static final Uri CONTACTS_BAREBONE_URI = 94 | Uri.parse("content://com.google.android.providers.talk/contacts_barebone"); 95 | private static final HashMap CONTACTS_PROJECTION = new HashMap(); 96 | public static final Uri CONTACTS_URI = Uri.parse("content://com.google.android.providers.talk/contacts"); 97 | private static final HashMap IN_MEMEORY_MESSAGES_PROJECTION = new HashMap(); 98 | private static final HashMap MESSAGES_PROJECTION = new HashMap(); 99 | public static final Uri MESSAGES_URI = Uri.parse("content://com.google.android.providers.talk/messages"); 100 | public static final Uri PRESENCE_URI = Uri.parse("content://com.google.android.providers.talk/presence"); 101 | private static final String PROVIDER_NAME = "com.google.android.providers.talk"; 102 | private static final String TAG = "GoogleTalkProvider"; 103 | private static final UriMatcher URI_MATCHER = new UriMatcher(-1); 104 | 105 | static { 106 | URI_MATCHER.addURI(PROVIDER_NAME, "accounts", 10); 107 | URI_MATCHER.addURI(PROVIDER_NAME, "accounts/#", 11); 108 | URI_MATCHER.addURI(PROVIDER_NAME, "accounts/status", 12); 109 | URI_MATCHER.addURI(PROVIDER_NAME, "contacts", 20); 110 | URI_MATCHER.addURI(PROVIDER_NAME, "contacts_barebone", 21); 111 | URI_MATCHER.addURI(PROVIDER_NAME, "contacts_chatting", 22); 112 | URI_MATCHER.addURI(PROVIDER_NAME, "contacts/blocked", 23); 113 | URI_MATCHER.addURI(PROVIDER_NAME, "contacts/#", 24); 114 | URI_MATCHER.addURI(PROVIDER_NAME, "contactsEtag", 37); 115 | URI_MATCHER.addURI(PROVIDER_NAME, "contactsEtag/#", 38); 116 | URI_MATCHER.addURI(PROVIDER_NAME, "presence", 40); 117 | URI_MATCHER.addURI(PROVIDER_NAME, "presence/#", 41); 118 | URI_MATCHER.addURI(PROVIDER_NAME, "presence/account/#", 42); 119 | URI_MATCHER.addURI(PROVIDER_NAME, "messages", 50); 120 | URI_MATCHER.addURI(PROVIDER_NAME, "messagesByAcctAndContact/#/*", 51); 121 | URI_MATCHER.addURI(PROVIDER_NAME, "messagesByThreadId/#", 52); 122 | URI_MATCHER.addURI(PROVIDER_NAME, "messagesByAccount/#", 53); 123 | URI_MATCHER.addURI(PROVIDER_NAME, "messages/#", 54); 124 | URI_MATCHER.addURI(PROVIDER_NAME, "otrMessages", 55); 125 | URI_MATCHER.addURI(PROVIDER_NAME, "otrMessagesByAcctAndContact/#/*", 56); 126 | URI_MATCHER.addURI(PROVIDER_NAME, "otrMessagesByThreadId/#", 57); 127 | URI_MATCHER.addURI(PROVIDER_NAME, "otrMessagesByAccount/#", 58); 128 | URI_MATCHER.addURI(PROVIDER_NAME, "otrMessages/#", 59); 129 | URI_MATCHER.addURI(PROVIDER_NAME, "groupMembers", 65); 130 | URI_MATCHER.addURI(PROVIDER_NAME, "groupMembers/#", 66); 131 | URI_MATCHER.addURI(PROVIDER_NAME, "avatars", 70); 132 | URI_MATCHER.addURI(PROVIDER_NAME, "avatars/#", 71); 133 | URI_MATCHER.addURI(PROVIDER_NAME, "avatarsBy/#", 72); 134 | URI_MATCHER.addURI(PROVIDER_NAME, "chats", 80); 135 | URI_MATCHER.addURI(PROVIDER_NAME, "chats/account/#", 81); 136 | URI_MATCHER.addURI(PROVIDER_NAME, "chats/#", 82); 137 | URI_MATCHER.addURI(PROVIDER_NAME, "accountSettings", 90); 138 | URI_MATCHER.addURI(PROVIDER_NAME, "accountSettings/#", 91); 139 | URI_MATCHER.addURI(PROVIDER_NAME, "accountSettings/#/*", 92); 140 | URI_MATCHER.addURI(PROVIDER_NAME, "invitations", 100); 141 | URI_MATCHER.addURI(PROVIDER_NAME, "invitations/#", 101); 142 | URI_MATCHER.addURI(PROVIDER_NAME, "accountStatus", 104); 143 | URI_MATCHER.addURI(PROVIDER_NAME, "accountStatus/#", 105); 144 | URI_MATCHER.addURI(PROVIDER_NAME, "accountStatus/new_messages", 106); 145 | URI_MATCHER.addURI(PROVIDER_NAME, "search_suggest_query", 130); 146 | URI_MATCHER.addURI(PROVIDER_NAME, "search_suggest_query/*", 130); 147 | URI_MATCHER.addURI(PROVIDER_NAME, "outgoingRmqMessages", 200); 148 | URI_MATCHER.addURI(PROVIDER_NAME, "outgoingRmqMessages/#", 201); 149 | URI_MATCHER.addURI(PROVIDER_NAME, "outgoingHighestRmqId", 202); 150 | URI_MATCHER.addURI(PROVIDER_NAME, "lastRmqId", 203); 151 | URI_MATCHER.addURI(PROVIDER_NAME, "s2dids", 204); 152 | ACCOUNT_STATUS_PROJECTION.put("_id", "accounts._id AS _id"); 153 | ACCOUNT_STATUS_PROJECTION.put("username", "accounts.username AS username"); 154 | ACCOUNT_STATUS_PROJECTION.put("account_connStatus", "accountStatus.connStatus AS account_connStatus"); 155 | CONTACTS_PROJECTION.put("_id", "contacts._id AS _id"); 156 | CONTACTS_PROJECTION.put("_count", "COUNT(*) AS _count"); 157 | CONTACTS_PROJECTION.put("_id", "contacts._id as _id"); 158 | CONTACTS_PROJECTION.put("username", "contacts.username as username"); 159 | CONTACTS_PROJECTION.put("nickname", "contacts.nickname as nickname"); 160 | CONTACTS_PROJECTION.put("account", "contacts.account as account"); 161 | CONTACTS_PROJECTION.put("contactList", "contacts.contactList as contactList"); 162 | CONTACTS_PROJECTION.put("type", "contacts.type as type"); 163 | CONTACTS_PROJECTION.put("subscriptionStatus", "contacts.subscriptionStatus as subscriptionStatus"); 164 | CONTACTS_PROJECTION.put("subscriptionType", "contacts.subscriptionType as subscriptionType"); 165 | CONTACTS_PROJECTION.put("qc", "contacts.qc as qc"); 166 | CONTACTS_PROJECTION.put("rejected", "contacts.rejected as rejected"); 167 | CONTACTS_PROJECTION.put("contact_id", "presence.contact_id AS contact_id"); 168 | CONTACTS_PROJECTION.put("mode", "presence.mode AS mode"); 169 | CONTACTS_PROJECTION.put("status", "presence.status AS status"); 170 | CONTACTS_PROJECTION.put("client_type", "presence.client_type AS client_type"); 171 | CONTACTS_PROJECTION.put("cap", "presence.cap AS cap"); 172 | CONTACTS_PROJECTION.put("chats_contact", "chats.contact_id AS chats_contact_id"); 173 | CONTACTS_PROJECTION.put("jid_resource", "chats.jid_resource AS jid_resource"); 174 | CONTACTS_PROJECTION.put("groupchat", "chats.groupchat AS groupchat"); 175 | CONTACTS_PROJECTION.put("last_unread_message", "chats.last_unread_message AS last_unread_message"); 176 | CONTACTS_PROJECTION.put("last_message_date", "chats.last_message_date AS last_message_date"); 177 | CONTACTS_PROJECTION.put("unsent_composed_message", "chats.unsent_composed_message AS unsent_composed_message"); 178 | CONTACTS_PROJECTION.put("shortcut", "chats.SHORTCUT AS shortcut"); 179 | CONTACTS_PROJECTION.put("is_active", "chats.IS_ACTIVE AS IS_ACTIVE"); 180 | CONTACTS_PROJECTION.put("avatars_hash", "avatars.hash AS avatars_hash"); 181 | CONTACTS_PROJECTION.put("avatars_data", "avatars.data AS avatars_data"); 182 | MESSAGES_PROJECTION.put("_id", "messages._id AS _id"); 183 | MESSAGES_PROJECTION.put("_count", "COUNT(*) AS _count"); 184 | MESSAGES_PROJECTION.put("thread_id", "messages.thread_id AS thread_id"); 185 | MESSAGES_PROJECTION.put("packet_id", "messages.packet_id AS packet_id"); 186 | MESSAGES_PROJECTION.put("nickname", "messages.nickname AS nickname"); 187 | MESSAGES_PROJECTION.put("body", "messages.body AS body"); 188 | MESSAGES_PROJECTION.put("date", "messages.date AS date"); 189 | MESSAGES_PROJECTION.put("type", "messages.type AS type"); 190 | MESSAGES_PROJECTION.put("msg_type", "messages.type AS msg_type"); 191 | MESSAGES_PROJECTION.put("send_status", "messages.send_status AS send_status"); 192 | MESSAGES_PROJECTION.put("err_code", "messages.err_code AS err_code"); 193 | MESSAGES_PROJECTION.put("err_msg", "messages.err_msg AS err_msg"); 194 | MESSAGES_PROJECTION.put("is_muc", "messages.is_muc AS is_muc"); 195 | MESSAGES_PROJECTION.put("show_ts", "messages.show_ts AS show_ts"); 196 | MESSAGES_PROJECTION.put("contact", "contacts.username AS contact"); 197 | MESSAGES_PROJECTION.put("account", "contacts.account AS account"); 198 | MESSAGES_PROJECTION.put("contact_type", "contacts.type AS contact_type"); 199 | MESSAGES_PROJECTION.put("consolidation_key", "messages.consolidation_key as consolidation_key"); 200 | IN_MEMEORY_MESSAGES_PROJECTION.put("_id", "inMemoryMessages._id AS _id"); 201 | IN_MEMEORY_MESSAGES_PROJECTION.put("_count", "COUNT(*) AS _count"); 202 | IN_MEMEORY_MESSAGES_PROJECTION.put("thread_id", "inMemoryMessages.thread_id AS thread_id"); 203 | IN_MEMEORY_MESSAGES_PROJECTION.put("packet_id", "inMemoryMessages.packet_id AS packet_id"); 204 | IN_MEMEORY_MESSAGES_PROJECTION.put("nickname", "inMemoryMessages.nickname AS nickname"); 205 | IN_MEMEORY_MESSAGES_PROJECTION.put("body", "inMemoryMessages.body AS body"); 206 | IN_MEMEORY_MESSAGES_PROJECTION.put("date", "inMemoryMessages.date AS date"); 207 | IN_MEMEORY_MESSAGES_PROJECTION.put("type", "inMemoryMessages.type AS type"); 208 | IN_MEMEORY_MESSAGES_PROJECTION.put("msg_type", "inMemoryMessages.type AS msg_type"); 209 | IN_MEMEORY_MESSAGES_PROJECTION.put("send_status", "inMemoryMessages.send_status AS send_status"); 210 | IN_MEMEORY_MESSAGES_PROJECTION.put("err_code", "inMemoryMessages.err_code AS err_code"); 211 | IN_MEMEORY_MESSAGES_PROJECTION.put("err_msg", "inMemoryMessages.err_msg AS err_msg"); 212 | IN_MEMEORY_MESSAGES_PROJECTION.put("is_muc", "inMemoryMessages.is_muc AS is_muc"); 213 | IN_MEMEORY_MESSAGES_PROJECTION.put("show_ts", "inMemoryMessages.show_ts AS show_ts"); 214 | IN_MEMEORY_MESSAGES_PROJECTION.put("contact", "contacts.username AS contact"); 215 | IN_MEMEORY_MESSAGES_PROJECTION.put("account", "contacts.account AS account"); 216 | IN_MEMEORY_MESSAGES_PROJECTION.put("contact_type", "contacts.type AS contact_type"); 217 | IN_MEMEORY_MESSAGES_PROJECTION 218 | .put("consolidation_key", "inMemoryMessages.consolidation_key as consolidation_key"); 219 | } 220 | 221 | private static String arrayToString(final String[] array) { 222 | if (array == null) { 223 | return "null"; 224 | } 225 | final StringBuilder sb = new StringBuilder("["); 226 | boolean first = true; 227 | for (final String string : array) { 228 | if (!first) { 229 | sb.append(", "); 230 | } 231 | sb.append(string); 232 | first = false; 233 | } 234 | return sb.append("]").toString(); 235 | } 236 | 237 | private final String databaseFile = "talk.db"; 238 | private DatabaseHelper databaseHelper; 239 | private final int databaseVersion = 68; 240 | 241 | private void appendAccountIdFromUri(final Uri uri, final StringBuilder whereClause) { 242 | appendIdFromUri(uri, "account_id", whereClause); 243 | } 244 | 245 | private void appendAndIfNeeded(final StringBuilder whereClause) { 246 | if (whereClause.length() != 0) { 247 | whereClause.append(" AND "); 248 | } 249 | } 250 | 251 | private void appendIdFromUri(final Uri uri, final String idName, final StringBuilder whereClause) { 252 | appendAndIfNeeded(whereClause); 253 | whereClause.append(idName).append("="); 254 | DatabaseUtils.appendValueToSql(whereClause, uri.getPathSegments().get(1)); 255 | } 256 | 257 | private void appendIdFromUri(final Uri uri, final StringBuilder whereClause) { 258 | appendIdFromUri(uri, "_id", whereClause); 259 | } 260 | 261 | @Override 262 | public int delete(final Uri uri, final String selection, final String[] selectionArgs) { 263 | // TODO Auto-generated method stub 264 | final int match = URI_MATCHER.match(uri); 265 | final SQLiteDatabase db = databaseHelper.getWritableDatabase(); 266 | switch (match) { 267 | case 50: 268 | return db.delete("messages", selection, selectionArgs); 269 | default: 270 | Log.w(TAG, 271 | "Not yet implemented: TalkProvider.delete (" + uri + ", " + selection + ", " + selectionArgs + 272 | ")"); 273 | } 274 | return 0; 275 | } 276 | 277 | @Override 278 | public String getType(final Uri uri) { 279 | final int match = URI_MATCHER.match(uri); 280 | switch (match) { 281 | case 82: 282 | return "vnd.android.cursor.item/gtalk-chats"; 283 | default: 284 | Log.w(TAG, "Not yet implemented: TalkProvider.getType (" + uri + ")"); 285 | return null; 286 | } 287 | } 288 | 289 | /** 290 | * Get uniques of a table in the database 291 | * 292 | * @param table the table of which uniques should be returned 293 | * @return array of uniques in the given table or null if unknown 294 | */ 295 | private String[] getUniques(final String table) { 296 | String[] uniques = null; 297 | if (table.equalsIgnoreCase("contacts")) { 298 | uniques = new String[]{"account", "username"}; 299 | } else if (table.equalsIgnoreCase("accounts")) { 300 | uniques = new String[]{"username"}; 301 | } 302 | return uniques; 303 | } 304 | 305 | private long insert(final String table, final String nullColumnHack, final ContentValues values) { 306 | final SQLiteDatabase db = databaseHelper.getWritableDatabase(); 307 | return db.insert(table, nullColumnHack, values); 308 | } 309 | 310 | @Override 311 | public Uri insert(final Uri uri, final ContentValues values) { 312 | final int match = URI_MATCHER.match(uri); 313 | Uri result = uri; 314 | switch (match) { 315 | case 10: 316 | result = ContentUris.withAppendedId(ACCOUNTS_URI, replace("accounts", null, values)); 317 | break; 318 | case 20: 319 | result = ContentUris.withAppendedId(CONTACTS_URI, replace("contacts", null, values)); 320 | break; 321 | case 40: 322 | result = ContentUris.withAppendedId(PRESENCE_URI, replace("talk_transient.presence", null, values)); 323 | break; 324 | case 50: 325 | result = ContentUris.withAppendedId(MESSAGES_URI, insert("messages", null, values)); 326 | break; 327 | case 80: 328 | result = ContentUris.withAppendedId(CHATS_URI, replace("chats", null, values)); 329 | break; 330 | case 90: 331 | result = ContentUris.withAppendedId(ACCOUNT_SETTINGS_URI, replace("accountSettings", null, values)); 332 | break; 333 | case 104: 334 | result = ContentUris 335 | .withAppendedId(ACCOUNT_STATUS_URI, replace("talk_transient.accountStatus", null, values)); 336 | break; 337 | default: 338 | Log.w(TAG, "Not yet implemented: TalkProvider.insert (" + uri + ", " + values + ")"); 339 | result = null; 340 | break; 341 | } 342 | Log.w(TAG, "TalkProvider.insert (" + uri + ", " + values + ") = " + result); 343 | return result; 344 | } 345 | 346 | @Override 347 | public boolean onCreate() { 348 | databaseHelper = new DatabaseHelper(); 349 | return true; 350 | } 351 | 352 | @Override 353 | public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, 354 | final String sortOrder) { 355 | 356 | final int match = URI_MATCHER.match(uri); 357 | final SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); 358 | final StringBuilder whereClause = new StringBuilder(); 359 | if (selection != null) { 360 | whereClause.append(selection); 361 | } 362 | final String limit = null; 363 | final SQLiteDatabase db = databaseHelper.getReadableDatabase(); 364 | switch (match) { 365 | case 10: 366 | builder.setTables("accounts"); 367 | break; 368 | case 11: 369 | builder.setTables("accounts"); 370 | appendIdFromUri(uri, whereClause); 371 | break; 372 | case 12: 373 | builder.setTables("accounts LEFT OUTER JOIN accountStatus ON (accounts._id = accountStatus.account)"); 374 | builder.setProjectionMap(ACCOUNT_STATUS_PROJECTION); 375 | break; 376 | case 20: 377 | builder.setTables( 378 | "contacts LEFT OUTER JOIN presence ON (contacts._id = presence.contact_id) LEFT OUTER JOIN chats ON (contacts._id = chats.contact_id) LEFT OUTER JOIN avatars ON (contacts.username = avatars.contact AND contacts.account = avatars.account_id)"); 379 | builder.setProjectionMap(CONTACTS_PROJECTION); 380 | break; 381 | case 21: 382 | builder.setTables("contacts"); 383 | break; 384 | case 22: 385 | builder.setTables( 386 | "chats LEFT OUTER JOIN contacts ON (contacts._id = chats.contact_id) LEFT OUTER JOIN presence ON (contacts._id = presence.contact_id) LEFT OUTER JOIN avatars ON (contacts.username = avatars.contact AND contacts.account = avatars.account_id)"); 387 | builder.setProjectionMap(CONTACTS_PROJECTION); 388 | /* 389 | * if (whereClause.length() != 0) { whereClause.append(" AND "); } 390 | * whereClause.append("chats.last_message_date IS NOT NULL"); 391 | */ 392 | break; 393 | case 40: 394 | builder.setTables("presence"); 395 | break; 396 | case 51: 397 | builder.setTables("messages LEFT OUTER JOIN contacts ON (contacts._id = messages.thread_id)"); 398 | appendIdFromUri(uri, "account", whereClause); 399 | whereClause.append(" AND contacts.username LIKE "); 400 | try { 401 | DatabaseUtils 402 | .appendValueToSql(whereClause, URLDecoder.decode(uri.getPathSegments().get(2), "UTF-8")); 403 | } catch (final UnsupportedEncodingException e1) { 404 | Log.w(TAG, e1); 405 | whereClause.append("\"%\""); 406 | } 407 | builder.setProjectionMap(MESSAGES_PROJECTION); 408 | break; 409 | case 72: 410 | builder.setTables("avatars"); 411 | appendAccountIdFromUri(uri, whereClause); 412 | break; 413 | case 80: 414 | builder.setTables("chats"); 415 | break; 416 | case 82: 417 | builder.setTables("messages"); 418 | appendIdFromUri(uri, "thread_id", whereClause); 419 | break; 420 | case 90: 421 | builder.setTables("accountSettings"); 422 | break; 423 | case 91: 424 | builder.setTables("accountSettings"); 425 | appendAccountIdFromUri(uri, whereClause); 426 | break; 427 | case 92: 428 | builder.setTables("accountSettings"); 429 | appendAccountIdFromUri(uri, whereClause); 430 | whereClause.append(" AND name LIKE "); 431 | DatabaseUtils.appendValueToSql(whereClause, uri.getPathSegments().get(2)); 432 | break; 433 | case 106: 434 | return db.rawQuery( 435 | "select contacts.account, count(last_unread_message) as active_count from chats,contacts where chats.contact_id = contacts._id AND chats.is_active = 1 group by contacts.account order by contacts.account;", 436 | null); 437 | default: 438 | Log.w(TAG, "Not yet implemented: TalkProvider.query (" + uri + ", " + arrayToString(projection) + ", " + 439 | selection + ", " + arrayToString(selectionArgs) + ", " + sortOrder + ")"); 440 | return null; 441 | } 442 | try { 443 | final Cursor cursor = 444 | builder.query(db, projection, whereClause.toString(), selectionArgs, null, null, sortOrder, limit); 445 | if (cursor != null) { 446 | cursor.setNotificationUri(getContext().getContentResolver(), uri); 447 | if (cursor.getCount() == 0) { 448 | Log.w(TAG, 449 | "Empty Result for: TalkProvider.query (" + uri + ", " + arrayToString(projection) + ", " + 450 | selection + ", " + arrayToString(selectionArgs) + ", " + sortOrder + ")"); 451 | } 452 | return cursor; 453 | } 454 | } catch (final Exception e) { 455 | Log.e(TAG, "error while db query for " + uri, e); 456 | } 457 | 458 | return null; 459 | } 460 | 461 | /** 462 | * Convenience method for replacing a row in the database. Holds id on 463 | * replace if possible (using getUniques) 464 | * 465 | * @param table the table in which to replace the row 466 | * @param nullColumnHack optional; may be null. SQL doesn't allow inserting a 467 | * completely empty row without naming at least one column name. 468 | * If your provided initialValues is empty, no column names are 469 | * known and an empty row can't be inserted. If not set to null, 470 | * the nullColumnHack parameter provides the name of nullable 471 | * column name to explicitly insert a NULL into in the case where 472 | * your initialValues is empty. 473 | * @param values this map contains the column values for the row. 474 | * @return the row ID of the newly inserted row, or -1 if an error occurred 475 | */ 476 | private long replace(final String table, final String nullColumnHack, final ContentValues values) { 477 | final String[] uniques = getUniques(table); 478 | return replace(table, nullColumnHack, values, uniques); 479 | } 480 | 481 | /** 482 | * Convenience method for replacing a row in the database. Holds id on 483 | * replace if entry with uniques is available 484 | * 485 | * @param table the table in which to replace the row 486 | * @param nullColumnHack optional; may be null. SQL doesn't allow inserting a 487 | * completely empty row without naming at least one column name. 488 | * If your provided initialValues is empty, no column names are 489 | * known and an empty row can't be inserted. If not set to null, 490 | * the nullColumnHack parameter provides the name of nullable 491 | * column name to explicitly insert a NULL into in the case where 492 | * your initialValues is empty. 493 | * @param values this map contains the column values for the row. 494 | * @param uniques array describing the uniques of a table, if null or empty new 495 | * id is created 496 | * @return the row ID of the newly inserted row, or -1 if an error occurred 497 | */ 498 | private long replace(final String table, final String nullColumnHack, final ContentValues values, 499 | final String... uniques) { 500 | final SQLiteDatabase db = databaseHelper.getWritableDatabase(); 501 | if (uniques == null || uniques.length == 0) { 502 | return db.replace(table, nullColumnHack, values); 503 | } 504 | final StringBuilder whereClause = new StringBuilder(); 505 | final String[] selectionArgs = new String[uniques.length]; 506 | for (int i = 0; i < uniques.length; i++) { 507 | final String unique = uniques[i]; 508 | appendAndIfNeeded(whereClause); 509 | whereClause.append(unique).append("=?"); 510 | selectionArgs[i] = values.getAsString(unique); 511 | } 512 | final Cursor c = db.query(table, new String[]{"_id"}, whereClause.toString(), selectionArgs, null, null, null); 513 | if (c.moveToFirst()) { 514 | final long id = c.getLong(0); 515 | c.close(); 516 | db.update(table, values, "_id=?", new String[]{id + ""}); 517 | return id; 518 | } else { 519 | c.close(); 520 | return db.replace(table, nullColumnHack, values); 521 | } 522 | } 523 | 524 | @Override 525 | public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { 526 | final int match = URI_MATCHER.match(uri); 527 | final SQLiteDatabase db = databaseHelper.getWritableDatabase(); 528 | String database = null; 529 | final StringBuilder whereClause = new StringBuilder(selection); 530 | switch (match) { 531 | case 20: 532 | database = "contacts"; 533 | break; 534 | case 21: 535 | database = "contacts"; 536 | appendIdFromUri(uri, whereClause); 537 | break; 538 | case 40: 539 | database = "talk_transient.presence"; 540 | break; 541 | case 41: 542 | database = "talk_transient.presence"; 543 | appendIdFromUri(uri, whereClause); 544 | break; 545 | case 50: 546 | database = "messages"; 547 | break; 548 | case 80: 549 | database = "chats"; 550 | break; 551 | case 81: 552 | database = "chats"; 553 | appendIdFromUri(uri, whereClause); 554 | break; 555 | case 90: 556 | database = "accountSettings"; 557 | break; 558 | case 91: 559 | database = "accountSettings"; 560 | appendIdFromUri(uri, whereClause); 561 | case 104: 562 | database = "talk_transient.accountStatus"; 563 | break; 564 | case 105: 565 | database = "talk_transient.accountStatus"; 566 | appendIdFromUri(uri, whereClause); 567 | break; 568 | default: 569 | Log.w(TAG, 570 | "Not yet implemented: TalkProvider.update (" + uri + ", " + values + ", " + selection + ", " + 571 | arrayToString(selectionArgs) + ")"); 572 | } 573 | if (database != null) { 574 | return db.update(database, values, whereClause.toString(), selectionArgs); 575 | } 576 | return 0; 577 | } 578 | 579 | } 580 | -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/ConnectionError.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | parcelable ConnectionError; -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/ConnectionError.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class ConnectionError implements Parcelable { 7 | 8 | public static final int AUTH_EXPIRED = 5; 9 | public static final int AUTH_FAILED = 4; 10 | public static final int CONNECTION_FAILED = 2; 11 | public static final Parcelable.Creator CREATOR = new Creator() { 12 | 13 | @Override 14 | public ConnectionError createFromParcel(final Parcel parcel) { 15 | return new ConnectionError(parcel); 16 | } 17 | 18 | @Override 19 | public ConnectionError[] newArray(final int num) { 20 | return new ConnectionError[num]; 21 | } 22 | }; 23 | public static final int HEARTBEAT_TIMEOUT = 6; 24 | public static final int NO_ERROR = 0; 25 | public static final int NO_NETWORK = 1; 26 | public static final int SERVER_FAILED = 7; 27 | public static final int SERVER_REJECTED = 8; 28 | public static final int UNKNOWN = 9; 29 | 30 | public static final int UNKNOWN_HOST = 3; 31 | 32 | public static boolean isAuthenticationError(final int errorCode) { 33 | return (errorCode == AUTH_FAILED); 34 | } 35 | 36 | public static String toString(final int error) { 37 | switch (error) { 38 | case NO_ERROR: 39 | return "NO ERROR"; 40 | case NO_NETWORK: 41 | return "NO NETWORK"; 42 | case CONNECTION_FAILED: 43 | return "CONNECTION FAILED"; 44 | case UNKNOWN_HOST: 45 | return "UNKNOWN HOST"; 46 | case AUTH_FAILED: 47 | return "AUTH FAILED"; 48 | case AUTH_EXPIRED: 49 | return "AUTH EXPIRED"; 50 | case HEARTBEAT_TIMEOUT: 51 | return "HEARTBEAT TIMEOUT"; 52 | case SERVER_FAILED: 53 | return "SERVER FAILED"; 54 | case SERVER_REJECTED: 55 | return "SERVER REJECT - RATE LIMIT"; 56 | case UNKNOWN: 57 | default: 58 | return "UNKNOWN"; 59 | } 60 | } 61 | 62 | private int errorCode; 63 | 64 | public ConnectionError(final int error) { 65 | setError(error); 66 | } 67 | 68 | public ConnectionError(final Parcel parcel) { 69 | errorCode = parcel.readInt(); 70 | } 71 | 72 | @Override 73 | public int describeContents() { 74 | return 0; 75 | } 76 | 77 | public int getError() { 78 | return errorCode; 79 | } 80 | 81 | public void setError(final int errorCode) { 82 | this.errorCode = errorCode; 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return toString(errorCode); 88 | } 89 | 90 | @Override 91 | public void writeToParcel(final Parcel parcel, final int flags) { 92 | parcel.writeInt(errorCode); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/ConnectionState.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | parcelable ConnectionState; -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/ConnectionState.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class ConnectionState implements Parcelable { 7 | 8 | public static final int AUTHENTICATED = 3; 9 | public static final int CONNECTING = 2; 10 | public static final Parcelable.Creator CREATOR = new Creator() { 11 | 12 | @Override 13 | public ConnectionState createFromParcel(final Parcel parcel) { 14 | return new ConnectionState(parcel); 15 | } 16 | 17 | @Override 18 | public ConnectionState[] newArray(final int num) { 19 | return new ConnectionState[num]; 20 | } 21 | }; 22 | public static final int IDLE = 0; 23 | public static final int ONLINE = 4; 24 | 25 | public static final int RECONNECTION_SCHEDULED = 1; 26 | 27 | public static String toString(final int state) { 28 | switch (state) { 29 | case IDLE: 30 | return "IDLE"; 31 | case RECONNECTION_SCHEDULED: 32 | return "RECONNECTION_SCHEDULED"; 33 | case CONNECTING: 34 | return "CONNECTING"; 35 | case AUTHENTICATED: 36 | return "AUTHENTICATED"; 37 | case ONLINE: 38 | default: 39 | return "ONLINE"; 40 | } 41 | } 42 | 43 | private int stateCode; 44 | 45 | public ConnectionState(final int state) { 46 | setState(state); 47 | } 48 | 49 | public ConnectionState(final Parcel parcel) { 50 | stateCode = parcel.readInt(); 51 | } 52 | 53 | @Override 54 | public int describeContents() { 55 | return 0; 56 | } 57 | 58 | public int getState() { 59 | return stateCode; 60 | } 61 | 62 | public boolean isLoggedIn() { 63 | return (stateCode >= AUTHENTICATED); 64 | } 65 | 66 | public boolean isOnline() { 67 | return (stateCode == ONLINE); 68 | } 69 | 70 | public boolean isPendingReconnect() { 71 | return (stateCode == RECONNECTION_SCHEDULED); 72 | } 73 | 74 | public void setState(final int stateCode) { 75 | this.stateCode = stateCode; 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return toString(stateCode); 81 | } 82 | 83 | @Override 84 | public void writeToParcel(final Parcel parcel, final int flags) { 85 | parcel.writeInt(stateCode); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/GroupChatInvitation.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | parcelable GroupChatInvitation; -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/GroupChatInvitation.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class GroupChatInvitation implements Parcelable { 7 | 8 | public static final Parcelable.Creator CREATOR = new Creator() { 9 | 10 | @Override 11 | public GroupChatInvitation createFromParcel(final Parcel parcel) { 12 | return new GroupChatInvitation(parcel); 13 | } 14 | 15 | @Override 16 | public GroupChatInvitation[] newArray(final int num) { 17 | return new GroupChatInvitation[num]; 18 | } 19 | }; 20 | private final long groupContactId; 21 | private final String inviter; 22 | private final String password; 23 | private final String reason; 24 | 25 | private final String roomAddress; 26 | 27 | public GroupChatInvitation(final Parcel parcel) { 28 | roomAddress = parcel.readString(); 29 | inviter = parcel.readString(); 30 | reason = parcel.readString(); 31 | password = parcel.readString(); 32 | groupContactId = parcel.readLong(); 33 | } 34 | 35 | public GroupChatInvitation(final String roomAddress, final String inviter, final String reason, 36 | final String password, final long groupContactId) { 37 | this.roomAddress = roomAddress; 38 | this.inviter = inviter; 39 | this.reason = reason; 40 | this.password = password; 41 | this.groupContactId = groupContactId; 42 | } 43 | 44 | @Override 45 | public int describeContents() { 46 | return 0; 47 | } 48 | 49 | public long getGroupContactId() { 50 | return groupContactId; 51 | } 52 | 53 | public String getInviter() { 54 | return inviter; 55 | } 56 | 57 | public String getPassword() { 58 | return password; 59 | } 60 | 61 | public String getReason() { 62 | return reason; 63 | } 64 | 65 | public String getRoomAddress() { 66 | return roomAddress; 67 | } 68 | 69 | @Override 70 | public void writeToParcel(final Parcel parcel, final int flags) { 71 | parcel.writeString(roomAddress); 72 | parcel.writeString(inviter); 73 | parcel.writeString(reason); 74 | parcel.writeString(password); 75 | parcel.writeLong(groupContactId); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IChatListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | interface IChatListener { 4 | void newMessageReceived(String from, String body, boolean notify); 5 | void newMessageSent(String body); 6 | void missedCall(); 7 | void callEnded(); 8 | void chatRead(String from); 9 | void chatClosed(String from); 10 | void willConvertToGroupChat(String oldJid, String groupChatRoom, long groupId); 11 | void convertedToGroupChat(String oldJid, String groupChatRoom, long groupId); 12 | void participantJoined(String room, String nickname); 13 | void participantLeft(String room, String nickname); 14 | boolean useLightweightNotify(); 15 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IChatSession.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.IChatListener; 4 | import android.net.Uri; 5 | 6 | interface IChatSession { 7 | boolean isGroupChat(); 8 | void markAsRead(); 9 | String[] getParticipants(); 10 | void inviteContact(String contact); 11 | void leave(); 12 | void sendChatMessage(String message); 13 | void saveUnsentComposedMessage(String message); 14 | String getUnsentComposedMessage(); 15 | void addRemoteChatListener(IChatListener listener); 16 | void removeRemoteChatListener(IChatListener listener); 17 | boolean isOffTheRecord(); 18 | boolean getLightweightNotify(); 19 | void reportEndCause(String s, boolean flag, int i); 20 | void reportMissedCall(String s, String s1, boolean flag, boolean flag1); 21 | void ensureNonZeroLastMessageDate(); 22 | void clearChatHistory(in Uri uri); 23 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IConnectionStateListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.ConnectionState; 4 | import com.google.android.gtalkservice.ConnectionError; 5 | 6 | interface IConnectionStateListener { 7 | void connectionStateChanged(in ConnectionState connectionstate, in ConnectionError connectionerror, long l, String s); 8 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IGTalkConnection.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.IHttpRequestCallback; 4 | import com.google.android.gtalkservice.IImSession; 5 | 6 | interface IGTalkConnection{ 7 | String getUsername(); 8 | String getJid(); 9 | String getDeviceId(); 10 | boolean isConnected(); 11 | IImSession createImSession(); 12 | IImSession getImSessionForAccountId(long l); 13 | IImSession getDefaultImSession(); 14 | long getLastActivityFromServerTime(); 15 | long getLastActivityToServerTime(); 16 | int getNumberOfConnectionsMade(); 17 | int getNumberOfConnectionsAttempted(); 18 | int getConnectionUptime(); 19 | void clearConnectionStatistics(); 20 | void sendHttpRequest(in byte[] bytes, IHttpRequestCallback httprequestcallback); 21 | void sendHeartbeat(); 22 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IGTalkConnectionListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.IGTalkConnection; 4 | 5 | interface IGTalkConnectionListener { 6 | void onConnectionCreated(IGTalkConnection igtalkconnection); 7 | void onConnectionCreationFailed(String s); 8 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IGTalkService.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.IGTalkConnectionListener; 4 | import com.google.android.gtalkservice.IGTalkConnection; 5 | import com.google.android.gtalkservice.IImSession; 6 | 7 | interface IGTalkService { 8 | void createGTalkConnection(String s, IGTalkConnectionListener igtalkconnectionlistener); 9 | List getActiveConnections(); 10 | IGTalkConnection getConnectionForUser(String s); 11 | IGTalkConnection getDefaultConnection(); 12 | IImSession getImSessionForAccountId(long l); 13 | void dismissAllNotifications(); 14 | void dismissNotificationsForAccount(long l); 15 | void dismissNotificationFor(String s, long l); 16 | boolean getDeviceStorageLow(); 17 | String printDiagnostics(); 18 | void setTalkForegroundState(); 19 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IGroupChatInvitationListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.GroupChatInvitation; 4 | 5 | interface IGroupChatInvitationListener { 6 | boolean onInvitationReceived(in GroupChatInvitation groupchatinvitation); 7 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IHttpRequestCallback.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | interface IHttpRequestCallback { 4 | void requestComplete(in byte[] bytes); 5 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IImSession.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import com.google.android.gtalkservice.IConnectionStateListener; 4 | import com.google.android.gtalkservice.IGroupChatInvitationListener; 5 | import com.google.android.gtalkservice.IChatListener; 6 | import com.google.android.gtalkservice.IJingleInfoStanzaListener; 7 | import com.google.android.gtalkservice.IRosterListener; 8 | import com.google.android.gtalkservice.ISessionStanzaListener; 9 | import com.google.android.gtalkservice.IChatSession; 10 | import com.google.android.gtalkservice.ConnectionState; 11 | import com.google.android.gtalkservice.Presence; 12 | import android.graphics.Bitmap; 13 | 14 | 15 | interface IImSession { 16 | long getAccountId(); 17 | String getUsername(); 18 | String getJid(); 19 | void login(String s, boolean flag); 20 | void logout(); 21 | ConnectionState getConnectionState(); 22 | void addConnectionStateListener(IConnectionStateListener iconnectionstatelistener); 23 | void removeConnectionStateListener(IConnectionStateListener iconnectionstatelistener); 24 | void setPresence(in Presence presence); 25 | Presence getPresence(); 26 | void uploadAvatar(in Bitmap bitmap); 27 | void uploadAvatarFromDb(); 28 | void addContact(String s, String s1, in String[] as); 29 | void editContact(String s, String s1, in String[] as); 30 | void removeContact(String s); 31 | void blockContact(String s); 32 | void pinContact(String s); 33 | void hideContact(String s); 34 | void clearContactFlags(String s); 35 | void approveSubscriptionRequest(String s, String s1, in String[] as); 36 | void declineSubscriptionRequest(String s); 37 | IChatSession createChatSession(String s); 38 | IChatSession getChatSession(String s); 39 | void createGroupChatSession(String s, in String[] as); 40 | void joinGroupChatSession(String s, String s1, String s2); 41 | void declineGroupChatInvitation(String s, String s1); 42 | void addGroupChatInvitationListener(IGroupChatInvitationListener igroupchatinvitationlistener); 43 | void removeGroupChatInvitationListener(IGroupChatInvitationListener igroupchatinvitationlistener); 44 | void addRemoteChatListener(IChatListener ichatlistener); 45 | void removeRemoteChatListener(IChatListener ichatlistener); 46 | void addRemoteRosterListener(IRosterListener irosterlistener); 47 | void removeRemoteRosterListener(IRosterListener irosterlistener); 48 | void goOffRecordWithContacts(in List list, boolean flag); 49 | void goOffRecordInRoom(String s, boolean flag); 50 | boolean isOffRecordWithContact(String contact); 51 | void closeAllChatSessions(); 52 | void pruneOldChatSessions(long l, long l1, long l2, boolean flag); 53 | void sendSessionStanza(String s); 54 | void addRemoteSessionStanzaListener(ISessionStanzaListener isessionstanzalistener); 55 | void removeRemoteSessionStanzaListener(ISessionStanzaListener isessionstanzalistener); 56 | void queryJingleInfo(); 57 | void addRemoteJingleInfoStanzaListener(IJingleInfoStanzaListener ijingleinfostanzalistener); 58 | void removeRemoteJingleInfoStanzaListener(IJingleInfoStanzaListener ijingleinfostanzalistener); 59 | void requestBatchedBuddyPresence(); 60 | void sendCallPerfStatsStanza(String s); 61 | void inviteContactsToGroupchat(String groupChat, in String[] contacts); 62 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IJingleInfoStanzaListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | interface IJingleInfoStanzaListener { 4 | void onStanzaReceived(String s); 5 | long getAccountId(); 6 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/IRosterListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | interface IRosterListener { 4 | void rosterChanged(); 5 | void presenceChanged(String s); 6 | void selfPresenceChanged(); 7 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/ISessionStanzaListener.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | interface ISessionStanzaListener { 4 | void onStanzaReceived(String s, String s1); 5 | void onStanzaResponse(String s, String s1, String s2); 6 | long getAccountId(); 7 | } -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/Presence.aidl: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | parcelable Presence; -------------------------------------------------------------------------------- /src/com/google/android/gtalkservice/Presence.java: -------------------------------------------------------------------------------- 1 | package com.google.android.gtalkservice; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class Presence implements Parcelable { 10 | 11 | public static enum Show { 12 | NONE, AWAY, EXTENDED_AWAY, DND, AVAILABLE; 13 | } 14 | 15 | public static final Parcelable.Creator CREATOR = new Creator() { 16 | 17 | @Override 18 | public Presence createFromParcel(final Parcel parcel) { 19 | return new Presence(parcel); 20 | } 21 | 22 | @Override 23 | public Presence[] newArray(final int num) { 24 | return new Presence[num]; 25 | } 26 | }; 27 | public static final Presence OFFLINE = new Presence(); 28 | private boolean allowInvisibility; 29 | private boolean available; 30 | private int capabilities; 31 | private final List defaultStatusList; 32 | private final List dndStatusList; 33 | private boolean invisible; 34 | private Show show; 35 | private String status; 36 | private int statusListContentsMax; 37 | private int statusListMax; 38 | private int statusMax; 39 | 40 | public int getNumeric() { 41 | if (!isAvailable()) { 42 | return 0; 43 | } 44 | if (isInvisible()) { 45 | return 1; 46 | } 47 | switch (show) { 48 | case DND: 49 | return 4; 50 | case AWAY: 51 | case EXTENDED_AWAY: 52 | return 2; 53 | case AVAILABLE: 54 | default: 55 | return 5; 56 | } 57 | } 58 | 59 | public Presence() { 60 | this(false, Show.NONE, null, 8); 61 | } 62 | 63 | public Presence(int numeric) { 64 | this(numeric != 0, Show.NONE, null, 8); 65 | switch (numeric) { 66 | case 1: 67 | invisible = true; 68 | break; 69 | case 4: 70 | show = Show.DND; 71 | break; 72 | case 2: 73 | case 5: 74 | default: 75 | show = Show.AVAILABLE; 76 | break; 77 | } 78 | } 79 | 80 | public Presence(final boolean available, final Show show, final String status, final int capabilities) { 81 | this.available = available; 82 | this.show = show; 83 | this.status = status; 84 | this.invisible = false; 85 | this.defaultStatusList = new ArrayList(); 86 | this.dndStatusList = new ArrayList(); 87 | this.capabilities = capabilities; 88 | } 89 | 90 | public Presence(final Parcel parcel) { 91 | super(); 92 | setStatusMax(parcel.readInt()); 93 | setStatusListMax(parcel.readInt()); 94 | setStatusListContentsMax(parcel.readInt()); 95 | if (parcel.readInt() != 0) { 96 | setAllowInvisibility(true); 97 | } else { 98 | setAllowInvisibility(false); 99 | } 100 | if (parcel.readInt() != 0) { 101 | setAvailable(true); 102 | } else { 103 | setAvailable(false); 104 | } 105 | setShow(Show.valueOf(parcel.readString())); 106 | status = parcel.readString(); 107 | if (parcel.readInt() == 0) { 108 | setInvisible(false); 109 | } else { 110 | setInvisible(true); 111 | } 112 | defaultStatusList = new ArrayList(); 113 | parcel.readStringList(defaultStatusList); 114 | dndStatusList = new ArrayList(); 115 | parcel.readStringList(dndStatusList); 116 | setCapabilities(parcel.readInt()); 117 | } 118 | 119 | public Presence(final Presence presence) { 120 | statusMax = presence.statusMax; 121 | statusListMax = presence.statusListMax; 122 | statusListContentsMax = presence.statusListContentsMax; 123 | allowInvisibility = presence.allowInvisibility; 124 | available = presence.available; 125 | this.show = presence.show; 126 | status = presence.status; 127 | invisible = presence.invisible; 128 | defaultStatusList = presence.defaultStatusList; 129 | dndStatusList = presence.dndStatusList; 130 | capabilities = presence.capabilities; 131 | } 132 | 133 | private boolean addToList(final List list, String string) { 134 | if (string == null || string.isEmpty()) { 135 | return false; 136 | } 137 | for (final String inList : list) { 138 | if (inList.trim().equals(string.trim())) { 139 | return false; 140 | } 141 | } 142 | 143 | if (string.length() > getStatusMax()) { 144 | string = string.substring(0, getStatusMax()); 145 | } 146 | list.add(0, string); 147 | checkListContentsLength(list); 148 | return true; 149 | } 150 | 151 | public boolean allowInvisibility() { 152 | return allowInvisibility; 153 | } 154 | 155 | private List checkListContentsLength(final List list) { 156 | if (list.size() > getStatusListContentsMax()) { 157 | for (int i = list.size() - 1; i >= getStatusListContentsMax(); i--) { 158 | list.remove(i); 159 | } 160 | 161 | } 162 | return list; 163 | } 164 | 165 | @Override 166 | public int describeContents() { 167 | return 0; 168 | } 169 | 170 | public boolean equals(final Presence presence) { 171 | if (presence != null && available == presence.available && show == presence.show && 172 | (status == null ? presence.status == null : status.equals(presence.status)) && 173 | (invisible == presence.invisible && statusMax == presence.statusMax && 174 | statusListMax == presence.statusListMax && statusListContentsMax == presence.statusListContentsMax && 175 | allowInvisibility == presence.allowInvisibility && 176 | listEqual(defaultStatusList, presence.defaultStatusList) && 177 | listEqual(dndStatusList, presence.dndStatusList) && capabilities == presence.capabilities)) { 178 | return true; 179 | } 180 | return false; 181 | } 182 | 183 | public int getCapabilities() { 184 | return capabilities; 185 | } 186 | 187 | public List getDefaultStatusList() { 188 | return new ArrayList(defaultStatusList); 189 | } 190 | 191 | public List getDndStatusList() { 192 | return new ArrayList(dndStatusList); 193 | } 194 | 195 | public Show getShow() { 196 | return show; 197 | } 198 | 199 | public String getStatus() { 200 | return status; 201 | } 202 | 203 | public int getStatusListContentsMax() { 204 | return statusListContentsMax; 205 | } 206 | 207 | public int getStatusListMax() { 208 | return statusListMax; 209 | } 210 | 211 | public int getStatusMax() { 212 | return statusMax; 213 | } 214 | 215 | public boolean isAvailable() { 216 | return available; 217 | } 218 | 219 | public boolean isInvisible() { 220 | return invisible; 221 | } 222 | 223 | private boolean listEqual(final List firstList, final List secondList) { 224 | if (firstList.size() != secondList.size()) { 225 | return false; 226 | } 227 | for (int i = 0; i < firstList.size(); i++) { 228 | if (!firstList.get(i).equals(secondList.get(i))) { 229 | return false; 230 | } 231 | } 232 | return true; 233 | } 234 | 235 | public String printDetails() { 236 | final StringBuilder stringbuilder = new StringBuilder(); 237 | stringbuilder.append("{ available=").append(available).append(", show=").append(show).append(", "); 238 | if (status == null) { 239 | stringbuilder.append(""); 240 | } else { 241 | stringbuilder.append(status); 242 | } 243 | stringbuilder.append((new StringBuilder()).append(", invisible=").append(invisible).toString()) 244 | .append(", allowInvisible=").append(allowInvisibility).append(", caps=0x") 245 | .append(Integer.toHexString(capabilities)).append(", default={"); 246 | if (defaultStatusList != null) { 247 | boolean first = true; 248 | for (final String defaultStatus : defaultStatusList) { 249 | if (!first) { 250 | stringbuilder.append(", "); 251 | } 252 | stringbuilder.append(defaultStatus); 253 | first = false; 254 | } 255 | 256 | } 257 | stringbuilder.append("}, dnd={"); 258 | if (dndStatusList != null) { 259 | boolean first = true; 260 | for (final String dndStatus : dndStatusList) { 261 | if (!first) { 262 | stringbuilder.append(", "); 263 | } 264 | stringbuilder.append(dndStatus); 265 | first = false; 266 | } 267 | 268 | } 269 | return stringbuilder.append("}").append("}").toString(); 270 | } 271 | 272 | public void setAllowInvisibility(final boolean allowInvisibility) { 273 | this.allowInvisibility = allowInvisibility; 274 | } 275 | 276 | public void setAvailable(final boolean available) { 277 | this.available = available; 278 | } 279 | 280 | public void setCapabilities(final int capabilities) { 281 | this.capabilities = capabilities; 282 | } 283 | 284 | public boolean setInvisible(final boolean invisible) { 285 | this.invisible = invisible; 286 | if (invisible && !allowInvisibility()) { 287 | return false; 288 | } else { 289 | return true; 290 | } 291 | } 292 | 293 | public void setShow(final Show show) { 294 | this.show = show; 295 | } 296 | 297 | public void setStatus(final Show show, final String s) { 298 | setShow(show); 299 | setStatus(s, true); 300 | } 301 | 302 | public void setStatus(final String s) { 303 | setStatus(s, false); 304 | } 305 | 306 | private void setStatus(final String s, final boolean flag) { 307 | status = s; 308 | if (!flag) { 309 | switch (show) { 310 | case DND: 311 | addToList(dndStatusList, s); 312 | break; 313 | case AVAILABLE: 314 | addToList(defaultStatusList, s); 315 | break; 316 | default: 317 | break; 318 | } 319 | } 320 | } 321 | 322 | public void setStatusListContentsMax(final int i) { 323 | statusListContentsMax = i; 324 | } 325 | 326 | public void setStatusListMax(final int i) { 327 | statusListMax = i; 328 | } 329 | 330 | public void setStatusMax(final int i) { 331 | statusMax = i; 332 | } 333 | 334 | @Override 335 | public void writeToParcel(final Parcel parcel, final int flags) { 336 | parcel.writeInt(getStatusMax()); 337 | parcel.writeInt(getStatusListMax()); 338 | parcel.writeInt(getStatusListContentsMax()); 339 | if (allowInvisibility()) { 340 | parcel.writeInt(1); 341 | } else { 342 | parcel.writeInt(0); 343 | } 344 | if (available) { 345 | parcel.writeInt(1); 346 | } else { 347 | parcel.writeInt(0); 348 | } 349 | parcel.writeString(show.toString()); 350 | parcel.writeString(status); 351 | if (invisible) { 352 | parcel.writeInt(1); 353 | } else { 354 | parcel.writeInt(0); 355 | } 356 | parcel.writeStringList(defaultStatusList); 357 | parcel.writeStringList(dndStatusList); 358 | parcel.writeInt(getCapabilities()); 359 | } 360 | 361 | } 362 | -------------------------------------------------------------------------------- /src/org/microg/gsf/deviceid/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 μg Project Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.microg.gsf.deviceid; 18 | 19 | import android.content.ContentValues; 20 | import android.content.Context; 21 | import android.database.Cursor; 22 | import android.database.sqlite.SQLiteDatabase; 23 | import android.database.sqlite.SQLiteOpenHelper; 24 | import com.google.android.AndroidContext; 25 | 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | public class DatabaseHelper extends SQLiteOpenHelper { 30 | public static final String META_FIRST_TIME_CONFIG = "first_time_config"; 31 | 32 | private static final int VERSION = 1; 33 | private static final String DATABASE_NAME = "device_ids"; 34 | 35 | public DatabaseHelper(Context context) { 36 | super(context, DATABASE_NAME, null, VERSION); 37 | } 38 | 39 | @Override 40 | public void onCreate(SQLiteDatabase db) { 41 | db.execSQL("CREATE TABLE device_spec (id INT PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE);"); 42 | db.execSQL("CREATE TABLE device_spec_data (spec_id INT FOREIGN KEY REFERENCES device_spec.id, name TEXT, value TEXT);"); 43 | db.execSQL("CREATE TABLE device_uniq (id INT PRIMARY KEY AUTOINCREMENT, spec_id INT FOREIGN KEY REFERENCES device_spec.id, name TEXT UNIQUE);"); 44 | db.execSQL("CREATE TABLE device_uniq_link (uniq_id INT FOREIGN KEY REFERENCES device_uniq.id, link TEXT UNIQUE);"); 45 | db.execSQL("CREATE TABLE device_uniq_data (uniq_id INT FOREIGN KEY REFERENCES device_uniq.id, name TEXT, value TEXT)"); 46 | db.execSQL("CREATE TABLE meta_data (name TEXT UNIQUE, value TEXT);"); 47 | putDeviceSpec("Samsung Galaxy Nexus (4.1.1)", AndroidContext.baseGalaxyNexus_16().asMap()); 48 | } 49 | 50 | @Override 51 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 52 | // no upgrades yet 53 | } 54 | 55 | public String getMeta(String name, String def) { 56 | SQLiteDatabase db = getReadableDatabase(); 57 | Cursor data = db.query("meta_data", new String[]{"value"}, "name=?", new String[]{name}, null, null, null); 58 | String result = def; 59 | if (data != null) { 60 | if (data.moveToNext()) { 61 | result = data.getString(0); 62 | } 63 | data.close(); 64 | } 65 | db.close(); 66 | return result; 67 | } 68 | 69 | public void putMeta(String name, String value) { 70 | ContentValues values = new ContentValues(); 71 | values.put("name", name); 72 | values.put("value", value); 73 | SQLiteDatabase db = getWritableDatabase(); 74 | db.insertWithOnConflict("meta_data", null, values, SQLiteDatabase.CONFLICT_REPLACE); 75 | db.close(); 76 | } 77 | 78 | public long putDeviceSpec(String name, Map data) { 79 | SQLiteDatabase db = getWritableDatabase(); 80 | ContentValues values = new ContentValues(); 81 | values.put("name", name); 82 | long id = db.insertWithOnConflict("device_spec", null, values, SQLiteDatabase.CONFLICT_IGNORE); 83 | if (id < 0) { 84 | throw new RuntimeException("Could not add device spec"); 85 | } 86 | for (Map.Entry entry : data.entrySet()) { 87 | values.clear(); 88 | values.put("id", id); 89 | values.put("name", entry.getKey()); 90 | values.put("value", entry.getValue()); 91 | db.insertWithOnConflict("device_spec_data", null, values, SQLiteDatabase.CONFLICT_REPLACE); 92 | } 93 | db.close(); 94 | return id; 95 | } 96 | 97 | public Map getDeviceData(String link) { 98 | HashMap result = new HashMap(); 99 | SQLiteDatabase db = getReadableDatabase(); 100 | Cursor cursor = db.rawQuery("SELECT device_spec_data.name, device_spec_data.value " + 101 | "FROM device_uniq_link JOIN device_uniq ON device_uniq.id = device_uniq_link.uniq_id " + 102 | "JOIN device_spec_data ON device_spec_data.spec_id = device_uniq.spec_id " + 103 | "WHERE device_uniq_link.link = ?", new String[]{link}); 104 | if (cursor != null) { 105 | while (cursor.moveToNext()) { 106 | result.put(cursor.getString(0), cursor.getString(1)); 107 | } 108 | cursor.close(); 109 | } 110 | cursor = db.rawQuery("SELECT device_uniq_data.name, device_uniq_data.value " + 111 | "FROM device_uniq_link JOIN device_uniq_data ON device_uniq_data.uniq_id = device_uniq_link.uniq_id " + 112 | "WHERE device_uniq_link.link = ?", new String[]{link}); 113 | if (cursor != null) { 114 | while (cursor.moveToNext()) { 115 | result.put(cursor.getString(0), cursor.getString(1)); 116 | } 117 | cursor.close(); 118 | } 119 | db.close(); 120 | return result; 121 | } 122 | 123 | public long putDeviceUniq(String name, long specId, Map data) { 124 | SQLiteDatabase db = getWritableDatabase(); 125 | ContentValues values = new ContentValues(); 126 | values.put("name", name); 127 | values.put("spec_id", specId); 128 | long id = db.insertWithOnConflict("device_uniq", null, values, SQLiteDatabase.CONFLICT_IGNORE); 129 | if (id < 0) { 130 | throw new RuntimeException("Could not add device uniq"); 131 | } 132 | for (Map.Entry entry : data.entrySet()) { 133 | values.clear(); 134 | values.put("id", id); 135 | values.put("name", entry.getKey()); 136 | values.put("value", entry.getValue()); 137 | db.insertWithOnConflict("device_uniq_data", null, values, SQLiteDatabase.CONFLICT_REPLACE); 138 | } 139 | db.close(); 140 | return id; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/org/microg/gsf/deviceid/FirstTimeConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 μg Project Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.microg.gsf.deviceid; 18 | 19 | import android.app.Activity; 20 | import android.os.Bundle; 21 | import com.google.android.gsf.R; 22 | 23 | public class FirstTimeConfig extends Activity { 24 | @Override 25 | public void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.first_time_config); 28 | } 29 | } -------------------------------------------------------------------------------- /src/org/microg/gsf/deviceid/ManagerService.java: -------------------------------------------------------------------------------- 1 | package org.microg.gsf.deviceid; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.IBinder; 7 | import android.os.RemoteException; 8 | import org.microg.gsf.IDeviceIdManager; 9 | 10 | import java.util.Arrays; 11 | 12 | public class ManagerService extends Service implements IDeviceIdManager { 13 | private static final int FIRST_TIME_CONFIG_VERSION = 1; 14 | private DatabaseHelper dbHelper; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | dbHelper = new DatabaseHelper(this); 20 | } 21 | 22 | @Override 23 | public IBinder onBind(Intent intent) { 24 | return asBinder(); 25 | } 26 | 27 | private boolean firstTimeConfigDone() { 28 | return Integer.valueOf(dbHelper.getMeta(DatabaseHelper.META_FIRST_TIME_CONFIG, "0")) == FIRST_TIME_CONFIG_VERSION; 29 | } 30 | 31 | private void runFirstTimeConfig() { 32 | Intent intent = new Intent(this, FirstTimeConfig.class); 33 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 34 | startActivity(intent); 35 | } 36 | 37 | @Override 38 | public String getSystemAndroidId() throws RemoteException { 39 | if (!firstTimeConfigDone()) { 40 | runFirstTimeConfig(); 41 | return null; 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public String getSystemDeviceInfo(String identifier) throws RemoteException { 48 | if (!firstTimeConfigDone()) { 49 | runFirstTimeConfig(); 50 | return null; 51 | } 52 | return null; 53 | } 54 | 55 | @Override 56 | public Bundle getSystemAllDeviceInfo() throws RemoteException { 57 | if (!firstTimeConfigDone()) { 58 | runFirstTimeConfig(); 59 | return null; 60 | } 61 | return null; 62 | } 63 | 64 | @Override 65 | public String getAppAndroidId(String packageName) throws RemoteException { 66 | if (!firstTimeConfigDone()) { 67 | runFirstTimeConfig(); 68 | return null; 69 | } 70 | return null; 71 | } 72 | 73 | @Override 74 | public String getAppDeviceInfo(String packageName, String identifier) throws RemoteException { 75 | if (!firstTimeConfigDone()) { 76 | runFirstTimeConfig(); 77 | return null; 78 | } 79 | return null; 80 | } 81 | 82 | @Override 83 | public Bundle getAppAllDeviceInfo(String packageName) throws RemoteException { 84 | if (!firstTimeConfigDone()) { 85 | runFirstTimeConfig(); 86 | return null; 87 | } 88 | return null; 89 | } 90 | 91 | @Override 92 | public void resetSystemAndroidId() throws RemoteException { 93 | if (!firstTimeConfigDone()) { 94 | runFirstTimeConfig(); 95 | return; 96 | } 97 | } 98 | 99 | @Override 100 | public void destroyAppAndroidId(String packageName) throws RemoteException { 101 | if (!firstTimeConfigDone()) { 102 | runFirstTimeConfig(); 103 | return; 104 | } 105 | } 106 | 107 | @Override 108 | public IBinder asBinder() { 109 | return new IDeviceIdManager.Stub() { 110 | @Override 111 | public String getSystemAndroidId() throws RemoteException { 112 | if (isPrivileged()) { 113 | ManagerService.this.getSystemAndroidId(); 114 | } 115 | return null; 116 | } 117 | 118 | @Override 119 | public String getSystemDeviceInfo(String identifier) throws RemoteException { 120 | if (isPrivileged()) { 121 | ManagerService.this.getSystemDeviceInfo(identifier); 122 | } 123 | return null; 124 | } 125 | 126 | @Override 127 | public Bundle getSystemAllDeviceInfo() throws RemoteException { 128 | if (isPrivileged()) { 129 | ManagerService.this.getSystemAllDeviceInfo(); 130 | } 131 | return null; 132 | } 133 | 134 | @Override 135 | public String getAppAndroidId(String packageName) throws RemoteException { 136 | if (isPackageOwner(packageName) || isPrivileged()) { 137 | ManagerService.this.getAppAndroidId(packageName); 138 | } 139 | return null; 140 | } 141 | 142 | @Override 143 | public String getAppDeviceInfo(String packageName, String identifier) throws RemoteException { 144 | if (isPackageOwner(packageName) || isPrivileged()) { 145 | ManagerService.this.getAppDeviceInfo(packageName, identifier); 146 | } 147 | return null; 148 | } 149 | 150 | @Override 151 | public void resetSystemAndroidId() throws RemoteException { 152 | if (isPrivileged()) { 153 | ManagerService.this.resetSystemAndroidId(); 154 | } 155 | } 156 | 157 | @Override 158 | public void destroyAppAndroidId(String packageName) throws RemoteException { 159 | if (isPackageOwner(packageName) || isPrivileged()) { 160 | ManagerService.this.destroyAppAndroidId(packageName); 161 | } 162 | } 163 | 164 | @Override 165 | public Bundle getAppAllDeviceInfo(String packageName) throws RemoteException { 166 | if (isPackageOwner(packageName) || isPrivileged()) { 167 | return ManagerService.this.getAppAllDeviceInfo(packageName); 168 | } 169 | return null; 170 | } 171 | 172 | private boolean isPackageOwner(String packageName) { 173 | return Arrays.asList(getPackageManager().getPackagesForUid(getCallingUid())).contains(packageName); 174 | } 175 | 176 | private boolean isPrivileged() { 177 | // TODO: Do security checks here 178 | return false; 179 | } 180 | }; 181 | } 182 | } 183 | --------------------------------------------------------------------------------