blob: de7b1d7b064b8040bad320ab5dd8dc8df943c101 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001package com.android.providers.subscribedfeeds;
2
3import android.content.Intent;
4import android.content.Context;
5import android.content.ContentResolver;
6import android.content.ContentValues;
7import android.content.SharedPreferences;
8import android.util.Log;
9import android.util.Config;
10import android.util.EventLog;
11import android.app.IntentService;
12import android.provider.Sync;
13import android.provider.SubscribedFeeds;
14import android.provider.SyncConstValue;
15import android.database.Cursor;
16import android.database.sqlite.SQLiteFullException;
17import android.app.AlarmManager;
18import android.app.PendingIntent;
19import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.text.TextUtils;
21import android.net.Uri;
Fred Quintanad9d2f112009-04-23 13:36:27 -070022import android.accounts.Account;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
24import java.util.ArrayList;
25import java.util.Calendar;
26
Fred Quintanad9d2f112009-04-23 13:36:27 -070027import com.google.android.collect.Lists;
28
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029/**
30 * A service to handle various intents asynchronously.
31 */
32public class SubscribedFeedsIntentService extends IntentService {
33 private static final String TAG = "Sync";
34
35 private static final String[] sAccountProjection =
Fred Quintanad9d2f112009-04-23 13:36:27 -070036 new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT,
37 SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
39 /** How often to refresh the subscriptions, in milliseconds */
40 private static final long SUBSCRIPTION_REFRESH_INTERVAL = 1000L * 60 * 60 * 24; // one day
41
42 private static final String sRefreshTime = "refreshTime";
43
44 private static final String sSubscribedFeedsPrefs = "subscribedFeeds";
45
46 private static final String GTALK_DATA_MESSAGE_RECEIVED =
47 "android.intent.action.GTALK_DATA_MESSAGE_RECEIVED";
48
49 private static final String SUBSCRIBED_FEEDS_REFRESH_ACTION =
50 "com.android.subscribedfeeds.action.REFRESH";
51
52 private static final int LOG_TICKLE = 2742;
53
54 public SubscribedFeedsIntentService() {
55 super("SubscribedFeedsIntentService");
56 }
57
58 protected void onHandleIntent(Intent intent) {
59 if (GTALK_DATA_MESSAGE_RECEIVED.equals(intent.getAction())) {
60 boolean fromTrustedServer = intent.getBooleanExtra("from_trusted_server", false);
61 if (fromTrustedServer) {
Fred Quintanad9d2f112009-04-23 13:36:27 -070062 String accountName = intent.getStringExtra("account");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 String token = intent.getStringExtra("message_token");
64
Fred Quintanad9d2f112009-04-23 13:36:27 -070065 if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(token)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 if (Config.LOGD) {
67 Log.d(TAG, "Ignoring malformed tickle -- missing account or token.");
68 }
69 return;
70 }
71
72 if (Config.LOGD) {
73 Log.d(TAG, "Received network tickle for "
Fred Quintanad9d2f112009-04-23 13:36:27 -070074 + accountName + " - " + token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 }
76
Fred Quintanad9d2f112009-04-23 13:36:27 -070077 handleTickle(this, accountName, token);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 } else {
79 if (Log.isLoggable(TAG, Log.VERBOSE)) {
80 Log.v(TAG, "Ignoring tickle -- not from trusted server.");
81 }
82 }
83
84 } else if (Intent.ACTION_BOOT_COMPLETED.equals(
85 intent.getAction())) {
86 if (Config.LOGD) {
87 Log.d(TAG, "Received boot completed action");
88 }
89 // load the time from the shared preferences and schedule an alarm
90 long refreshTime = getSharedPreferences(
91 sSubscribedFeedsPrefs,
92 Context.MODE_WORLD_READABLE).getLong(sRefreshTime, 0);
93 scheduleRefresh(this, refreshTime);
94 } else if (SUBSCRIBED_FEEDS_REFRESH_ACTION.equals(intent.getAction())) {
95 if (Config.LOGD) {
96 Log.d(TAG, "Received sSubscribedFeedsRefreshIntent");
97 }
98 handleRefreshAlarm(this);
99 }
100 }
101 private void scheduleRefresh(Context context, long when) {
102 AlarmManager alarmManager = (AlarmManager) context.getSystemService(
103 Context.ALARM_SERVICE);
104 PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
105 0, new Intent(SUBSCRIBED_FEEDS_REFRESH_ACTION), 0);
106 alarmManager.set(AlarmManager.RTC, when, pendingIntent);
107 }
108
Fred Quintanad9d2f112009-04-23 13:36:27 -0700109 private void handleTickle(Context context, String accountName, String feed) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 Cursor c = null;
111 Sync.Settings.QueryMap syncSettings =
112 new Sync.Settings.QueryMap(context.getContentResolver(),
113 false /* don't keep updated */,
114 null /* not needed since keep updated is false */);
115 final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
Fred Quintanad9d2f112009-04-23 13:36:27 -0700116 + "and " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "= ? "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 + "and " + SubscribedFeeds.Feeds.FEED + "= ?";
118 try {
Fred Quintanad9d2f112009-04-23 13:36:27 -0700119 // TODO(fredq) fix the hardcoded type
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 c = context.getContentResolver().query(SubscribedFeeds.Feeds.CONTENT_URI,
Fred Quintanad9d2f112009-04-23 13:36:27 -0700121 null, where, new String[]{accountName, "com.google.GAIA", feed}, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 if (c.getCount() == 0) {
123 Log.w(TAG, "received tickle for non-existent feed: "
Fred Quintanad9d2f112009-04-23 13:36:27 -0700124 + "account " + accountName + ", feed " + feed);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 EventLog.writeEvent(LOG_TICKLE, "unknown");
126 }
127 while (c.moveToNext()) {
128 // initiate a sync
129 String authority = c.getString(c.getColumnIndexOrThrow(
130 SubscribedFeeds.Feeds.AUTHORITY));
131 EventLog.writeEvent(LOG_TICKLE, authority);
132 if (!syncSettings.getSyncProviderAutomatically(authority)) {
133 Log.d(TAG, "supressing tickle since provider " + authority
134 + " is configured to not sync automatically");
135 continue;
136 }
137 Uri uri = Uri.parse("content://" + authority);
138 Bundle extras = new Bundle();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700139 extras.putParcelable(ContentResolver.SYNC_EXTRAS_ACCOUNT,
140 new Account(accountName, "com.google.GAIA"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 extras.putString("feed", feed);
142 context.getContentResolver().startSync(uri, extras);
143 }
144 } finally {
145 if (c != null) c.deactivate();
146 syncSettings.close();
147 }
148 }
149
150 /**
151 * Cause all the subscribed feeds to be marked dirty and their
152 * authtokens to be refreshed, which will result in new authtokens
153 * being sent to the subscription server. Then reschedules this
154 * event for one week in the future.
155 *
156 * @param context Context we are running within
157 */
158 private void handleRefreshAlarm(Context context) {
159 // retrieve the list of accounts from the subscribed feeds
Fred Quintanad9d2f112009-04-23 13:36:27 -0700160 ArrayList<Account> accounts = Lists.newArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 ContentResolver contentResolver = context.getContentResolver();
162 Cursor c = contentResolver.query(SubscribedFeeds.Accounts.CONTENT_URI,
163 sAccountProjection, null, null, null);
Fred Quintana22f71142009-03-24 20:10:17 -0700164 try {
165 while (c.moveToNext()) {
Fred Quintanad9d2f112009-04-23 13:36:27 -0700166 String accountName = c.getString(0);
167 String accountType = c.getString(1);
168 accounts.add(new Account(accountName, accountType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 }
Fred Quintana22f71142009-03-24 20:10:17 -0700170 } finally {
171 c.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173
174 // Clear the auth tokens for all these accounts so that we are sure
175 // they will still be valid until the next time we refresh them.
Fred Quintanad9d2f112009-04-23 13:36:27 -0700176 // TODO(fredq): add this when the google login service is done
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
178 // mark the feeds dirty, by setting the accounts to the same value,
179 // which will trigger a sync.
180 try {
181 ContentValues values = new ContentValues();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700182 for (Account account : accounts) {
183 values.put(SyncConstValue._SYNC_ACCOUNT, account.mName);
184 values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.mType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values,
Fred Quintanad9d2f112009-04-23 13:36:27 -0700186 SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=? AND "
187 + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?",
188 new String[] {account.mName, account.mType});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 }
190 } catch (SQLiteFullException e) {
191 Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping");
192 }
193
194 // Schedule a refresh.
195 long refreshTime = Calendar.getInstance().getTimeInMillis() + SUBSCRIPTION_REFRESH_INTERVAL;
196 scheduleRefresh(context, refreshTime);
197 SharedPreferences.Editor editor = context.getSharedPreferences(sSubscribedFeedsPrefs,
198 Context.MODE_WORLD_READABLE).edit();
199 editor.putLong(sRefreshTime, refreshTime);
200 editor.commit();
201 }
202}