blob: df599c7cfa1d3965545b6e60f743aa7eef2cb8f7 [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;
20import android.os.Debug;
21import android.text.TextUtils;
22import android.net.Uri;
23
24import java.util.ArrayList;
25import java.util.Calendar;
26
27/**
28 * A service to handle various intents asynchronously.
29 */
30public class SubscribedFeedsIntentService extends IntentService {
31 private static final String TAG = "Sync";
32
33 private static final String[] sAccountProjection =
34 new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT};
35
36 /** How often to refresh the subscriptions, in milliseconds */
37 private static final long SUBSCRIPTION_REFRESH_INTERVAL = 1000L * 60 * 60 * 24; // one day
38
39 private static final String sRefreshTime = "refreshTime";
40
41 private static final String sSubscribedFeedsPrefs = "subscribedFeeds";
42
43 private static final String GTALK_DATA_MESSAGE_RECEIVED =
44 "android.intent.action.GTALK_DATA_MESSAGE_RECEIVED";
45
46 private static final String SUBSCRIBED_FEEDS_REFRESH_ACTION =
47 "com.android.subscribedfeeds.action.REFRESH";
48
49 private static final int LOG_TICKLE = 2742;
50
51 public SubscribedFeedsIntentService() {
52 super("SubscribedFeedsIntentService");
53 }
54
55 protected void onHandleIntent(Intent intent) {
56 if (GTALK_DATA_MESSAGE_RECEIVED.equals(intent.getAction())) {
57 boolean fromTrustedServer = intent.getBooleanExtra("from_trusted_server", false);
58 if (fromTrustedServer) {
59 String account = intent.getStringExtra("account");
60 String token = intent.getStringExtra("message_token");
61
62 if (TextUtils.isEmpty(account) || TextUtils.isEmpty(token)) {
63 if (Config.LOGD) {
64 Log.d(TAG, "Ignoring malformed tickle -- missing account or token.");
65 }
66 return;
67 }
68
69 if (Config.LOGD) {
70 Log.d(TAG, "Received network tickle for "
71 + account + " - " + token);
72 }
73
74 handleTickle(this, account, token);
75 } else {
76 if (Log.isLoggable(TAG, Log.VERBOSE)) {
77 Log.v(TAG, "Ignoring tickle -- not from trusted server.");
78 }
79 }
80
81 } else if (Intent.ACTION_BOOT_COMPLETED.equals(
82 intent.getAction())) {
83 if (Config.LOGD) {
84 Log.d(TAG, "Received boot completed action");
85 }
86 // load the time from the shared preferences and schedule an alarm
87 long refreshTime = getSharedPreferences(
88 sSubscribedFeedsPrefs,
89 Context.MODE_WORLD_READABLE).getLong(sRefreshTime, 0);
90 scheduleRefresh(this, refreshTime);
91 } else if (SUBSCRIBED_FEEDS_REFRESH_ACTION.equals(intent.getAction())) {
92 if (Config.LOGD) {
93 Log.d(TAG, "Received sSubscribedFeedsRefreshIntent");
94 }
95 handleRefreshAlarm(this);
96 }
97 }
98 private void scheduleRefresh(Context context, long when) {
99 AlarmManager alarmManager = (AlarmManager) context.getSystemService(
100 Context.ALARM_SERVICE);
101 PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
102 0, new Intent(SUBSCRIBED_FEEDS_REFRESH_ACTION), 0);
103 alarmManager.set(AlarmManager.RTC, when, pendingIntent);
104 }
105
106 private void handleTickle(Context context, String account, String feed) {
107 Cursor c = null;
108 Sync.Settings.QueryMap syncSettings =
109 new Sync.Settings.QueryMap(context.getContentResolver(),
110 false /* don't keep updated */,
111 null /* not needed since keep updated is false */);
112 final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
113 + "and " + SubscribedFeeds.Feeds.FEED + "= ?";
114 try {
115 c = context.getContentResolver().query(SubscribedFeeds.Feeds.CONTENT_URI,
116 null, where, new String[]{account, feed}, null);
117 if (c.getCount() == 0) {
118 Log.w(TAG, "received tickle for non-existent feed: "
119 + "account " + account + ", feed " + feed);
120 EventLog.writeEvent(LOG_TICKLE, "unknown");
121 }
122 while (c.moveToNext()) {
123 // initiate a sync
124 String authority = c.getString(c.getColumnIndexOrThrow(
125 SubscribedFeeds.Feeds.AUTHORITY));
126 EventLog.writeEvent(LOG_TICKLE, authority);
127 if (!syncSettings.getSyncProviderAutomatically(authority)) {
128 Log.d(TAG, "supressing tickle since provider " + authority
129 + " is configured to not sync automatically");
130 continue;
131 }
132 Uri uri = Uri.parse("content://" + authority);
133 Bundle extras = new Bundle();
134 extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
135 extras.putString("feed", feed);
136 context.getContentResolver().startSync(uri, extras);
137 }
138 } finally {
139 if (c != null) c.deactivate();
140 syncSettings.close();
141 }
142 }
143
144 /**
145 * Cause all the subscribed feeds to be marked dirty and their
146 * authtokens to be refreshed, which will result in new authtokens
147 * being sent to the subscription server. Then reschedules this
148 * event for one week in the future.
149 *
150 * @param context Context we are running within
151 */
152 private void handleRefreshAlarm(Context context) {
153 // retrieve the list of accounts from the subscribed feeds
154 ArrayList<String> accounts = new ArrayList<String>();
155 ContentResolver contentResolver = context.getContentResolver();
156 Cursor c = contentResolver.query(SubscribedFeeds.Accounts.CONTENT_URI,
157 sAccountProjection, null, null, null);
158 while (c.moveToNext()) {
159 String account = c.getString(0);
160 if (TextUtils.isEmpty(account)) {
161 continue;
162 }
163 accounts.add(account);
164 }
165 c.deactivate();
166
167 // Clear the auth tokens for all these accounts so that we are sure
168 // they will still be valid until the next time we refresh them.
169 // TODO: add this when the google login service is done
170
171 // mark the feeds dirty, by setting the accounts to the same value,
172 // which will trigger a sync.
173 try {
174 ContentValues values = new ContentValues();
175 for (String account : accounts) {
176 values.put(SyncConstValue._SYNC_ACCOUNT, account);
177 contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values,
178 SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?", new String[] {account});
179 }
180 } catch (SQLiteFullException e) {
181 Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping");
182 }
183
184 // Schedule a refresh.
185 long refreshTime = Calendar.getInstance().getTimeInMillis() + SUBSCRIPTION_REFRESH_INTERVAL;
186 scheduleRefresh(context, refreshTime);
187 SharedPreferences.Editor editor = context.getSharedPreferences(sSubscribedFeedsPrefs,
188 Context.MODE_WORLD_READABLE).edit();
189 editor.putLong(sRefreshTime, refreshTime);
190 editor.commit();
191 }
192}