1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.os.AsyncResult;
24import android.os.Handler;
25import android.os.Message;
26import android.os.PowerManager;
27import android.provider.Telephony.Sms.Intents;
28import android.util.Log;
29
30/**
31 * Monitors the device and ICC storage, and sends the appropriate events.
32 *
33 * This code was formerly part of {@link SMSDispatcher}, and has been moved
34 * into a separate class to support instantiation of multiple SMSDispatchers on
35 * dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
36 */
37public final class SmsStorageMonitor extends Handler {
38    private static final String TAG = "SmsStorageMonitor";
39
40    /** SIM/RUIM storage is full */
41    private static final int EVENT_ICC_FULL = 1;
42
43    /** Memory status reporting is acknowledged by RIL */
44    private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 2;
45
46    /** Radio is ON */
47    private static final int EVENT_RADIO_ON = 3;
48
49    /** Context from phone object passed to constructor. */
50    private final Context mContext;
51
52    /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
53    private PowerManager.WakeLock mWakeLock;
54
55    private boolean mReportMemoryStatusPending;
56
57    final CommandsInterface mCm;                            // accessed from inner class
58    boolean mStorageAvailable = true;                       // accessed from inner class
59
60    /**
61     * Hold the wake lock for 5 seconds, which should be enough time for
62     * any receiver(s) to grab its own wake lock.
63     */
64    private static final int WAKE_LOCK_TIMEOUT = 5000;
65
66    /**
67     * Creates an SmsStorageMonitor and registers for events.
68     * @param phone the Phone to use
69     */
70    public SmsStorageMonitor(PhoneBase phone) {
71        mContext = phone.getContext();
72        mCm = phone.mCM;
73
74        createWakelock();
75
76        mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
77        mCm.registerForOn(this, EVENT_RADIO_ON, null);
78
79        // Register for device storage intents.  Use these to notify the RIL
80        // that storage for SMS is or is not available.
81        IntentFilter filter = new IntentFilter();
82        filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
83        filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
84        mContext.registerReceiver(mResultReceiver, filter);
85    }
86
87    public void dispose() {
88        mCm.unSetOnIccSmsFull(this);
89        mCm.unregisterForOn(this);
90        mContext.unregisterReceiver(mResultReceiver);
91    }
92
93    /**
94     * Handles events coming from the phone stack. Overridden from handler.
95     * @param msg the message to handle
96     */
97    @Override
98    public void handleMessage(Message msg) {
99        AsyncResult ar;
100
101        switch (msg.what) {
102            case EVENT_ICC_FULL:
103                handleIccFull();
104                break;
105
106            case EVENT_REPORT_MEMORY_STATUS_DONE:
107                ar = (AsyncResult) msg.obj;
108                if (ar.exception != null) {
109                    mReportMemoryStatusPending = true;
110                    Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
111                            + mStorageAvailable);
112                } else {
113                    mReportMemoryStatusPending = false;
114                }
115                break;
116
117            case EVENT_RADIO_ON:
118                if (mReportMemoryStatusPending) {
119                    Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
120                            + mStorageAvailable);
121                    mCm.reportSmsMemoryStatus(mStorageAvailable,
122                            obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
123                }
124                break;
125        }
126    }
127
128    private void createWakelock() {
129        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
130        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SmsStorageMonitor");
131        mWakeLock.setReferenceCounted(true);
132    }
133
134    /**
135     * Called when SIM_FULL message is received from the RIL.  Notifies interested
136     * parties that SIM storage for SMS messages is full.
137     */
138    private void handleIccFull() {
139        // broadcast SIM_FULL intent
140        Intent intent = new Intent(Intents.SIM_FULL_ACTION);
141        mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
142        mContext.sendBroadcast(intent, SMSDispatcher.RECEIVE_SMS_PERMISSION);
143    }
144
145    /** Returns whether or not there is storage available for an incoming SMS. */
146    public boolean isStorageAvailable() {
147        return mStorageAvailable;
148    }
149
150    private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
151        @Override
152        public void onReceive(Context context, Intent intent) {
153            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
154                mStorageAvailable = false;
155                mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
156            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
157                mStorageAvailable = true;
158                mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
159            }
160        }
161    };
162}
163