1/*
2 * Copyright (C) 2015 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.messaging.sms;
18
19import android.content.res.Resources;
20
21import com.android.messaging.Factory;
22import com.android.messaging.R;
23import com.android.messaging.datamodel.SyncManager;
24import com.android.messaging.util.BugleGservices;
25import com.android.messaging.util.BugleGservicesKeys;
26import com.android.messaging.util.LogUtil;
27
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30
31/**
32 * Class handling message cleanup when storage is low
33 */
34public class SmsReleaseStorage {
35    /**
36     * Class representing a time duration specified by Gservices
37     */
38    public static class Duration {
39        // Time duration unit types
40        public static final int UNIT_WEEK = 'w';
41        public static final int UNIT_MONTH = 'm';
42        public static final int UNIT_YEAR = 'y';
43
44        // Number of units
45        public final int mCount;
46        // Unit type: week, month or year
47        public final int mUnit;
48
49        public Duration(final int count, final int unit) {
50            mCount = count;
51            mUnit = unit;
52        }
53    }
54
55    private static final String TAG = LogUtil.BUGLE_TAG;
56
57    private static final Duration DEFAULT_DURATION = new Duration(1, Duration.UNIT_MONTH);
58
59    private static final Pattern DURATION_PATTERN = Pattern.compile("([1-9]+\\d*)(w|m|y)");
60    /**
61     * Parse message retaining time duration specified by Gservices
62     *
63     * @return The parsed time duration from Gservices
64     */
65    public static Duration parseMessageRetainingDuration() {
66        final String smsAutoDeleteMessageRetainingDuration =
67                BugleGservices.get().getString(
68                        BugleGservicesKeys.SMS_STORAGE_PURGING_MESSAGE_RETAINING_DURATION,
69                        BugleGservicesKeys.SMS_STORAGE_PURGING_MESSAGE_RETAINING_DURATION_DEFAULT);
70        final Matcher matcher = DURATION_PATTERN.matcher(smsAutoDeleteMessageRetainingDuration);
71        try {
72            if (matcher.matches()) {
73                return new Duration(
74                        Integer.parseInt(matcher.group(1)),
75                        matcher.group(2).charAt(0));
76            }
77        } catch (final NumberFormatException e) {
78            // Nothing to do
79        }
80        LogUtil.e(TAG, "SmsAutoDelete: invalid duration " +
81                smsAutoDeleteMessageRetainingDuration);
82        return DEFAULT_DURATION;
83    }
84
85    /**
86     * Get string representation of the time duration
87     *
88     * @param duration
89     * @return
90     */
91    public static String getMessageRetainingDurationString(final Duration duration) {
92        final Resources resources = Factory.get().getApplicationContext().getResources();
93        switch (duration.mUnit) {
94            case Duration.UNIT_WEEK:
95                return resources.getQuantityString(
96                        R.plurals.week_count, duration.mCount, duration.mCount);
97            case Duration.UNIT_MONTH:
98                return resources.getQuantityString(
99                        R.plurals.month_count, duration.mCount, duration.mCount);
100            case Duration.UNIT_YEAR:
101                return resources.getQuantityString(
102                        R.plurals.year_count, duration.mCount, duration.mCount);
103        }
104        throw new IllegalArgumentException(
105                "SmsAutoDelete: invalid duration unit " + duration.mUnit);
106    }
107
108    // Time conversations
109    private static final long WEEK_IN_MILLIS = 7 * 24 * 3600 * 1000L;
110    private static final long MONTH_IN_MILLIS = 30 * 24 * 3600 * 1000L;
111    private static final long YEAR_IN_MILLIS = 365 * 24 * 3600 * 1000L;
112
113    /**
114     * Convert time duration to time in milliseconds
115     *
116     * @param duration
117     * @return
118     */
119    public static long durationToTimeInMillis(final Duration duration) {
120        switch (duration.mUnit) {
121            case Duration.UNIT_WEEK:
122                return duration.mCount * WEEK_IN_MILLIS;
123            case Duration.UNIT_MONTH:
124                return duration.mCount * MONTH_IN_MILLIS;
125            case Duration.UNIT_YEAR:
126                return duration.mCount * YEAR_IN_MILLIS;
127        }
128        return -1L;
129    }
130
131    /**
132     * Delete message actions:
133     * 0: delete media messages
134     * 1: delete old messages
135     *
136     * @param actionIndex The index of the delete action to perform
137     * @param durationInMillis The time duration for retaining messages
138     */
139    public static void deleteMessages(final int actionIndex, final long durationInMillis) {
140        int deleted = 0;
141        switch (actionIndex) {
142            case 0: {
143                // Delete media
144                deleted = MmsUtils.deleteMediaMessages();
145                break;
146            }
147            case 1: {
148                // Delete old messages
149                final long now = System.currentTimeMillis();
150                final long cutOffTimestampInMillis = now - durationInMillis;
151                // Delete messages from telephony provider
152                deleted = MmsUtils.deleteMessagesOlderThan(cutOffTimestampInMillis);
153                break;
154            }
155            default: {
156                LogUtil.e(TAG, "SmsStorageStatusManager: invalid action " + actionIndex);
157                break;
158            }
159        }
160
161        if (deleted > 0) {
162            // Kick off a sync to update local db.
163            SyncManager.sync();
164        }
165    }
166}
167