summaryrefslogtreecommitdiffstats
path: root/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
blob: 8b3bedf28a7a22944b7cfefcd9d04c4fd5fdfb04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package com.android.providers.subscribedfeeds;

import android.content.Intent;
import android.content.Context;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.SharedPreferences;
import android.util.Log;
import android.util.Config;
import android.util.EventLog;
import android.app.IntentService;
import android.provider.SubscribedFeeds;
import android.provider.SyncConstValue;
import android.database.Cursor;
import android.database.sqlite.SQLiteFullException;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils;
import android.net.Uri;

import java.util.ArrayList;
import java.util.Calendar;

/**
 * A service to handle various intents asynchronously.
 */
public class SubscribedFeedsIntentService extends IntentService {
    private static final String TAG = "Sync";

    private static final String[] sAccountProjection =
            new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT};

    /** How often to refresh the subscriptions, in milliseconds */
    private static final long SUBSCRIPTION_REFRESH_INTERVAL = 1000L * 60 * 60 * 24; // one day

    private static final String sRefreshTime = "refreshTime";

    private static final String sSubscribedFeedsPrefs = "subscribedFeeds";

    private static final String GTALK_DATA_MESSAGE_RECEIVED =
            "android.intent.action.GTALK_DATA_MESSAGE_RECEIVED";

    private static final String SUBSCRIBED_FEEDS_REFRESH_ACTION =
            "com.android.subscribedfeeds.action.REFRESH";

    private static final int LOG_TICKLE = 2742;

    public SubscribedFeedsIntentService() {
        super("SubscribedFeedsIntentService");
    }

    protected void onHandleIntent(Intent intent) {
        if (GTALK_DATA_MESSAGE_RECEIVED.equals(intent.getAction())) {
            boolean fromTrustedServer = intent.getBooleanExtra("from_trusted_server", false);
            if (fromTrustedServer) {
                String account = intent.getStringExtra("account");
                String token = intent.getStringExtra("message_token");

                if (TextUtils.isEmpty(account) || TextUtils.isEmpty(token)) {
                    if (Config.LOGD) {
                        Log.d(TAG, "Ignoring malformed tickle -- missing account or token.");
                    }
                    return;
                }

                if (Config.LOGD) {
                    Log.d(TAG, "Received network tickle for "
                            + account + " - " + token);
                }

                handleTickle(this, account, token);
            } else {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Ignoring tickle -- not from trusted server.");
                }
            }

        } else if (Intent.ACTION_BOOT_COMPLETED.equals(
                intent.getAction())) {
            if (Config.LOGD) {
                Log.d(TAG, "Received boot completed action");
            }
            // load the time from the shared preferences and schedule an alarm
            long refreshTime = getSharedPreferences(
                    sSubscribedFeedsPrefs,
                    Context.MODE_WORLD_READABLE).getLong(sRefreshTime, 0);
            scheduleRefresh(this, refreshTime);
        } else if (SUBSCRIBED_FEEDS_REFRESH_ACTION.equals(intent.getAction())) {
            if (Config.LOGD) {
                Log.d(TAG, "Received sSubscribedFeedsRefreshIntent");
            }
            handleRefreshAlarm(this);
        }
    }
    private void scheduleRefresh(Context context, long when) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(
                Context.ALARM_SERVICE);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
                0, new Intent(SUBSCRIBED_FEEDS_REFRESH_ACTION), 0);
        alarmManager.set(AlarmManager.RTC, when, pendingIntent);
    }

    private void handleTickle(Context context, String account, String feed) {
        Cursor c = null;
        final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
                + "and " + SubscribedFeeds.Feeds.FEED + "= ?";
        try {
            c = context.getContentResolver().query(SubscribedFeeds.Feeds.CONTENT_URI,
                    null, where, new String[]{account, feed}, null);
            if (c.getCount() == 0) {
                Log.w(TAG, "received tickle for non-existent feed: "
                        + "account " + account + ", feed " + feed);
                EventLog.writeEvent(LOG_TICKLE, "unknown");
            }
            while (c.moveToNext()) {
                // initiate a sync
                String authority = c.getString(c.getColumnIndexOrThrow(
                        SubscribedFeeds.Feeds.AUTHORITY));
                EventLog.writeEvent(LOG_TICKLE, authority);
                try {
                    if (!ContentResolver.getContentService()
                            .getSyncProviderAutomatically(authority)) {
                        Log.d(TAG, "supressing tickle since provider " + authority
                                + " is configured to not sync automatically");
                        continue;
                    }
                } catch (RemoteException e) {
                    continue;
                }
                Uri uri = Uri.parse("content://" + authority);
                Bundle extras = new Bundle();
                extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
                extras.putString("feed", feed);
                context.getContentResolver().startSync(uri, extras);
            }
        } finally {
            if (c != null) c.deactivate();
        }
    }

    /**
     * Cause all the subscribed feeds to be marked dirty and their
     * authtokens to be refreshed, which will result in new authtokens
     * being sent to the subscription server. Then reschedules this
     * event for one week in the future.
     *
     * @param context Context we are running within
     */
    private void handleRefreshAlarm(Context context) {
        // retrieve the list of accounts from the subscribed feeds
        ArrayList<String> accounts = new ArrayList<String>();
        ContentResolver contentResolver = context.getContentResolver();
        Cursor c = contentResolver.query(SubscribedFeeds.Accounts.CONTENT_URI,
                sAccountProjection, null, null, null);
        while (c.moveToNext()) {
            String account = c.getString(0);
            if (TextUtils.isEmpty(account)) {
                continue;
            }
            accounts.add(account);
        }
        c.deactivate();

        // Clear the auth tokens for all these accounts so that we are sure
        // they will still be valid until the next time we refresh them.
        // TODO: add this when the google login service is done

        // mark the feeds dirty, by setting the accounts to the same value,
        //  which will trigger a sync.
        try {
            ContentValues values = new ContentValues();
            for (String account : accounts) {
                values.put(SyncConstValue._SYNC_ACCOUNT, account);
                contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values,
                        SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?", new String[] {account});
            }
        } catch (SQLiteFullException e) {
            Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping");
        }

        // Schedule a refresh.
        long refreshTime = Calendar.getInstance().getTimeInMillis() + SUBSCRIPTION_REFRESH_INTERVAL;
        scheduleRefresh(context, refreshTime);
        SharedPreferences.Editor editor = context.getSharedPreferences(sSubscribedFeedsPrefs,
                Context.MODE_WORLD_READABLE).edit();
        editor.putLong(sRefreshTime, refreshTime);
        editor.commit();
    }
}