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