1 /*
2  * Copyright (C) 2016 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 
17 package com.android.cellbroadcastreceiver;
18 
19 import android.content.Context;
20 import android.os.PersistableBundle;
21 import android.telephony.CarrierConfigManager;
22 import android.util.Log;
23 import android.util.SparseArray;
24 
25 import com.android.cellbroadcastreceiver.CellBroadcastAlertAudio.ToneType;
26 
27 import java.util.ArrayList;
28 
29 /**
30  * CellBroadcastOtherChannelsManager handles the additional cell broadcast channels that
31  * carriers might enable through carrier config app.
32  * Syntax: "<channel id range>:type=<tone type>"
33  * For example,
34  * <string-array name="carrier_additional_cbs_channels_strings" num="3">
35  *     <item value="43008:type=earthquake" />
36  *     <item value="0xAFEE:type=tsunami" />
37  *     <item value="0xAC00-0xAFED:type=other" />
38  *     <item value="1234-5678" />
39  * </string-array>
40  * If no tones are specified, the tone type will be set to CMAS_DEFAULT.
41  */
42 public class CellBroadcastOtherChannelsManager {
43 
44     private static final String TAG = "CellBroadcastOtherChannelsManager";
45 
46     private static CellBroadcastOtherChannelsManager sInstance = null;
47 
48     /**
49      * Channel range caches with sub id as the key.
50      */
51     private static SparseArray<ArrayList<CellBroadcastChannelRange>> sChannelRanges =
52             new SparseArray<>();
53 
54     /**
55      * Cell broadcast channel range
56      * A range is consisted by starting channel id, ending channel id, and the tone type
57      */
58     public static class CellBroadcastChannelRange {
59 
60         private static final String KEY_TYPE = "type";
61         private static final String KEY_EMERGENCY = "emergency";
62 
63         public int mStartId;
64         public int mEndId;
65         public ToneType mToneType;
66         public boolean mIsEmergency;
67 
CellBroadcastChannelRange(String channelRange)68         public CellBroadcastChannelRange(String channelRange) throws Exception {
69 
70             mToneType = ToneType.CMAS_DEFAULT;
71             mIsEmergency = false;
72 
73             int colonIndex = channelRange.indexOf(':');
74             if (colonIndex != -1){
75                 // Parse the tone type and emergency flag
76                 String[] pairs = channelRange.substring(colonIndex + 1).trim().split(",");
77                 for (String pair : pairs) {
78                     pair = pair.trim();
79                     String[] tokens = pair.split("=");
80                     if (tokens.length == 2) {
81                         String key = tokens[0].trim();
82                         String value = tokens[1].trim();
83                         switch (key) {
84                             case KEY_TYPE:
85                                 mToneType = ToneType.valueOf(value.toUpperCase());
86                                 break;
87                             case KEY_EMERGENCY:
88                                 mIsEmergency = value.equalsIgnoreCase("true");
89                                 break;
90                         }
91                     }
92                 }
93                 channelRange = channelRange.substring(0, colonIndex).trim();
94             }
95 
96             // Parse the channel range
97             int dashIndex = channelRange.indexOf('-');
98             if (dashIndex != -1) {
99                 // range that has start id and end id
100                 mStartId = Integer.decode(channelRange.substring(0, dashIndex).trim());
101                 mEndId = Integer.decode(channelRange.substring(dashIndex + 1).trim());
102             } else {
103                 // Not a range, only a single id
104                 mStartId = mEndId = Integer.decode(channelRange);
105             }
106         }
107     }
108 
109     /**
110      * Get the instance of the cell broadcast other channel manager
111      * @return The singleton instance
112      */
getInstance()113     public static CellBroadcastOtherChannelsManager getInstance() {
114         if (sInstance == null) {
115             sInstance = new CellBroadcastOtherChannelsManager();
116         }
117         return sInstance;
118     }
119 
120     /**
121      * Get cell broadcast channels enabled by the carriers.
122      * @param context Application context
123      * @param subId Subscription id
124      * @return The list of channel ranges enabled by the carriers.
125      */
getCellBroadcastChannelRanges( Context context, int subId)126      public ArrayList<CellBroadcastChannelRange> getCellBroadcastChannelRanges(
127             Context context, int subId) {
128 
129         // Check if the cache already had it.
130         if (sChannelRanges.get(subId) == null) {
131 
132             if (context == null) {
133                 loge("context is null");
134                 return null;
135             }
136 
137             ArrayList<CellBroadcastChannelRange> result = new ArrayList<>();
138             String[] ranges;
139             CarrierConfigManager configManager =
140                     (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
141 
142             if (configManager != null) {
143                 PersistableBundle carrierConfig = configManager.getConfigForSubId(subId);
144 
145                 if (carrierConfig != null) {
146                     ranges = carrierConfig.getStringArray(
147                             CarrierConfigManager.KEY_CARRIER_ADDITIONAL_CBS_CHANNELS_STRINGS);
148 
149                     if (ranges == null || ranges.length == 0) {
150                         log("No additional channels configured. subId = " + subId);
151 
152                         // If there is nothing configured, store an empty list in the cache
153                         // so we won't look up again next time.
154                         sChannelRanges.put(subId, result);
155                         return result;
156                     }
157 
158                     for (String range : ranges) {
159                         try {
160                             result.add(new CellBroadcastChannelRange(range));
161                         } catch (Exception e) {
162                             loge("Failed to parse \"" + range + "\". e=" + e);
163                         }
164                     }
165 
166                     sChannelRanges.put(subId, result);
167 
168                 } else {
169                     loge("Can't get carrier config. subId=" + subId);
170                     return null;
171                 }
172             } else {
173                 loge("Carrier config manager is not available");
174                 return null;
175             }
176         }
177 
178         return sChannelRanges.get(subId);
179     }
180 
log(String msg)181     private static void log(String msg) {
182         Log.d(TAG, msg);
183     }
184 
loge(String msg)185     private static void loge(String msg) {
186         Log.e(TAG, msg);
187     }
188 }
189