1/*
2 * Copyright (c) 2015, Motorola Mobility LLC
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *     - Redistributions of source code must retain the above copyright
8 *       notice, this list of conditions and the following disclaimer.
9 *     - Redistributions in binary form must reproduce the above copyright
10 *       notice, this list of conditions and the following disclaimer in the
11 *       documentation and/or other materials provided with the distribution.
12 *     - Neither the name of Motorola Mobility nor the
13 *       names of its contributors may be used to endorse or promote products
14 *       derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 * DAMAGE.
27 */
28
29package com.android.service.ims.presence;
30
31import android.app.AlarmManager;
32import android.app.PendingIntent;
33import android.content.Context;
34import android.content.Intent;
35import android.os.SystemClock;
36import android.text.format.Time;
37
38import com.android.ims.internal.Logger;
39
40import java.util.ArrayList;
41import java.util.List;
42
43public class PollingTask {
44    private Logger logger = Logger.getLogger(this.getClass().getName());
45    private Context mContext = null;
46
47    private static long sMaxId = 0;
48    public static final String ACTION_POLLING_RETRY_ALARM =
49            "com.android.service.ims.presence.capability_polling_retry";
50    private PendingIntent mRetryAlarmIntent = null;
51
52    public long mId;
53    public int mType;
54    public List<Contacts.Item> mContacts = new ArrayList<Contacts.Item>();
55
56    private long mTimeUnit;
57    private int mTotalRetry;
58    private int mCurrentRetry;
59    private long mLastUpdateTime;
60
61    private boolean mCancelled = false;
62    private boolean mCompleted = false;
63
64    public PollingTask(int type, List<Contacts.Item> list) {
65        mId = sMaxId++;
66        mType = type;
67
68        mContacts.clear();
69        for(int i = 0; i < list.size(); i++) {
70            Contacts.Item item = list.get(i);
71            mContacts.add(item);
72        }
73
74        mCurrentRetry = 0;
75        mTotalRetry = 5;
76        mTimeUnit = 1800; // 1800s = 30 minutes
77        if (CapabilityPolling.ACTION_POLLING_NEW_CONTACTS == mType) {
78            mTotalRetry = 4;
79            mTimeUnit = 60; // 60s = 1 minute
80        }
81        mLastUpdateTime = 0;
82    }
83
84    public void execute() {
85        PollingsQueue queue = PollingsQueue.getInstance(null);
86        if (queue == null) {
87            return;
88        }
89
90        PollingAction action = new PollingAction(queue.getContext(), this);
91        action.execute();
92    }
93
94    public void finish(boolean fullUpdated) {
95        cancelRetryAlarm();
96
97        PollingsQueue queue = PollingsQueue.getInstance(null);
98        if (queue == null) {
99            return;
100        }
101
102        mCompleted = fullUpdated ? true : false;
103        queue.remove(this);
104    }
105
106    public void retry() {
107        mCurrentRetry += 1;
108        logger.print("retry mCurrentRetry=" + mCurrentRetry);
109        if (mCurrentRetry > mTotalRetry) {
110            logger.print("Retry finished for task: " + this);
111            updateTimeStampToCurrent();
112            finish(false);
113            return;
114        }
115
116        if (mCancelled) {
117            logger.print("Task is cancelled: " + this);
118            finish(false);
119            return;
120        }
121
122        long delay = mTimeUnit;
123        for (int i = 0; i < mCurrentRetry - 1; i++) {
124            delay *= 2;
125        }
126
127        logger.print("retry delay=" + delay);
128
129        scheduleRetry(delay * 1000);
130    }
131
132    public void cancel() {
133        mCancelled = true;
134        logger.print("Cancel this task: " + this);
135
136        if (mRetryAlarmIntent != null) {
137            cancelRetryAlarm();
138            finish(false);
139        }
140    }
141
142    public void onPreExecute() {
143        logger.print("onPreExecute(), id = " + mId);
144        cancelRetryAlarm();
145    }
146
147    public void onPostExecute(int result) {
148        logger.print("onPostExecute(), result = " + result);
149        mLastUpdateTime = System.currentTimeMillis();
150    }
151
152    private void updateTimeStampToCurrent() {
153        if (mContext == null) {
154            return;
155        }
156
157        EABContactManager contactManager = new EABContactManager(
158                mContext.getContentResolver(), mContext.getPackageName());
159        for (int i = 0; i < mContacts.size(); i++) {
160            Contacts.Item item = mContacts.get(i);
161
162            String number = item.number();
163            long current = System.currentTimeMillis();
164            EABContactManager.Request request = new EABContactManager.Request(number)
165                    .setLastUpdatedTimeStamp(current);
166            int result = contactManager.update(request);
167            if (result <= 0) {
168                logger.debug("failed to update timestamp for contact: ");
169            }
170        }
171    }
172
173    private void cancelRetryAlarm() {
174        if (mRetryAlarmIntent != null) {
175            if (mContext != null) {
176                AlarmManager alarmManager = (AlarmManager)
177                        mContext.getSystemService(Context.ALARM_SERVICE);
178                alarmManager.cancel(mRetryAlarmIntent);
179            }
180            mRetryAlarmIntent = null;
181        }
182    }
183
184    private void scheduleRetry(long msec) {
185        logger.print("scheduleRetry msec=" + msec);
186
187        cancelRetryAlarm();
188
189        if (mContext == null) {
190            PollingsQueue queue = PollingsQueue.getInstance(null);
191            if (queue != null) {
192                mContext = queue.getContext();
193            }
194        }
195
196        if (mContext != null) {
197            Intent intent = new Intent(ACTION_POLLING_RETRY_ALARM);
198            intent.setClass(mContext, AlarmBroadcastReceiver.class);
199            intent.putExtra("pollingTaskId", mId);
200
201            mRetryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
202                    PendingIntent.FLAG_UPDATE_CURRENT);
203
204            AlarmManager alarmManager = (AlarmManager)
205                    mContext.getSystemService(Context.ALARM_SERVICE);
206            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
207                    SystemClock.elapsedRealtime() + msec, mRetryAlarmIntent);
208        }
209    }
210
211    private String getTimeString(long time) {
212        if (time <= 0) {
213            time = System.currentTimeMillis();
214        }
215
216        Time tobj = new Time();
217        tobj.set(time);
218        return String.format("%s.%s", tobj.format("%m-%d %H:%M:%S"), time % 1000);
219    }
220
221    public boolean isCompleted() {
222        return mCompleted;
223    }
224
225    @Override
226    public boolean equals(Object o) {
227        if (this == o) return true;
228        if (!(o instanceof PollingTask)) return false;
229
230        PollingTask that = (PollingTask)o;
231        return (this.mId == that.mId) && (this.mType == that.mType);
232    }
233
234    @Override
235    public String toString() {
236        StringBuilder sb = new StringBuilder();
237        sb.append("PollingTask { ");
238        sb.append("\nId: " + mId);
239        sb.append("\nType: " + mType);
240        sb.append("\nCurrentRetry: " + mCurrentRetry);
241        sb.append("\nTotalRetry: " + mTotalRetry);
242        sb.append("\nTimeUnit: " + mTimeUnit);
243        sb.append("\nLast update time: " + mLastUpdateTime + "(" +
244                getTimeString(mLastUpdateTime) + ")");
245        sb.append("\nContacts: " + mContacts.size());
246        for (int i = 0; i < mContacts.size(); i++) {
247            sb.append("\n");
248            Contacts.Item item = mContacts.get(i);
249            sb.append("Number " + i + ": " + Logger.hidePhoneNumberPii(item.number()));
250        }
251        sb.append("\nCompleted: " + mCompleted);
252        sb.append(" }");
253        return sb.toString();
254    }
255}
256
257