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 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 */ 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 */ 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 181 private static void log(String msg) { 182 Log.d(TAG, msg); 183 } 184 185 private static void loge(String msg) { 186 Log.e(TAG, msg); 187 } 188 } 189