1/*
2 * Copyright (C) 2008 Esmertec AG.
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mms.util;
19
20import com.android.mms.LogTag;
21
22import android.content.BroadcastReceiver;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.database.Cursor;
28import android.database.sqlite.SqliteWrapper;
29import android.provider.Telephony.Mms.Rate;
30import android.util.Log;
31
32public class RateController {
33    private static final String TAG = LogTag.TAG;
34    private static final boolean DEBUG = false;
35    private static final boolean LOCAL_LOGV = false;
36
37    private static final int RATE_LIMIT = 100;
38    private static final long ONE_HOUR = 1000 * 60 * 60;
39
40    private static final int NO_ANSWER  = 0;
41    private static final int ANSWER_YES = 1;
42    private static final int ANSWER_NO  = 2;
43
44    public static final int ANSWER_TIMEOUT = 20000;
45    public static final String RATE_LIMIT_SURPASSED_ACTION =
46        "com.android.mms.RATE_LIMIT_SURPASSED";
47    public static final String RATE_LIMIT_CONFIRMED_ACTION =
48        "com.android.mms.RATE_LIMIT_CONFIRMED";
49
50    private static RateController sInstance;
51    private static boolean sMutexLock;
52
53    private final Context mContext;
54    private int mAnswer;
55
56    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
57        @Override
58        public void onReceive(Context context, Intent intent) {
59            if (LOCAL_LOGV) {
60                Log.v(TAG, "Intent received: " + intent);
61            }
62
63            if (RATE_LIMIT_CONFIRMED_ACTION.equals(intent.getAction())) {
64                synchronized (this) {
65                    mAnswer = intent.getBooleanExtra("answer", false)
66                                            ? ANSWER_YES : ANSWER_NO;
67                    notifyAll();
68                }
69            }
70        }
71    };
72
73    private RateController(Context context) {
74        mContext = context;
75    }
76
77    public static void init(Context context) {
78        if (LOCAL_LOGV) {
79            Log.v(TAG, "RateController.init()");
80        }
81
82        if (sInstance != null) {
83            Log.w(TAG, "Already initialized.");
84            return;
85        }
86        sInstance = new RateController(context);
87    }
88
89    public static RateController getInstance() {
90        if (sInstance == null) {
91            throw new IllegalStateException("Uninitialized.");
92        }
93        return sInstance;
94    }
95
96    public final void update() {
97        ContentValues values = new ContentValues(1);
98        values.put(Rate.SENT_TIME, System.currentTimeMillis());
99        SqliteWrapper.insert(mContext, mContext.getContentResolver(),
100                             Rate.CONTENT_URI, values);
101    }
102
103    public final boolean isLimitSurpassed() {
104        long oneHourAgo = System.currentTimeMillis() - ONE_HOUR;
105        Cursor c = SqliteWrapper.query(mContext, mContext.getContentResolver(),
106                Rate.CONTENT_URI, new String[] { "COUNT(*) AS rate" },
107                Rate.SENT_TIME + ">" + oneHourAgo, null, null);
108        if (c != null) {
109            try {
110                if (c.moveToFirst()) {
111                    return c.getInt(0) >= RATE_LIMIT;
112                }
113            } finally {
114                c.close();
115            }
116        }
117        return false;
118    }
119
120    synchronized public boolean isAllowedByUser() {
121        while (sMutexLock) {
122            try {
123                wait();
124            } catch (InterruptedException _) {
125                 // Ignore it.
126            }
127        }
128        sMutexLock = true;
129
130        mContext.registerReceiver(mBroadcastReceiver,
131                new IntentFilter(RATE_LIMIT_CONFIRMED_ACTION));
132
133        mAnswer = NO_ANSWER;
134        try {
135            Intent intent = new Intent(RATE_LIMIT_SURPASSED_ACTION);
136            // Using NEW_TASK here is necessary because we're calling
137            // startActivity from outside an activity.
138            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
139            mContext.startActivity(intent);
140            return waitForAnswer() == ANSWER_YES;
141        } finally {
142            mContext.unregisterReceiver(mBroadcastReceiver);
143            sMutexLock = false;
144            notifyAll();
145        }
146    }
147
148    synchronized private int waitForAnswer() {
149        for (int t = 0; (mAnswer == NO_ANSWER) && (t < ANSWER_TIMEOUT); t += 1000) {
150            try {
151                if (LOCAL_LOGV) {
152                    Log.v(TAG, "Waiting for answer..." + t / 1000);
153                }
154                wait(1000L);
155            } catch (InterruptedException _) {
156                 // Ignore it.
157            }
158        }
159        return mAnswer;
160    }
161}
162