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.telephony.Rlog;
29import android.telephony.SubscriptionManager;
30
31/**
32 * Monitors the device and ICC storage, and sends the appropriate events.
33 *
34 * This code was formerly part of {@link SMSDispatcher}, and has been moved
35 * into a separate class to support instantiation of multiple SMSDispatchers on
36 * dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
37 */
38public class SmsStorageMonitor extends Handler {
39    private static final String TAG = "SmsStorageMonitor";
40
41    /** SIM/RUIM storage is full */
42    private static final int EVENT_ICC_FULL = 1;
43
44    /** Memory status reporting is acknowledged by RIL */
45    private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 2;
46
47    /** Radio is ON */
48    private static final int EVENT_RADIO_ON = 3;
49
50    /** Context from phone object passed to constructor. */
51    private final Context mContext;
52
53    /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
54    private PowerManager.WakeLock mWakeLock;
55
56    private boolean mReportMemoryStatusPending;
57
58    /** it is use to put in to extra value for SIM_FULL_ACTION and SMS_REJECTED_ACTION */
59    Phone mPhone;
60
61    final CommandsInterface mCi;                            // accessed from inner class
62    boolean mStorageAvailable = true;                       // accessed from inner class
63
64    /**
65     * Hold the wake lock for 5 seconds, which should be enough time for
66     * any receiver(s) to grab its own wake lock.
67     */
68    private static final int WAKE_LOCK_TIMEOUT = 5000;
69
70    /**
71     * Creates an SmsStorageMonitor and registers for events.
72     * @param phone the Phone to use
73     */
74    public SmsStorageMonitor(Phone phone) {
75        mPhone = phone;
76        mContext = phone.getContext();
77        mCi = phone.mCi;
78
79        createWakelock();
80
81        mCi.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
82        mCi.registerForOn(this, EVENT_RADIO_ON, null);
83
84        // Register for device storage intents.  Use these to notify the RIL
85        // that storage for SMS is or is not available.
86        IntentFilter filter = new IntentFilter();
87        filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
88        filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
89        mContext.registerReceiver(mResultReceiver, filter);
90    }
91
92    public void dispose() {
93        mCi.unSetOnIccSmsFull(this);
94        mCi.unregisterForOn(this);
95        mContext.unregisterReceiver(mResultReceiver);
96    }
97
98    /**
99     * Handles events coming from the phone stack. Overridden from handler.
100     * @param msg the message to handle
101     */
102    @Override
103    public void handleMessage(Message msg) {
104        AsyncResult ar;
105
106        switch (msg.what) {
107            case EVENT_ICC_FULL:
108                handleIccFull();
109                break;
110
111            case EVENT_REPORT_MEMORY_STATUS_DONE:
112                ar = (AsyncResult) msg.obj;
113                if (ar.exception != null) {
114                    mReportMemoryStatusPending = true;
115                    Rlog.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
116                            + mStorageAvailable);
117                } else {
118                    mReportMemoryStatusPending = false;
119                }
120                break;
121
122            case EVENT_RADIO_ON:
123                if (mReportMemoryStatusPending) {
124                    Rlog.v(TAG, "Sending pending memory status report : mStorageAvailable = "
125                            + mStorageAvailable);
126                    mCi.reportSmsMemoryStatus(mStorageAvailable,
127                            obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
128                }
129                break;
130        }
131    }
132
133    private void createWakelock() {
134        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
135        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SmsStorageMonitor");
136        mWakeLock.setReferenceCounted(true);
137    }
138
139    /**
140     * Called when SIM_FULL message is received from the RIL.  Notifies interested
141     * parties that SIM storage for SMS messages is full.
142     */
143    private void handleIccFull() {
144        // broadcast SIM_FULL intent
145        Intent intent = new Intent(Intents.SIM_FULL_ACTION);
146        mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
147        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
148        mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
149    }
150
151    /** Returns whether or not there is storage available for an incoming SMS. */
152    public boolean isStorageAvailable() {
153        return mStorageAvailable;
154    }
155
156    private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
157        @Override
158        public void onReceive(Context context, Intent intent) {
159            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
160                mStorageAvailable = false;
161                mCi.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
162            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
163                mStorageAvailable = true;
164                mCi.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
165            }
166        }
167    };
168}
169