/telephony/java/com/android/internal/telephony/cat/IconLoader.java

http://github.com/CyanogenMod/android_frameworks_base · Java · 362 lines · 250 code · 37 blank · 75 comment · 45 complexity · f43e9572f49d924837bef035b6ad5fe9 MD5 · raw file

  1. /*
  2. * Copyright (C) 2006 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. package com.android.internal.telephony.cat;
  17. import com.android.internal.telephony.IccFileHandler;
  18. import android.graphics.Bitmap;
  19. import android.graphics.Color;
  20. import android.os.AsyncResult;
  21. import android.os.Handler;
  22. import android.os.HandlerThread;
  23. import android.os.Looper;
  24. import android.os.Message;
  25. import android.util.Log;
  26. import java.util.HashMap;
  27. /**
  28. * Class for loading icons from the SIM card. Has two states: single, for loading
  29. * one icon. Multi, for loading icons list.
  30. *
  31. */
  32. class IconLoader extends Handler {
  33. // members
  34. private int mState = STATE_SINGLE_ICON;
  35. private ImageDescriptor mId = null;
  36. private Bitmap mCurrentIcon = null;
  37. private int mRecordNumber;
  38. private IccFileHandler mSimFH = null;
  39. private Message mEndMsg = null;
  40. private byte[] mIconData = null;
  41. // multi icons state members
  42. private int[] mRecordNumbers = null;
  43. private int mCurrentRecordIndex = 0;
  44. private Bitmap[] mIcons = null;
  45. private HashMap<Integer, Bitmap> mIconsCache = null;
  46. private static IconLoader sLoader = null;
  47. // Loader state values.
  48. private static final int STATE_SINGLE_ICON = 1;
  49. private static final int STATE_MULTI_ICONS = 2;
  50. // Finished loading single record from a linear-fixed EF-IMG.
  51. private static final int EVENT_READ_EF_IMG_RECOED_DONE = 1;
  52. // Finished loading single icon from a Transparent DF-Graphics.
  53. private static final int EVENT_READ_ICON_DONE = 2;
  54. // Finished loading single colour icon lookup table.
  55. private static final int EVENT_READ_CLUT_DONE = 3;
  56. // Color lookup table offset inside the EF.
  57. private static final int CLUT_LOCATION_OFFSET = 4;
  58. // CLUT entry size, {Red, Green, Black}
  59. private static final int CLUT_ENTRY_SIZE = 3;
  60. private IconLoader(Looper looper , IccFileHandler fh) {
  61. super(looper);
  62. mSimFH = fh;
  63. mIconsCache = new HashMap<Integer, Bitmap>(50);
  64. }
  65. static IconLoader getInstance(Handler caller, IccFileHandler fh) {
  66. if (sLoader != null) {
  67. return sLoader;
  68. }
  69. if (fh != null) {
  70. HandlerThread thread = new HandlerThread("Cat Icon Loader");
  71. thread.start();
  72. return new IconLoader(thread.getLooper(), fh);
  73. }
  74. return null;
  75. }
  76. void loadIcons(int[] recordNumbers, Message msg) {
  77. if (recordNumbers == null || recordNumbers.length == 0 || msg == null) {
  78. return;
  79. }
  80. mEndMsg = msg;
  81. // initialize multi icons load variables.
  82. mIcons = new Bitmap[recordNumbers.length];
  83. mRecordNumbers = recordNumbers;
  84. mCurrentRecordIndex = 0;
  85. mState = STATE_MULTI_ICONS;
  86. startLoadingIcon(recordNumbers[0]);
  87. }
  88. void loadIcon(int recordNumber, Message msg) {
  89. if (msg == null) {
  90. return;
  91. }
  92. mEndMsg = msg;
  93. mState = STATE_SINGLE_ICON;
  94. startLoadingIcon(recordNumber);
  95. }
  96. private void startLoadingIcon(int recordNumber) {
  97. // Reset the load variables.
  98. mId = null;
  99. mIconData = null;
  100. mCurrentIcon = null;
  101. mRecordNumber = recordNumber;
  102. // make sure the icon was not already loaded and saved in the local cache.
  103. if (mIconsCache.containsKey(recordNumber)) {
  104. mCurrentIcon = mIconsCache.get(recordNumber);
  105. postIcon();
  106. return;
  107. }
  108. // start the first phase ==> loading Image Descriptor.
  109. readId();
  110. }
  111. @Override
  112. public void handleMessage(Message msg) {
  113. AsyncResult ar;
  114. try {
  115. switch (msg.what) {
  116. case EVENT_READ_EF_IMG_RECOED_DONE:
  117. ar = (AsyncResult) msg.obj;
  118. if (handleImageDescriptor((byte[]) ar.result)) {
  119. readIconData();
  120. } else {
  121. throw new Exception("Unable to parse image descriptor");
  122. }
  123. break;
  124. case EVENT_READ_ICON_DONE:
  125. ar = (AsyncResult) msg.obj;
  126. byte[] rawData = ((byte[]) ar.result);
  127. if (mId.codingScheme == ImageDescriptor.CODING_SCHEME_BASIC) {
  128. mCurrentIcon = parseToBnW(rawData, rawData.length);
  129. mIconsCache.put(mRecordNumber, mCurrentIcon);
  130. postIcon();
  131. } else if (mId.codingScheme == ImageDescriptor.CODING_SCHEME_COLOUR) {
  132. mIconData = rawData;
  133. readClut();
  134. }
  135. break;
  136. case EVENT_READ_CLUT_DONE:
  137. ar = (AsyncResult) msg.obj;
  138. byte [] clut = ((byte[]) ar.result);
  139. mCurrentIcon = parseToRGB(mIconData, mIconData.length,
  140. false, clut);
  141. mIconsCache.put(mRecordNumber, mCurrentIcon);
  142. postIcon();
  143. break;
  144. }
  145. } catch (Exception e) {
  146. CatLog.d(this, "Icon load failed");
  147. // post null icon back to the caller.
  148. postIcon();
  149. }
  150. }
  151. /**
  152. * Handles Image descriptor parsing and required processing. This is the
  153. * first step required to handle retrieving icons from the SIM.
  154. *
  155. * @param data byte [] containing Image Instance descriptor as defined in
  156. * TS 51.011.
  157. */
  158. private boolean handleImageDescriptor(byte[] rawData) {
  159. mId = ImageDescriptor.parse(rawData, 1);
  160. if (mId == null) {
  161. return false;
  162. }
  163. return true;
  164. }
  165. // Start reading colour lookup table from SIM card.
  166. private void readClut() {
  167. int length = mIconData[3] * CLUT_ENTRY_SIZE;
  168. Message msg = this.obtainMessage(EVENT_READ_CLUT_DONE);
  169. mSimFH.loadEFImgTransparent(mId.imageId,
  170. mIconData[CLUT_LOCATION_OFFSET],
  171. mIconData[CLUT_LOCATION_OFFSET + 1], length, msg);
  172. }
  173. // Start reading Image Descriptor from SIM card.
  174. private void readId() {
  175. if (mRecordNumber < 0) {
  176. mCurrentIcon = null;
  177. postIcon();
  178. return;
  179. }
  180. Message msg = this.obtainMessage(EVENT_READ_EF_IMG_RECOED_DONE);
  181. mSimFH.loadEFImgLinearFixed(mRecordNumber, msg);
  182. }
  183. // Start reading icon bytes array from SIM card.
  184. private void readIconData() {
  185. Message msg = this.obtainMessage(EVENT_READ_ICON_DONE);
  186. mSimFH.loadEFImgTransparent(mId.imageId, 0, 0, mId.length ,msg);
  187. }
  188. // When all is done pass icon back to caller.
  189. private void postIcon() {
  190. if (mState == STATE_SINGLE_ICON) {
  191. mEndMsg.obj = mCurrentIcon;
  192. mEndMsg.sendToTarget();
  193. } else if (mState == STATE_MULTI_ICONS) {
  194. mIcons[mCurrentRecordIndex++] = mCurrentIcon;
  195. // If not all icons were loaded, start loading the next one.
  196. if (mCurrentRecordIndex < mRecordNumbers.length) {
  197. startLoadingIcon(mRecordNumbers[mCurrentRecordIndex]);
  198. } else {
  199. mEndMsg.obj = mIcons;
  200. mEndMsg.sendToTarget();
  201. }
  202. }
  203. }
  204. /**
  205. * Convert a TS 131.102 image instance of code scheme '11' into Bitmap
  206. * @param data The raw data
  207. * @param length The length of image body
  208. * @return The bitmap
  209. */
  210. public static Bitmap parseToBnW(byte[] data, int length){
  211. int valueIndex = 0;
  212. int width = data[valueIndex++] & 0xFF;
  213. int height = data[valueIndex++] & 0xFF;
  214. int numOfPixels = width*height;
  215. int[] pixels = new int[numOfPixels];
  216. int pixelIndex = 0;
  217. int bitIndex = 7;
  218. byte currentByte = 0x00;
  219. while (pixelIndex < numOfPixels) {
  220. // reassign data and index for every byte (8 bits).
  221. if (pixelIndex % 8 == 0) {
  222. currentByte = data[valueIndex++];
  223. bitIndex = 7;
  224. }
  225. pixels[pixelIndex++] = bitToBnW((currentByte >> bitIndex-- ) & 0x01);
  226. }
  227. if (pixelIndex != numOfPixels) {
  228. CatLog.d("IconLoader", "parseToBnW; size error");
  229. }
  230. return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
  231. }
  232. /**
  233. * Decode one bit to a black and white color:
  234. * 0 is black
  235. * 1 is white
  236. * @param bit to decode
  237. * @return RGB color
  238. */
  239. private static int bitToBnW(int bit){
  240. if(bit == 1){
  241. return Color.WHITE;
  242. } else {
  243. return Color.BLACK;
  244. }
  245. }
  246. /**
  247. * a TS 131.102 image instance of code scheme '11' into color Bitmap
  248. *
  249. * @param data The raw data
  250. * @param length the length of image body
  251. * @param transparency with or without transparency
  252. * @param clut coulor lookup table
  253. * @return The color bitmap
  254. */
  255. public static Bitmap parseToRGB(byte[] data, int length,
  256. boolean transparency, byte[] clut) {
  257. int valueIndex = 0;
  258. int width = data[valueIndex++] & 0xFF;
  259. int height = data[valueIndex++] & 0xFF;
  260. int bitsPerImg = data[valueIndex++] & 0xFF;
  261. int numOfClutEntries = data[valueIndex++] & 0xFF;
  262. if (true == transparency) {
  263. clut[numOfClutEntries - 1] = Color.TRANSPARENT;
  264. }
  265. int numOfPixels = width * height;
  266. int[] pixels = new int[numOfPixels];
  267. valueIndex = 6;
  268. int pixelIndex = 0;
  269. int bitsStartOffset = 8 - bitsPerImg;
  270. int bitIndex = bitsStartOffset;
  271. byte currentByte = data[valueIndex++];
  272. int mask = getMask(bitsPerImg);
  273. boolean bitsOverlaps = (8 % bitsPerImg == 0);
  274. while (pixelIndex < numOfPixels) {
  275. // reassign data and index for every byte (8 bits).
  276. if (bitIndex < 0) {
  277. currentByte = data[valueIndex++];
  278. bitIndex = bitsOverlaps ? (bitsStartOffset) : (bitIndex * -1);
  279. }
  280. int clutEntry = ((currentByte >> bitIndex) & mask);
  281. int clutIndex = clutEntry * CLUT_ENTRY_SIZE;
  282. pixels[pixelIndex++] = Color.rgb(clut[clutIndex],
  283. clut[clutIndex + 1], clut[clutIndex + 2]);
  284. bitIndex -= bitsPerImg;
  285. }
  286. return Bitmap.createBitmap(pixels, width, height,
  287. Bitmap.Config.ARGB_8888);
  288. }
  289. /**
  290. * Calculate bit mask for a given number of bits. The mask should enable to
  291. * make a bitwise and to the given number of bits.
  292. * @param numOfBits number of bits to calculate mask for.
  293. * @return bit mask
  294. */
  295. private static int getMask(int numOfBits) {
  296. int mask = 0x00;
  297. switch (numOfBits) {
  298. case 1:
  299. mask = 0x01;
  300. break;
  301. case 2:
  302. mask = 0x03;
  303. break;
  304. case 3:
  305. mask = 0x07;
  306. break;
  307. case 4:
  308. mask = 0x0F;
  309. break;
  310. case 5:
  311. mask = 0x1F;
  312. break;
  313. case 6:
  314. mask = 0x3F;
  315. break;
  316. case 7:
  317. mask = 0x7F;
  318. break;
  319. case 8:
  320. mask = 0xFF;
  321. break;
  322. }
  323. return mask;
  324. }
  325. }