diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch) | |
tree | 7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /telephony/java | |
parent | e70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff) | |
download | frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'telephony/java')
56 files changed, 4668 insertions, 3462 deletions
diff --git a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java new file mode 100644 index 0000000..8a82966 --- /dev/null +++ b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.text.Editable; + +/* + * Japanese Phone number formatting rule is a bit complicated. + * Here are some valid examples: + * + * 022-229-1234 0223-23-1234 022-301-9876 015-482-7849 0154-91-3478 + * 01547-5-4534 090-1234-1234 080-0123-6789 + * 0800-000-9999 0570-000-000 0276-00-0000 + * + * As you can see, there is no straight-forward rule here. + * In order to handle this, a big array is prepared. + */ +/* package */ class JapanesePhoneNumberFormatter { + private static short FORMAT_MAP[] = { + -100, 10, 220, -15, 410, 530, -15, 670, 780, 1060, + -100, -25, 20, 40, 70, 100, 150, 190, 200, 210, + -36, -100, -100, -35, -35, -35, 30, -100, -100, -100, + -35, -35, -35, -35, -35, -35, -35, -45, -35, -35, + -100, -100, -100, -35, -35, -35, -35, 50, -35, 60, + -35, -35, -45, -35, -45, -35, -35, -45, -35, -35, + -35, -35, -45, -35, -35, -35, -35, -45, -45, -35, + -100, -100, -35, -35, -35, 80, 90, -100, -100, -100, + -35, -35, -35, -35, -35, -35, -45, -45, -35, -35, + -35, -35, -35, -35, -35, -35, -45, -35, -35, -35, + -25, -25, -35, -35, 110, 120, 130, -35, 140, -25, + -35, -25, -35, -35, -35, -35, -35, -45, -25, -35, + -35, -25, -35, -35, -35, -35, -35, -25, -45, -35, + -35, -35, -35, -35, -45, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -45, -45, -35, -35, + -100, -100, -35, 160, 170, 180, -35, -35, -100, -100, + -35, -35, -45, -35, -45, -45, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -45, -35, + -35, -35, -35, -35, -45, -45, -45, -35, -45, -35, + -25, -25, -35, -35, -35, -35, -35, -25, -35, -35, + -25, -25, -35, -35, -35, -35, -35, -35, -25, -25, + -25, -35, -35, -35, -35, -35, -25, -35, -35, -25, + -100, -100, 230, 250, 260, 270, 320, 340, 360, 390, + -35, -25, -25, 240, -35, -35, -35, -25, -35, -35, + -25, -35, -35, -35, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -35, -35, -35, -25, -35, -35, -25, + -35, -35, -35, -35, -35, -25, -35, -35, -35, -25, + -35, -25, -25, -25, -35, 280, 290, 300, 310, -35, + -25, -25, -25, -25, -25, -25, -25, -35, -35, -25, + -25, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -25, -25, -35, -35, -35, -25, -25, -25, -25, -25, + -25, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -25, -35, 330, -35, -35, -35, -35, -35, + -25, -35, -35, -35, -35, -35, -25, -25, -25, -25, + -35, -25, -25, -25, -35, -25, -35, -35, 350, -35, + -25, -35, -35, -35, -35, -35, -35, -35, -25, -25, + -35, -25, -35, 370, -35, -35, -25, -35, -35, 380, + -25, -35, -35, -25, -25, -35, -35, -35, -35, -35, + -25, -35, -25, -25, -25, -25, -35, -35, -35, -35, + -25, -35, -25, 400, -35, -35, -35, -35, -25, -35, + -25, -35, -35, -35, -35, -25, -25, -25, -25, -25, + -15, -15, 420, 460, -25, -25, 470, 480, 500, 510, + -15, -25, 430, -25, -25, -25, -25, -25, 440, 450, + -25, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -25, -25, -35, -35, -25, -25, -25, -35, -35, -35, + -15, -25, -15, -15, -15, -15, -15, -25, -25, -15, + -25, -25, -25, -25, -25, -25, -35, -25, -35, -35, + -35, -25, -25, -35, -25, -35, -35, -35, -25, -25, + 490, -15, -25, -25, -25, -35, -35, -25, -35, -35, + -15, -35, -35, -35, -35, -35, -35, -35, -35, -15, + -35, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -35, -35, -35, -25, -25, -25, 520, + -100, -100, -45, -100, -45, -100, -45, -100, -45, -100, + -25, -100, -25, 540, 580, 590, 600, 610, 630, 640, + -25, -35, -35, -35, -25, -25, -35, -35, -35, 550, + -35, -35, -25, -25, -25, -25, 560, 570, -25, -35, + -35, -35, -35, -35, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -35, -25, -25, -35, -25, -25, + -25, -25, -25, -25, -35, -35, -25, -35, -35, -25, + -35, -35, -25, -35, -35, -35, -35, -35, -35, -25, + -100, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -36, -100, -35, -35, -35, -35, 620, -35, -35, -100, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -45, + -25, -35, -25, -25, -35, -35, -35, -35, -25, -25, + -25, -25, -25, -25, -35, -35, -35, 650, -35, 660, + -35, -35, -35, -35, -45, -35, -35, -35, -35, -45, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -25, + -26, -100, 680, 690, 700, -25, 720, 730, -25, 740, + -25, -35, -25, -25, -25, -35, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -35, -35, -35, -35, -35, + -35, -100, -35, -35, -35, -35, 710, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -45, -35, + -25, -35, -25, -35, -25, -35, -35, -35, -35, -25, + -35, -35, -35, -35, -35, -25, -35, -25, -35, -35, + -35, -35, -25, -25, 750, 760, 770, -35, -35, -35, + -25, -35, -25, -25, -25, -25, -35, -35, -35, -25, + -25, -35, -35, -35, -35, -25, -25, -35, -35, -25, + -25, -35, -35, -35, -35, -35, -25, -25, -35, -35, + 790, -100, 800, 850, 900, 920, 940, 1030, 1040, 1050, + -36, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -35, -25, -25, -35, 810, -25, -35, -35, -25, 820, + -25, -35, -25, -25, -35, -35, -35, -35, -35, -25, + -25, -35, 830, -35, 840, -35, -25, -35, -35, -25, + -35, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -100, -25, -25, -25, -100, -100, -100, -100, -100, -100, + -25, -25, -35, -35, -35, -35, 860, -35, 870, 880, + -25, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -45, -45, -35, + -100, -100, -100, -100, -100, -100, 890, -100, -100, -100, + -25, -45, -45, -25, -45, -45, -25, -45, -45, -45, + -25, -25, -25, -25, -25, -35, -35, 910, -35, -25, + -35, -35, -35, -35, -35, -35, -35, -45, -35, -35, + -100, 930, -35, -35, -35, -35, -35, -35, -35, -35, + -100, -100, -45, -100, -45, -100, -100, -100, -100, -100, + -25, -25, -25, 950, -25, 970, 990, -35, 1000, 1010, + -35, -35, -35, -35, -35, -35, 960, -35, -35, -35, + -45, -45, -45, -45, -45, -45, -35, -45, -45, -45, + -35, -35, -25, -35, -35, 980, -35, -35, -35, -35, + -100, -100, -25, -25, -100, -100, -100, -100, -100, -100, + -25, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -25, -35, -35, -35, -35, -35, -35, -35, -35, -25, + -25, -35, -35, -35, -25, -25, -35, -35, -35, 1020, + -45, -45, -35, -35, -45, -45, -45, -45, -45, -45, + -25, -25, -25, -25, -25, -35, -25, -35, -25, -35, + -35, -25, -25, -35, -35, -35, -25, -35, -25, -35, + -25, -25, -35, -35, -35, -35, -35, -35, -35, -25, + -26, -100, 1070, 1080, 1090, 1110, 1120, 1130, 1140, 1160, + -35, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -35, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -35, -100, -35, -35, -35, -100, -35, -35, -35, 1100, + -35, -35, -35, -35, -35, -35, -45, -35, -35, -35, + -35, -25, -35, -25, -35, -35, -35, -35, -25, -35, + -25, -25, -25, -25, -35, -35, -35, -35, -35, -35, + -25, -25, -35, -35, -35, -25, -25, -35, -35, -35, + 1150, -25, -35, -35, -35, -35, -35, -35, -25, -25, + -35, -35, -45, -35, -35, -35, -35, -35, -35, -35, + -35, 1170, -25, -35, 1180, -35, 1190, -35, -25, -25, + -100, -100, -45, -45, -100, -100, -100, -100, -100, -100, + -25, -35, -35, -35, -35, -35, -35, -25, -25, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -45}; + + public static void format(Editable text) { + // Here, "root" means the position of "'": + // 0'3, 0'90, and +81'-90 + // (dash will be deleted soon, so it is actually +81'90). + int rootIndex = 1; + int length = text.length(); + if (length > 3 + && text.subSequence(0, 3).toString().equals("+81")) { + rootIndex = 3; + } else if (length < 1 || text.charAt(0) != '0') { + return; + } + + CharSequence saved = text.subSequence(0, length); + + // Strip the dashes first, as we're going to add them back + int i = 0; + while (i < text.length()) { + if (text.charAt(i) == '-') { + text.delete(i, i + 1); + } else { + i++; + } + } + + length = text.length(); + int dashposition; + + i = rootIndex; + int base = 0; + while (i < length) { + char ch = text.charAt(i); + if (!Character.isDigit(ch)) { + text.replace(0, length, saved); + return; + } + short value = FORMAT_MAP[base + ch - '0']; + if (value < 0) { + if (value <= -100) { + text.replace(0, length, saved); + return; + } + int dashPos2 = rootIndex + (Math.abs(value) % 10); + if (length > dashPos2) { + text.insert(dashPos2, "-"); + } + int dashPos1 = rootIndex + (Math.abs(value) / 10); + if (length > dashPos1) { + text.insert(dashPos1, "-"); + } + break; + } else { + base = value; + i++; + } + } + + if (length > 3 && rootIndex == 3) { + text.insert(rootIndex, "-"); + } + } +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/NeighboringCellInfo.aidl b/telephony/java/android/telephony/NeighboringCellInfo.aidl new file mode 100644 index 0000000..a7e709e --- /dev/null +++ b/telephony/java/android/telephony/NeighboringCellInfo.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/content/Intent.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.telephony; + +parcelable NeighboringCellInfo;
\ No newline at end of file diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java new file mode 100644 index 0000000..326401a --- /dev/null +++ b/telephony/java/android/telephony/NeighboringCellInfo.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents the neighboring cell information, including + * Received Signal Strength and Cell ID location. + */ +public class NeighboringCellInfo implements Parcelable +{ + /** + * Signal strength is not available + */ + static final public int UNKNOWN_RSSI = 99; + /** + * Cell location is not available + */ + static final public int UNKNOWN_CID = -1; + + private int mRssi; + private int mCid; + + /** + * Empty constructor. Initializes the RSSI and CID. + */ + public NeighboringCellInfo() { + mRssi = UNKNOWN_RSSI; + mCid = UNKNOWN_CID; + } + + /** + * Initialize the object from rssi and cid. + */ + public NeighboringCellInfo(int rssi, int cid) { + mRssi = rssi; + mCid = cid; + } + + /** + * Initialize the object from a parcel. + */ + public NeighboringCellInfo(Parcel in) { + mRssi = in.readInt(); + mCid = in.readInt(); + } + + /** + * @return received signal strength in "asu", ranging from 0 - 31, + * or UNKNOWN_RSSI if unknown + * + * For GSM, dBm = -113 + 2*asu, + * 0 means "-113 dBm or less" and 31 means "-51 dBm or greater" + */ + public int getRssi() { + return mRssi; + } + + /** + * @return cell id, UNKNOWN_CID if unknown, 0xffffffff max legal value + */ + public int getCid() { + return mCid; + } + + /** + * Set the cell id. + */ + public void setCid(int cid) { + mCid = cid; + } + + /** + * Set the signal strength of the cell. + */ + public void setRssi(int rssi) { + mRssi = rssi; + } + + @Override + public String toString() { + return "["+ ((mCid == UNKNOWN_CID) ? "/" : Integer.toHexString(mCid)) + + " at " + ((mRssi == UNKNOWN_RSSI)? "/" : mRssi) + "]"; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRssi); + dest.writeInt(mCid); + } + + public static final Parcelable.Creator<NeighboringCellInfo> CREATOR + = new Parcelable.Creator<NeighboringCellInfo>() { + public NeighboringCellInfo createFromParcel(Parcel in) { + return new NeighboringCellInfo(in); + } + + public NeighboringCellInfo[] newArray(int size) { + return new NeighboringCellInfo[size]; + } + }; +} + + diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index f2b63cc..d3942fc 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -505,9 +505,10 @@ public class PhoneNumberUtils * Called Party BCD Number * * See Also TS 51.011 10.5.1 "dialing number/ssc string" + * and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)" * * @param bytes the data buffer - * @param offset should point to the TOI/NPI octet after the length byte + * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte * @param length is the number of bytes including TOA byte * and must be at least 2 * @@ -518,7 +519,7 @@ public class PhoneNumberUtils */ public static String calledPartyBCDToString (byte[] bytes, int offset, int length) { - boolean prependedPlus = false; + boolean prependPlus = false; StringBuilder ret = new StringBuilder(1 + length * 2); if (length < 2) { @@ -526,18 +527,92 @@ public class PhoneNumberUtils } if ((bytes[offset] & 0xff) == TOA_International) { - ret.append("+"); - prependedPlus = true; + prependPlus = true; } internalCalledPartyBCDFragmentToString( ret, bytes, offset + 1, length - 1); - if (prependedPlus && ret.length() == 1) { + if (prependPlus && ret.length() == 0) { // If the only thing there is a prepended plus, return "" return ""; } + if (prependPlus) { + // This is an "international number" and should have + // a plus prepended to the dialing number. But there + // can also be Gsm MMI codes as defined in TS 22.030 6.5.2 + // so we need to handle those also. + // + // http://web.telia.com/~u47904776/gsmkode.htm is a + // has a nice list of some of these GSM codes. + // + // Examples are: + // **21*+886988171479# + // **21*8311234567# + // *21# + // #21# + // *#21# + // *31#+11234567890 + // #31#+18311234567 + // #31#8311234567 + // 18311234567 + // +18311234567# + // +18311234567 + // Odd ball cases that some phones handled + // where there is no dialing number so they + // append the "+" + // *21#+ + // **21#+ + String retString = ret.toString(); + Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$"); + Matcher m = p.matcher(retString); + if (m.matches()) { + if ("".equals(m.group(2))) { + // Started with two [#*] ends with # + // So no dialing number and we'll just + // append a +, this handles **21#+ + ret = new StringBuilder(); + ret.append(m.group(1)); + ret.append(m.group(3)); + ret.append(m.group(4)); + ret.append(m.group(5)); + ret.append("+"); + } else { + // Starts with [#*] and ends with # + // Assume group 4 is a dialing number + // such as *21*+1234554# + ret = new StringBuilder(); + ret.append(m.group(1)); + ret.append(m.group(2)); + ret.append(m.group(3)); + ret.append("+"); + ret.append(m.group(4)); + ret.append(m.group(5)); + } + } else { + p = Pattern.compile("(^[#*])(.*)([#*])(.*)"); + m = p.matcher(retString); + if (m.matches()) { + // Starts with [#*] and only one other [#*] + // Assume the data after last [#*] is dialing + // number (i.e. group 4) such as *31#+11234567890. + // This also includes the odd ball *21#+ + ret = new StringBuilder(); + ret.append(m.group(1)); + ret.append(m.group(2)); + ret.append(m.group(3)); + ret.append("+"); + ret.append(m.group(4)); + } else { + // Does NOT start with [#*] just prepend '+' + ret = new StringBuilder(); + ret.append('+'); + ret.append(retString); + } + } + } + return ret.toString(); } @@ -688,7 +763,7 @@ public class PhoneNumberUtils numberToCalledPartyBCD(String number) { // The extra byte required for '+' is taken into consideration while calculating // length of ret. - int size = ((number.charAt(0) == '+') ? number.length() - 1 : number.length()); + int size = (hasPlus(number) ? number.length() - 1 : number.length()); byte[] ret = new byte[(size + 1) / 2 + 1]; return numberToCalledPartyBCDHelper(ret, 0, number); @@ -702,7 +777,7 @@ public class PhoneNumberUtils numberToCalledPartyBCDWithLength(String number) { // The extra byte required for '+' is taken into consideration while calculating // length of ret. - int size = ((number.charAt(0) == '+') ? number.length() - 1 : number.length()); + int size = (hasPlus(number) ? number.length() - 1 : number.length()); int length = (size + 1) / 2 + 1; byte[] ret = new byte[length + 1]; @@ -710,21 +785,22 @@ public class PhoneNumberUtils return numberToCalledPartyBCDHelper(ret, 1, number); } + private static boolean + hasPlus(String s) { + return s.indexOf('+') >= 0; + } + private static byte[] numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) { - int size; - int curChar; - - size = number.length(); - - if (number.charAt(0) == '+') { - curChar = 1; + if (hasPlus(number)) { + number = number.replaceAll("\\+", ""); ret[offset] = (byte) TOA_International; } else { - curChar = 0; ret[offset] = (byte) TOA_Unknown; } + int size = number.length(); + int curChar = 0; int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1); for (int i = 1; i < 1 + countFullBytes; i++) { ret[offset + i] @@ -818,6 +894,8 @@ public class PhoneNumberUtils public static final int FORMAT_UNKNOWN = 0; /** NANP formatting */ public static final int FORMAT_NANP = 1; + /** Japanese formatting */ + public static final int FORMAT_JAPAN = 2; /** List of country codes for countries that use the NANP */ private static final String[] NANP_COUNTRIES = new String[] { @@ -878,6 +956,9 @@ public class PhoneNumberUtils return FORMAT_NANP; } } + if (locale.equals(Locale.JAPAN)) { + return FORMAT_JAPAN; + } return FORMAT_UNKNOWN; } @@ -891,10 +972,12 @@ public class PhoneNumberUtils public static void formatNumber(Editable text, int defaultFormattingType) { int formatType = defaultFormattingType; - // This only handles +1 for now if (text.length() > 2 && text.charAt(0) == '+') { if (text.charAt(1) == '1') { formatType = FORMAT_NANP; + } else if (text.length() >= 3 && text.charAt(1) == '8' + && text.charAt(2) == '1') { + formatType = FORMAT_JAPAN; } else { return; } @@ -904,6 +987,9 @@ public class PhoneNumberUtils case FORMAT_NANP: formatNanpNumber(text); return; + case FORMAT_JAPAN: + formatJapaneseNumber(text); + return; } } @@ -932,7 +1018,7 @@ public class PhoneNumberUtils return; } CharSequence saved = text.subSequence(0, length); - + // Strip the dashes first, as we're going to add them back int p = 0; while (p < text.length()) { @@ -1013,7 +1099,7 @@ public class PhoneNumberUtils int pos = dashPositions[i]; text.replace(pos + i, pos + i, "-"); } - + // Remove trailing dashes int len = text.length(); while (len > 0) { @@ -1026,6 +1112,25 @@ public class PhoneNumberUtils } } + /** + * Formats a phone number in-place using the Japanese formatting rules. + * Numbers will be formatted as: + * + * <p><code> + * 03-xxxx-xxxx + * 090-xxxx-xxxx + * 0120-xxx-xxx + * +81-3-xxxx-xxxx + * +81-90-xxxx-xxxx + * </code></p> + * + * @param text the number to be formatted, will be modified with + * the formatting + */ + public static void formatJapaneseNumber(Editable text) { + JapanesePhoneNumberFormatter.format(text); + } + // Three and four digit phone numbers for either special services // or from the network (eg carrier-originated SMS messages) should // not match @@ -1040,10 +1145,10 @@ public class PhoneNumberUtils * listed in the ril / sim, then return true, otherwise false. */ public static boolean isEmergencyNumber(String number) { - // Strip the separators from the number before comparing it + // Strip the separators from the number before comparing it // to the list. number = extractNetworkPortion(number); - + // retrieve the list of emergency numbers String numbers = SystemProperties.get("ro.ril.ecclist"); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 529ba31..374a703 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -16,20 +16,19 @@ package android.telephony; -import android.content.BroadcastReceiver; +import com.android.internal.telephony.*; + +import java.util.ArrayList; +import java.util.List; + +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SdkConstant; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.os.Bundle; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.ServiceManager; +import android.os.SystemProperties; -import com.android.internal.telephony.*; -import android.telephony.CellLocation; -import android.telephony.ServiceState; - -import java.util.ArrayList; /** * Provides access to information about the telephony services on @@ -73,6 +72,93 @@ public class TelephonyManager { return sInstance; } + + // + // Broadcast Intent actions + // + + /** + * Broadcast intent action indicating that the call state (cellular) + * on the device has changed. + * + * <p> + * The {@link #EXTRA_STATE} extra indicates the new call state. + * If the new state is RINGING, a second extra + * {@link #EXTRA_INCOMING_NUMBER} provides the incoming phone number as + * a String. + * + * <p class="note"> + * Requires the READ_PHONE_STATE permission. + * + * <p class="note"> + * This was a {@link android.content.Context#sendStickyBroadcast sticky} + * broadcast in version 1.0, but it is no longer sticky. + * Instead, use {@link #getCallState} to synchronously query the current call state. + * + * @see #EXTRA_STATE + * @see #EXTRA_INCOMING_NUMBER + * @see #getCallState + * + * @hide pending API Council approval + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PHONE_STATE_CHANGED = + "android.intent.action.PHONE_STATE"; + + /** + * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast + * for a String containing the new call state. + * + * @see #EXTRA_STATE_IDLE + * @see #EXTRA_STATE_RINGING + * @see #EXTRA_STATE_OFFHOOK + * + * <p class="note"> + * Retrieve with + * {@link android.content.Intent#getStringExtra(String)}. + * + * @hide pending API Council approval + */ + public static final String EXTRA_STATE = Phone.STATE_KEY; + + /** + * Value used with {@link #EXTRA_STATE} corresponding to + * {@link #CALL_STATE_IDLE}. + * + * @hide pending API Council approval + */ + public static final String EXTRA_STATE_IDLE = Phone.State.IDLE.toString(); + + /** + * Value used with {@link #EXTRA_STATE} corresponding to + * {@link #CALL_STATE_RINGING}. + * + * @hide pending API Council approval + */ + public static final String EXTRA_STATE_RINGING = Phone.State.RINGING.toString(); + + /** + * Value used with {@link #EXTRA_STATE} corresponding to + * {@link #CALL_STATE_OFFHOOK}. + * + * @hide pending API Council approval + */ + public static final String EXTRA_STATE_OFFHOOK = Phone.State.OFFHOOK.toString(); + + /** + * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast + * for a String containing the incoming phone number. + * Only valid when the new call state is RINGING. + * + * <p class="note"> + * Retrieve with + * {@link android.content.Intent#getStringExtra(String)}. + * + * @hide pending API Council approval + */ + public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; + + // // // Device Info @@ -157,6 +243,26 @@ public class TelephonyManager { } /** + * Returns the neighboring cell information of the device. + * + * @return List of NeighboringCellInfo or null if info unavailable. + * + * <p>Requires Permission: + * (@link android.Manifest.permission#ACCESS_COARSE_UPDATES} + */ + public List<NeighboringCellInfo> getNeighboringCellInfo() { + try { + ITelephony tel = getITelephony(); + if (tel != null) { + return tel.getNeighboringCellInfo(); + } + } catch (RemoteException ex) { + } + return null; + + } + + /** * No phone module */ public static final int PHONE_TYPE_NONE = 0; @@ -233,6 +339,7 @@ public class TelephonyManager { /** * Returns a constant indicating the radio technology (network type) * currently in use on the device. + * @return the network type * * @see #NETWORK_TYPE_UNKNOWN * @see #NETWORK_TYPE_GPRS @@ -255,6 +362,26 @@ public class TelephonyManager { } } + /** + * Returns a string representation of the radio technology (network type) + * currently in use on the device. + * @return the name of the radio technology + * + * @hide pending API council review + */ + public String getNetworkTypeName() { + switch (getNetworkType()) { + case NETWORK_TYPE_GPRS: + return "GPRS"; + case NETWORK_TYPE_EDGE: + return "EDGE"; + case NETWORK_TYPE_UMTS: + return "UMTS"; + default: + return "UNKNOWN"; + } + } + // // // SIM Card @@ -562,62 +689,3 @@ public class TelephonyManager { } } } - -/* - * These have not been implemented since they're not used anywhere in the system - * and it's impossible to tell if they're working or not. The comments in SystemProperties - * were wrong in at least one case, so I don't trust them. They claimed that - * PROPERTY_OPERATOR_ISROAMING was "1" if it was true, but it's actually set to "true." - */ - - - /* Set to '1' if voice mail is waiting, otherwise false */ - /* - public boolean isVoiceMailWaiting() { - return "1".equals( - SystemProperties.get(TelephonyProperties.PROPERTY_LINE1_VOICE_MAIL_WAITING)); - } - */ - - /* Set to 'true' if unconditional voice call forwarding is enabled - * Availablity: only if configured in SIM; SIM state must be "READY" - */ - /* - public boolean isCallForwarding() { - SystemProperties.get(TelephonyProperties.PROPERTY_LINE1_VOICE_CALL_FORWARDING); - } - */ - - /* '1' if the current network is the result of a manual network selection. - * Availability: when registered to a network - */ - /* - public boolean isNetworkManual() { - return SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISMANUAL); - } - */ - - -/* - * These have not been implemented because they're not public IMHO. -joeo - */ - - /* - * Baseband version - * Availability: property is available any time radio is on - */ - /* - public String getBasebandVersion() { - return SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION); - } - */ - - /* - * Radio Interface Layer (RIL) library implementation. - */ - /* - public String getRilLibrary() { - return SystemProperties.get(TelephonyProperties.PROPERTY_RIL_IMPL); - } - */ - diff --git a/telephony/java/android/telephony/gsm/SmsManager.java b/telephony/java/android/telephony/gsm/SmsManager.java index 274c461..c63b530 100644 --- a/telephony/java/android/telephony/gsm/SmsManager.java +++ b/telephony/java/android/telephony/gsm/SmsManager.java @@ -104,11 +104,12 @@ public final class SmsManager { int limit; if (messageCount > 1) { - limit = (encodingType == SmsMessage.ENCODING_7BIT)? - SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER: SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; + limit = (encodingType == SmsMessage.ENCODING_7BIT) ? + SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER : + SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; } else { - limit = (encodingType == SmsMessage.ENCODING_7BIT)? - SmsMessage.MAX_USER_DATA_SEPTETS: SmsMessage.MAX_USER_DATA_BYTES; + limit = (encodingType == SmsMessage.ENCODING_7BIT) ? + SmsMessage.MAX_USER_DATA_SEPTETS : SmsMessage.MAX_USER_DATA_BYTES; } try { @@ -117,8 +118,7 @@ public final class SmsManager { result.add(text.substring(start, end)); start = end; } - } - catch (EncodeException e) { + } catch (EncodeException e) { // ignore it. } return result; diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java index 836a013..f79b0a0 100644 --- a/telephony/java/android/telephony/gsm/SmsMessage.java +++ b/telephony/java/android/telephony/gsm/SmsMessage.java @@ -16,11 +16,12 @@ package android.telephony.gsm; -import android.pim.Time; import android.telephony.PhoneNumberUtils; import android.util.Config; import android.util.Log; import android.telephony.PhoneNumberUtils; +import android.text.format.Time; + import com.android.internal.telephony.gsm.EncodeException; import com.android.internal.telephony.gsm.GsmAlphabet; import com.android.internal.telephony.gsm.SimUtils; @@ -222,7 +223,7 @@ public class SmsMessage { * * @hide pending API Council approval to extend the public API */ - public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; + static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; /** The maximum number of payload septets per message */ public static final int MAX_USER_DATA_SEPTETS = 160; @@ -329,6 +330,9 @@ public class SmsMessage { */ boolean isStatusReportMessage = false; + /** + * This class represents the encoded form of an outgoing SMS. + */ public static class SubmitPdu { public byte[] encodedScAddress; // Null if not applicable. public byte[] encodedMessage; @@ -472,23 +476,25 @@ public class SmsMessage { ret[1] = septets; if (septets > MAX_USER_DATA_SEPTETS) { ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; - ret[2] = septets % MAX_USER_DATA_SEPTETS_WITH_HEADER; + ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER + - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); } else { ret[0] = 1; ret[2] = MAX_USER_DATA_SEPTETS - septets; } ret[3] = ENCODING_7BIT; } catch (EncodeException ex) { - // fall back to USC-2 + // fall back to UCS-2 int octets = messageBody.length() * 2; - ret[1] = octets; + ret[1] = messageBody.length(); if (octets > MAX_USER_DATA_BYTES) { // 6 is the size of the user data header ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; - ret[2] = octets % MAX_USER_DATA_BYTES_WITH_HEADER; + ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER + - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; } else { ret[0] = 1; - ret[2] = MAX_USER_DATA_BYTES - octets; + ret[2] = (MAX_USER_DATA_BYTES - octets)/2; } ret[3] = ENCODING_16BIT; } @@ -559,8 +565,7 @@ public class SmsMessage { System.arraycopy(header, 0, userData, 0, header.length); System.arraycopy(textPart, 0, userData, header.length, textPart.length); - } - else { + } else { userData = textPart; } @@ -875,7 +880,9 @@ public class SmsMessage { if (dataInSeptets) { // Return the number of septets - return userDataLength - headerSeptets; + int count = userDataLength - headerSeptets; + // If count < 0, return 0 (means UDL was probably incorrect) + return count < 0 ? 0 : count; } else { // Return the number of octets return userData.length; diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index 5f630f8..13b6e5c 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -40,6 +40,33 @@ public class CallerInfo { public static final String UNKNOWN_NUMBER = "-1"; public static final String PRIVATE_NUMBER = "-2"; + /** + * Please note that, any one of these member variables can be null, + * and any accesses to them should be prepared to handle such a case. + * + * Also, it is implied that phoneNumber is more often populated than + * name is, (think of calls being dialed/received using numbers where + * names are not known to the device), so phoneNumber should serve as + * a dependable fallback when name is unavailable. + * + * One other detail here is that this CallerInfo object reflects + * information found on a connection, it is an OUTPUT that serves + * mainly to display information to the user. In no way is this object + * used as input to make a connection, so we can choose to display + * whatever human-readable text makes sense to the user for a + * connection. This is especially relevant for the phone number field, + * since it is the one field that is most likely exposed to the user. + * + * As an example: + * 1. User dials "911" + * 2. Device recognizes that this is an emergency number + * 3. We use the "Emergency Number" string instead of "911" in the + * phoneNumber field. + * + * What we're really doing here is treating phoneNumber as an essential + * field here, NOT name. We're NOT always guaranteed to have a name + * for a connection, but the number should be displayable. + */ public String name; public String phoneNumber; public String phoneLabel; @@ -192,6 +219,9 @@ public class CallerInfo { // shortcut and skip the query. if (PhoneNumberUtils.isEmergencyNumber(number)) { CallerInfo ci = new CallerInfo(); + + // Note we're setting the phone number here (refer to javadoc + // comments at the top of CallerInfo class). ci.phoneNumber = context.getString( com.android.internal.R.string.emergency_call_dialog_number_for_display); return ci; @@ -200,7 +230,10 @@ public class CallerInfo { if (!sSkipVmCheck && PhoneNumberUtils.compare(number, TelephonyManager.getDefault().getVoiceMailNumber())) { CallerInfo ci = new CallerInfo(); - ci.name = TelephonyManager.getDefault().getVoiceMailAlphaTag(); + + // Note we're setting the phone number here (refer to javadoc + // comments at the top of CallerInfo class). + ci.phoneNumber = TelephonyManager.getDefault().getVoiceMailAlphaTag(); // TODO: FIND ANOTHER ICON //info.photoResource = android.R.drawable.badge_voicemail; return ci; @@ -214,7 +247,8 @@ public class CallerInfo { } } - Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, number); + Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, + Uri.encode(number)); CallerInfo info = getCallerInfo(context, contactUri); diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index 6cb829d..145e5d8 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -224,6 +224,8 @@ public class CallerInfoAsyncQuery { // accordingly. if (cw.event == EVENT_EMERGENCY_NUMBER) { mCallerInfo = new CallerInfo(); + // Note we're setting the phone number here (refer to javadoc + // comments at the top of CallerInfo class). mCallerInfo.phoneNumber = mQueryContext.getString(com.android.internal .R.string.emergency_call_dialog_number_for_display); mCallerInfo.photoResource = com.android.internal.R.drawable.picture_emergency; @@ -231,7 +233,10 @@ public class CallerInfoAsyncQuery { } else if (cw.event == EVENT_VOICEMAIL_NUMBER) { mCallerInfo = new CallerInfo(); try { - mCallerInfo.name = TelephonyManager.getDefault().getVoiceMailAlphaTag(); + // Note we're setting the phone number here (refer to javadoc + // comments at the top of CallerInfo class). + mCallerInfo.phoneNumber = + TelephonyManager.getDefault().getVoiceMailAlphaTag(); } catch (SecurityException ex) { // Should never happen: if this process does not have // permission to retrieve VM tag, it should not have diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 663fc03..4957366 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -17,6 +17,8 @@ package com.android.internal.telephony; import android.os.Bundle; +import java.util.List; +import android.telephony.NeighboringCellInfo; /** * Interface used to interact with the phone. Mostly this is used by the @@ -43,10 +45,26 @@ interface ITelephony { /** * If there is currently a call in progress, show the call screen. - * Returns true if the call screen was shown. + * The DTMF dialpad may or may not be visible initially, depending on + * whether it was up when the user last exited the InCallScreen. + * + * @return true if the call screen was shown. */ boolean showCallScreen(); - + + /** + * Variation of showCallScreen() that also specifies whether the + * DTMF dialpad should be initially visible when the InCallScreen + * comes up. + * + * @param showDialpad if true, make the dialpad visible initially, + * otherwise hide the dialpad initially. + * @return true if the call screen was shown. + * + * @see showCallScreen + */ + boolean showCallScreenWithDialpad(boolean showDialpad); + /** * End call or go to the Home screen * @@ -157,6 +175,11 @@ interface ITelephony { Bundle getCellLocation(); + /** + * Returns the neighboring cell information of the device. + */ + List<NeighboringCellInfo> getNeighboringCellInfo(); + int getCallState(); int getDataActivity(); int getDataState(); diff --git a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java index c558cd1..61d4c9f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java +++ b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java @@ -16,15 +16,15 @@ package com.android.internal.telephony; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.util.Log; import android.os.Handler; import android.os.Message; import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.Log; /** * @@ -40,7 +40,6 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { private static final boolean DBG = false; public static final String INTENT_KEY_ASU = "asu"; - public static final String INTENT_KEY_NUM = "incoming_number"; private static final int NOTIF_PHONE = 1 << 0; private static final int NOTIF_SERVICE = 1 << 1; @@ -49,7 +48,6 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { private static final int NOTIF_MAX = 1 << 5; Phone.State mPhoneState = Phone.State.IDLE; - String mIncomingNumber; ServiceState mServiceState = new ServiceState(); int mAsu = -1; private Context mContext; @@ -141,7 +139,7 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { public void notifyPhoneCallState(int eventWhat) { mWants |= NOTIF_PHONE; mPhoneStateEventWhat = eventWhat; - mFilter.addAction(TelephonyIntents.ACTION_PHONE_STATE_CHANGED); + mFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); } public boolean getNotifyPhoneCallState() { @@ -189,11 +187,10 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { Message message = Message.obtain(mTarget, mAsuEventWhat); mTarget.sendMessage(message); } - } else if (TelephonyIntents.ACTION_PHONE_STATE_CHANGED.equals(action)) { + } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { if (DBG) Log.d(LOG_TAG, "onReceiveIntent: ACTION_PHONE_STATE_CHANGED, state=" + intent.getStringExtra(Phone.STATE_KEY)); String phoneState = intent.getStringExtra(Phone.STATE_KEY); - mIncomingNumber = intent.getStringExtra(INTENT_KEY_NUM); mPhoneState = (Phone.State) Enum.valueOf( Phone.State.class, phoneState); diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 8519796..9219e7a 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -10,26 +10,6 @@ package com.android.internal.telephony; public class TelephonyIntents { /** - * <p>Broadcast Action: The phone state has changed. The intent will have the following - * extra values:</p> - * <ul> - * <li><em>phoneName</em> - A string version of the phone name.</li> - * <li><em>state</em> - A string version of the new phone state.</li> - * </ul> - * - * <p class="note"> - * You can <em>not</em> receive this through components declared - * in manifests, only by exlicitly registering for it with - * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, - * android.content.IntentFilter) Context.registerReceiver()}. - * - * <p class="note"> - * Requires the READ_PHONE_STATE permission. - */ - public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; - - - /** * Broadcast Action: The phone service state has changed. The intent will have the following * extra values:</p> * <ul> diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index c197734..6aa90f1 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -53,11 +53,7 @@ public interface TelephonyProperties */ static final String PROPERTY_OPERATOR_ISROAMING = "gsm.operator.isroaming"; - /** '1' if the current network is the result of a manual network selection. - * Availability: when registered to a network - */ - static final String PROPERTY_OPERATOR_ISMANUAL = "gsm.operator.ismanual"; - /** The ISO country code equivilent of the current registered operator's + /** The ISO country code equivalent of the current registered operator's * MCC (Mobile Country Code) * Availability: when registered to a network */ @@ -70,14 +66,6 @@ public interface TelephonyProperties */ static String PROPERTY_SIM_STATE = "gsm.sim.state"; - /** Set to '1' if voice mail is waiting, otherwise false */ - static String PROPERTY_LINE1_VOICE_MAIL_WAITING = "gsm.sim.voice-mail.waiting"; - - /** Set to 'true' if unconditional voice call forwarding is enabled - * Availablity: only if configured in SIM; SIM state must be "READY" - */ - static String PROPERTY_LINE1_VOICE_CALL_FORWARDING = "gsm.sim.line1.cff"; - /** The MCC+MNC (mobile country code+mobile network code) of the * provider of the SIM. 5 or 6 decimal digits. * Availablity: SIM state must be "READY" diff --git a/telephony/java/com/android/internal/telephony/gsm/CallTracker.java b/telephony/java/com/android/internal/telephony/gsm/CallTracker.java index 9821852..afd11c4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/CallTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/CallTracker.java @@ -15,16 +15,32 @@ */ package com.android.internal.telephony.gsm; -import com.android.internal.telephony.*; -import android.os.*; + +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_EDGE; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_GPRS; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UMTS; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UNKNOWN; import android.os.AsyncResult; -import android.util.Log; -import android.provider.Checkin; +import android.os.Handler; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.os.SystemProperties; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.telephony.gsm.GsmCellLocation; +import android.util.EventLog; +import android.util.Log; + +import com.android.internal.telephony.Call; +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.Phone; -import java.util.List; import java.util.ArrayList; +import java.util.List; /** * {@hide} @@ -36,6 +52,9 @@ public final class CallTracker extends Handler private static final boolean DBG_POLL = false; + // Event Log Tags + private static final int EVENT_LOG_CALL_DROP = 50106; + //***** Constants static final int POLL_DELAY_MSEC = 250; @@ -165,7 +184,7 @@ public final class CallTracker extends Handler throw new CallStateException("cannot dial in current state"); } - pendingMO = new GSMConnection(dialString, this, foregroundCall); + pendingMO = new GSMConnection(phone.getContext(), dialString, this, foregroundCall); hangupPendingMO = false; if (pendingMO.address == null || pendingMO.address.length() == 0 @@ -503,7 +522,7 @@ public final class CallTracker extends Handler return; } } else { - connections[i] = new GSMConnection(dc, this, i); + connections[i] = new GSMConnection(phone.getContext(), dc, this, i); // it's a ringing call if (connections[i].getCall() == ringingCall) { @@ -540,7 +559,7 @@ public final class CallTracker extends Handler // we were tracking. Assume dropped call and new call droppedDuringPoll.add(conn); - connections[i] = new GSMConnection (dc, this, i); + connections[i] = new GSMConnection (phone.getContext(), dc, this, i); if (connections[i].getCall() == ringingCall) { newRinging = connections[i]; @@ -916,6 +935,22 @@ public final class CallTracker extends Handler } else { causeCode = ((int[])ar.result)[0]; } + // Log the causeCode if its not normal + if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || + causeCode == CallFailCause.TEMPORARY_FAILURE || + causeCode == CallFailCause.SWITCHING_CONGESTION || + causeCode == CallFailCause.CHANNEL_NOT_AVAIL || + causeCode == CallFailCause.QOS_NOT_AVAIL || + causeCode == CallFailCause.BEARER_NOT_AVAIL || + causeCode == CallFailCause.ERROR_UNSPECIFIED) { + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(causeCode, cid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(EVENT_LOG_CALL_DROP, val); + } for (int i = 0, s = droppedDuringPoll.size() ; i < s ; i++ diff --git a/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java b/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java index ececd72..c7b08d2 100644 --- a/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java @@ -212,9 +212,9 @@ public interface CommandsInterface * * AsyncResult.result is an Object[] * ((Object[])AsyncResult.result)[0] is a String containing the NITZ time string - * ((Object[])AsyncResult.result)[0] is an Integer containing - * the UNIX time_t returned by time() when - * this NITZ time was posted. + * ((Object[])AsyncResult.result)[1] is a Long containing the milliseconds since boot as + * returned by elapsedRealtime() when this NITZ time + * was posted. * * Please note that the delivery of this message may be delayed several * seconds on system startup diff --git a/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java index 63d145e..b0b8cdc 100644 --- a/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java @@ -16,8 +16,12 @@ package com.android.internal.telephony.gsm; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_EDGE; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_GPRS; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UMTS; +import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UNKNOWN; import android.app.AlarmManager; -import android.app.IAlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -40,14 +44,19 @@ import android.provider.Checkin; import android.provider.Settings; import android.provider.Telephony; import android.provider.Settings.SettingNotFoundException; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.gsm.PdpConnection.PdpFailCause; - import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.telephony.gsm.GsmCellLocation; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; -import android.text.TextUtils; +import android.util.EventLog.List; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.gsm.PdpConnection.PdpFailCause; +import android.net.wifi.WifiManager; +import android.net.NetworkInfo; + import java.io.IOException; import java.util.ArrayList; @@ -66,12 +75,14 @@ final class DataConnectionTracker extends Handler * SCANNING: data connection fails with one apn but other apns are available * ready to start data connection on other apns (before INITING) * CONNECTED: IP connection is setup + * DISCONNECTING: PdpConnection.disconnect() has been called, but PDP + * context is not yet deactivated * FAILED: data connection fail for all apns settings * * getDataConnectionState() maps State to DataState * FAILED or IDLE : DISCONNECTED * INITING or CONNECTING or SCANNING: CONNECTING - * CONNECTED : CONNECTED + * CONNECTED : CONNECTED or DISCONNECTING */ enum State { IDLE, @@ -79,6 +90,7 @@ final class DataConnectionTracker extends Handler CONNECTING, SCANNING, CONNECTED, + DISCONNECTING, FAILED } @@ -99,14 +111,7 @@ final class DataConnectionTracker extends Handler @Override public void onChange(boolean selfChange) { - boolean isConnected; - - isConnected = (state != State.IDLE && state != State.FAILED); - // TODO: It'd be nice to only do this if the changed entrie(s) - // match the current operator. - cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); - createAllApnList(); - sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + sendMessage(obtainMessage(EVENT_APN_CHANGED)); } } @@ -172,6 +177,12 @@ final class DataConnectionTracker extends Handler private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; + // wifi connection status will be updated by sticky intent + private boolean mIsWifiConnected = false; + + /** Intent sent when the reconnect alarm fires. */ + private PendingIntent mReconnectIntent = null; + //***** Constants // TODO: Increase this to match the max number of simultaneous @@ -183,6 +194,8 @@ final class DataConnectionTracker extends Handler private static final int POLL_PDP_MILLIS = 5 * 1000; private static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000; + /** Cap out with 1 hour retry interval. */ + private static final int RECONNECT_DELAY_MAX_MILLIS = 60 * 60 * 1000; /** Slow poll when attempting connection recovery. */ private static final int POLL_NETSTAT_SLOW_MILLIS = 5000; @@ -215,13 +228,16 @@ final class DataConnectionTracker extends Handler // represents an invalid IP address private static final String NULL_IP = "0.0.0.0"; - static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; + private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; + private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; + //***** Tag IDs for EventLog private static final int EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED = 50101; private static final int EVENT_LOG_RADIO_RESET = 50102; private static final int EVENT_LOG_PDP_RESET = 50103; private static final int EVENT_LOG_REREGISTER_NETWORK = 50104; + private static final int EVENT_LOG_RADIO_PDP_SETUP_FAIL = 50105; //***** Event Codes static final int EVENT_DATA_SETUP_COMPLETE = 1; @@ -244,37 +260,44 @@ final class DataConnectionTracker extends Handler static final int EVENT_GPRS_ATTACHED = 26; static final int EVENT_START_NETSTAT_POLL = 27; static final int EVENT_START_RECOVERY = 28; + static final int EVENT_APN_CHANGED = 29; - BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver () + BroadcastReceiver mIntentReceiver = new BroadcastReceiver () { + @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_SCREEN_ON)) { mIsScreenOn = true; stopNetStatPoll(); startNetStatPoll(); - } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mIsScreenOn = false; stopNetStatPoll(); startNetStatPoll(); - } else { - Log.w(LOG_TAG, "DataConnectionTracker received unexpected Intent: " + intent.getAction()); - } - } - }; + } else if (action.equals((INTENT_RECONNECT_ALARM))) { + Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state); - BroadcastReceiver alarmReceiver - = new BroadcastReceiver () { - - public void onReceive(Context context, Intent intent) - { - Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state); - - if (state == State.FAILED) { - cleanUpConnection(false, null); + String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); + if (state == State.FAILED) { + cleanUpConnection(false, reason); + } + trySetupData(reason); + } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + final android.net.NetworkInfo networkInfo = (NetworkInfo) + intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); + } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; + + if (!enabled) { + // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION + // quit and wont report disconnected til next enalbing. + mIsWifiConnected = false; + } } - - trySetupData(null); } }; @@ -301,13 +324,13 @@ final class DataConnectionTracker extends Handler IntentFilter filter = new IntentFilter(); filter.addAction(INTENT_RECONNECT_ALARM); - phone.getContext().registerReceiver( - alarmReceiver, filter, null, phone.h); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + + phone.getContext().registerReceiver(mIntentReceiver, filter, null, phone.h); - IntentFilter filterS = new IntentFilter(); - filterS.addAction(Intent.ACTION_SCREEN_ON); - filterS.addAction(Intent.ACTION_SCREEN_OFF); - phone.getContext().registerReceiver(screenOnOffReceiver, filterS); mDataConnectionTracker = this; mResolver = phone.getContext().getContentResolver(); @@ -326,6 +349,7 @@ final class DataConnectionTracker extends Handler } void setState(State s) { + if (DBG) log ("setState: " + s); if (state != s) { if (s == State.INITING) { // request PDP context Checkin.updateStats( @@ -347,6 +371,20 @@ final class DataConnectionTracker extends Handler waitingApns.clear(); // when teardown the connection and set to IDLE } } + + String getStateInString() { + switch (state) { + case IDLE: return "IDLE"; + case INITING: return "INIT"; + case CONNECTING: return "CING"; + case SCANNING: return "SCAN"; + case CONNECTED: return "CNTD"; + case DISCONNECTING: return "DING"; + case FAILED: return "FAIL"; + default: return "ERRO"; + } + } + String[] getActiveApnTypes() { String[] result; if (mActiveApn != null) { @@ -394,7 +432,8 @@ final class DataConnectionTracker extends Handler sendMessageDelayed( obtainMessage(EVENT_RESTORE_DEFAULT_APN), getRestoreDefaultApnDelay()); - return Phone.APN_ALREADY_ACTIVE; + if (state == State.INITING) return Phone.APN_REQUEST_STARTED; + else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; } if (!isApnTypeAvailable(type)) { @@ -440,6 +479,27 @@ final class DataConnectionTracker extends Handler return Phone.APN_REQUEST_FAILED; } } + + /** + * The data connection is expected to be setup while device + * 1. has sim card + * 2. registered to gprs service + * 3. user doesn't explicitly disable data service + * 4. wifi is not on + * + * @return false while no data connection if all above requirements are met. + */ + boolean isDataConnectionAsDesired() { + boolean roaming = phone.getServiceState().getRoaming(); + + if (phone.mSIMRecords.getRecordsLoaded() && + phone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && + (!roaming || getDataOnRoamingEnabled()) && + !mIsWifiConnected ) { + return (state == State.CONNECTED); + } + return true; + } private boolean isApnTypeActive(String type) { // TODO: to support simultaneous, mActiveApn can be a List instead. @@ -534,8 +594,8 @@ final class DataConnectionTracker extends Handler // the shared values. If it is not, then update it. public void setDataOnRoamingEnabled(boolean enabled) { if (getDataOnRoamingEnabled() != enabled) { - Settings.System.putInt(phone.getContext().getContentResolver(), - Settings.System.DATA_ROAMING, enabled ? 1 : 0); + Settings.Secure.putInt(phone.getContext().getContentResolver(), + Settings.Secure.DATA_ROAMING, enabled ? 1 : 0); } Message roamingMsg = phone.getServiceState().getRoaming() ? obtainMessage(EVENT_ROAMING_ON) : obtainMessage(EVENT_ROAMING_OFF); @@ -545,8 +605,8 @@ final class DataConnectionTracker extends Handler //Retrieve the data roaming setting from the shared preferences. public boolean getDataOnRoamingEnabled() { try { - return Settings.System.getInt(phone.getContext().getContentResolver(), - Settings.System.DATA_ROAMING) > 0; + return Settings.Secure.getInt(phone.getContext().getContentResolver(), + Settings.Secure.DATA_ROAMING) > 0; } catch (SettingNotFoundException snfe) { return false; } @@ -582,6 +642,10 @@ final class DataConnectionTracker extends Handler startNetStatPoll(); phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); } else { + if (state == State.FAILED) { + cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + } trySetupData(Phone.REASON_GPRS_ATTACHED); } } @@ -651,18 +715,45 @@ final class DataConnectionTracker extends Handler */ private void cleanUpConnection(boolean tearDown, String reason) { if (DBG) log("Clean up connection due to " + reason); + + // Clear the reconnect alarm, if set. + if (mReconnectIntent != null) { + AlarmManager am = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + am.cancel(mReconnectIntent); + mReconnectIntent = null; + } + for (PdpConnection pdp : pdpList) { if (tearDown) { - Message msg = obtainMessage(EVENT_DISCONNECT_DONE); + Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); pdp.disconnect(msg); } else { pdp.clearSettings(); } } stopNetStatPoll(); - setState(State.IDLE); - phone.notifyDataConnection(reason); - mActiveApn = null; + + /* + * If we've been asked to tear down the connection, + * set the state to DISCONNECTING. However, there's + * a race that can occur if for some reason we were + * already in the IDLE state. In that case, the call + * to pdp.disconnect() above will immediately post + * a message to the handler thread that the disconnect + * is done, and if the handler runs before the code + * below does, the handler will have set the state to + * IDLE before the code below runs. If we didn't check + * for that, future calls to trySetupData would fail, + * and we would never get out of the DISCONNECTING state. + */ + if (!tearDown) { + setState(State.IDLE); + phone.notifyDataConnection(reason); + mActiveApn = null; + } else if (state != State.IDLE) { + setState(State.DISCONNECTING); + } } /** @@ -729,6 +820,7 @@ final class DataConnectionTracker extends Handler Message msg = obtainMessage(); msg.what = EVENT_DATA_SETUP_COMPLETE; + msg.obj = reason; pdp.connect(apn, msg); setState(State.INITING); @@ -789,6 +881,25 @@ final class DataConnectionTracker extends Handler } /** + * Handles changes to the APN database. + */ + private void onApnChanged() { + boolean isConnected; + + isConnected = (state != State.IDLE && state != State.FAILED); + + // TODO: It'd be nice to only do this if the changed entrie(s) + // match the current operator. + createAllApnList(); + if (state != State.DISCONNECTING) { + cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); + if (!isConnected) { + trySetupData(Phone.REASON_APN_CHANGED); + } + } + } + + /** * @param explicitPoll if true, indicates that *we* polled for this * update while state == CONNECTED rather than having it delivered * via an unsolicited response (which could have happened at any @@ -822,7 +933,6 @@ final class DataConnectionTracker extends Handler Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); cleanUpConnection(true, null); - trySetupData(null); return; } @@ -855,16 +965,15 @@ final class DataConnectionTracker extends Handler + " Reconnecting"); cleanUpConnection(true, null); - trySetupData(null); } } } } - private void notifyDefaultData() { + private void notifyDefaultData(String reason) { setupDnsProperties(); setState(State.CONNECTED); - phone.notifyDataConnection(null); + phone.notifyDataConnection(reason); startNetStatPoll(); // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; @@ -949,7 +1058,7 @@ final class DataConnectionTracker extends Handler private void startNetStatPoll() { - if (state == State.CONNECTED && mPingTestActive == false) { + if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); resetPollStats(); netStatPollEnabled = true; @@ -1066,14 +1175,13 @@ final class DataConnectionTracker extends Handler " pkts since last received"); // We've exceeded the threshold. Run ping test as a final check; // it will proceed with recovery if ping fails. - netStatPollEnabled = false; stopNetStatPoll(); Thread pingTest = new Thread() { public void run() { - mPingTestActive = true; runPingTest(); } }; + mPingTestActive = true; pingTest.start(); } } else { @@ -1141,27 +1249,26 @@ final class DataConnectionTracker extends Handler return (shouldPost && cause != PdpConnection.PdpFailCause.UNKNOWN); } - private void - reconnectAfterFail(PdpConnection.PdpFailCause lastFailCauseCode) - { + private void reconnectAfterFail(PdpFailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " + (nextReconnectDelay / 1000) + "s"); - try { - IAlarmManager am = IAlarmManager.Stub.asInterface( - ServiceManager.getService(Context.ALARM_SERVICE)); - PendingIntent sender = PendingIntent.getBroadcast( - phone.getContext(), 0, - new Intent(INTENT_RECONNECT_ALARM), 0); - am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + nextReconnectDelay, - sender); - } catch (RemoteException ex) { - } + AlarmManager am = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(INTENT_RECONNECT_ALARM); + intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); + mReconnectIntent = PendingIntent.getBroadcast( + phone.getContext(), 0, intent, 0); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + nextReconnectDelay, + mReconnectIntent); // double it for next time nextReconnectDelay *= 2; + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } if (!shouldPostNotification(lastFailCauseCode)) { Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " @@ -1202,7 +1309,8 @@ final class DataConnectionTracker extends Handler // cleanUpConnection if it needs to free up a PdpConnection. reason = Phone.REASON_APN_SWITCHED; cleanUpConnection(true, reason); - // Fall through to EVENT_TRY_SETUP_DATA. + break; + case EVENT_TRY_SETUP_DATA: trySetupData(reason); break; @@ -1213,7 +1321,6 @@ final class DataConnectionTracker extends Handler if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN); mRequestedApnType = Phone.APN_TYPE_DEFAULT; - trySetupData(Phone.REASON_RESTORE_DEFAULT_APN ); } break; @@ -1272,6 +1379,9 @@ final class DataConnectionTracker extends Handler case EVENT_DATA_SETUP_COMPLETE: ar = (AsyncResult) msg.obj; + if (ar.userObj instanceof String) { + reason = (String) ar.userObj; + } if (ar.exception == null) { // everything is setup @@ -1299,7 +1409,7 @@ final class DataConnectionTracker extends Handler } else { SystemProperties.set("gsm.defaultpdpcontext.active", "false"); } - notifyDefaultData(); + notifyDefaultData(reason); // TODO: For simultaneous PDP support, we need to build another // trigger another TRY_SETUP_DATA for the next APN type. (Note @@ -1310,7 +1420,23 @@ final class DataConnectionTracker extends Handler cause = (PdpConnection.PdpFailCause) (ar.result); if(DBG) log("PDP setup failed " + cause); - + // Log this failure to the Event Logs. + if (cause == PdpConnection.PdpFailCause.BAD_APN || + cause == PdpConnection.PdpFailCause.BAD_PAP_SECRET || + cause == PdpConnection.PdpFailCause.BARRED || + cause == PdpConnection.PdpFailCause.RADIO_ERROR_RETRY || + cause == PdpConnection.PdpFailCause.SUSPENED_TEMPORARY || + cause == PdpConnection.PdpFailCause.UNKNOWN || + cause == PdpConnection.PdpFailCause.USER_AUTHENTICATION) { + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List( + cause.ordinal(), cid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); + } // No try for permanent failure if (cause.isPermanentFail()) { notifyNoData(cause); @@ -1320,23 +1446,28 @@ final class DataConnectionTracker extends Handler waitingApns.remove(0); if (waitingApns.isEmpty()) { // No more to try, start delayed retry - notifyNoData(cause); - reconnectAfterFail(cause); + startDelayedRetry(cause, reason); } else { // we still have more apns to try setState(State.SCANNING); - trySetupData(null); + trySetupData(reason); } } else { - notifyNoData(cause); - reconnectAfterFail(cause); + startDelayedRetry(cause, reason); } } break; case EVENT_DISCONNECT_DONE: if(DBG) log("EVENT_DISCONNECT_DONE"); - trySetupData(null); + ar = (AsyncResult) msg.obj; + if (ar.userObj instanceof String) { + reason = (String) ar.userObj; + } + setState(State.IDLE); + phone.notifyDataConnection(reason); + mActiveApn = null; + trySetupData(reason); break; case EVENT_PDP_STATE_CHANGED: @@ -1375,15 +1506,17 @@ final class DataConnectionTracker extends Handler break; case EVENT_VOICE_CALL_ENDED: - // in case data setup was attempted when we were on a voice call - trySetupData(Phone.REASON_VOICE_CALL_ENDED); - if (state == State.CONNECTED && - !phone.mSST.isConcurrentVoiceAndData()) { - startNetStatPoll(); - phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); + if (state == State.CONNECTED) { + if (!phone.mSST.isConcurrentVoiceAndData()) { + startNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); + } else { + // clean slate after call end. + resetPollStats(); + } } else { - // clean slate after call end. - resetPollStats(); + // in case data setup was attempted when we were on a voice call + trySetupData(Phone.REASON_VOICE_CALL_ENDED); } break; @@ -1396,6 +1529,10 @@ final class DataConnectionTracker extends Handler mPingTestActive = false; doRecovery(); break; + + case EVENT_APN_CHANGED: + onApnChanged(); + break; } } @@ -1509,4 +1646,14 @@ final class DataConnectionTracker extends Handler } return result.toString(); } + + private void startDelayedRetry(PdpConnection.PdpFailCause cause, String reason) { + notifyNoData(cause); + if (mRequestedApnType != Phone.APN_TYPE_DEFAULT) { + sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); + } + else { + reconnectAfterFail(cause, reason); + } + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java b/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java index c316a23..43930c1 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java @@ -16,7 +16,9 @@ package com.android.internal.telephony.gsm; import com.android.internal.telephony.*; +import android.content.Context; import android.os.Handler; +import android.os.PowerManager; import android.os.Registrant; import android.os.Looper; import android.os.Message; @@ -72,6 +74,8 @@ public class GSMConnection extends Connection { Handler h; + private PowerManager.WakeLock mPartialWakeLock; + //***** Event Constants static final int EVENT_DTMF_DONE = 1; @@ -106,8 +110,11 @@ public class GSMConnection extends Connection { /** This is probably an MT call that we first saw in a CLCC response */ /*package*/ - GSMConnection (DriverCall dc, CallTracker ct, int index) + GSMConnection (Context context, DriverCall dc, CallTracker ct, int index) { + createWakeLock(context); + acquireWakeLock(); + owner = ct; h = new MyHandler(owner.getLooper()); @@ -124,8 +131,11 @@ public class GSMConnection extends Connection { /** This is an MO call, created when dialing */ /*package*/ - GSMConnection (String dialString, CallTracker ct, GSMCall parent) + GSMConnection (Context context, String dialString, CallTracker ct, GSMCall parent) { + createWakeLock(context); + acquireWakeLock(); + owner = ct; h = new MyHandler(owner.getLooper()); @@ -412,6 +422,7 @@ public class GSMConnection extends Connection { parent.connectionDisconnected(this); } } + releaseWakeLock(); } // Returns true if state has changed, false if nothing changed @@ -513,6 +524,7 @@ public class GSMConnection extends Connection { // outgoing calls only processNextPostDialChar(); } + releaseWakeLock(); } private void @@ -574,6 +586,21 @@ public class GSMConnection extends Connection { return postDialString.substring(nextPostDialChar); } + @Override + protected void finalize() + { + /** + * It is understood that This finializer is not guaranteed + * to be called and the release lock call is here just in + * case there is some path that doesn't call onDisconnect + * and or onConnectedInOrOut. + */ + if (mPartialWakeLock.isHeld()) { + Log.e(LOG_TAG, "[GSMConn] UNEXPECTED; mPartialWakeLock is held when finalizing."); + } + releaseWakeLock(); + } + private void processNextPostDialChar() { @@ -672,6 +699,28 @@ public class GSMConnection extends Connection { } } + private void + createWakeLock(Context context) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); + } + + private void + acquireWakeLock() { + log("acquireWakeLock"); + mPartialWakeLock.acquire(); + } + + private void + releaseWakeLock() { + synchronized(mPartialWakeLock) { + if (mPartialWakeLock.isHeld()) { + log("releaseWakeLock"); + mPartialWakeLock.release(); + } + } + } + private void log(String msg) { Log.d(LOG_TAG, "[GSMConn] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index f2e5799..3f4bb5c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -16,29 +16,58 @@ package com.android.internal.telephony.gsm; -import android.content.*; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_ACTION_DISABLE; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_ACTION_ENABLE; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_ACTION_ERASURE; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_ACTION_REGISTRATION; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_REASON_ALL; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_REASON_ALL_CONDITIONAL; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_REASON_BUSY; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_REASON_NOT_REACHABLE; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_REASON_NO_REPLY; +import static com.android.internal.telephony.gsm.CommandsInterface.CF_REASON_UNCONDITIONAL; +import static com.android.internal.telephony.gsm.CommandsInterface.SERVICE_CLASS_VOICE; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; import android.database.SQLException; -import android.os.*; +import android.net.Uri; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.Telephony; -import com.android.internal.telephony.*; -import com.android.internal.telephony.gsm.stk.Service; -import static com.android.internal.telephony.gsm.CommandsInterface.*; - -import com.android.internal.telephony.test.SimulatedRadioControl; -import android.text.TextUtils; -import android.util.Log; -import static com.android.internal.telephony.TelephonyProperties.*; -import android.net.Uri; -import android.telephony.PhoneNumberUtils; import android.telephony.CellLocation; +import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.telephony.Call; +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneBase; +import com.android.internal.telephony.PhoneNotifier; +import com.android.internal.telephony.PhoneSubInfo; +import com.android.internal.telephony.SimCard; +import com.android.internal.telephony.gsm.SimException; +import com.android.internal.telephony.gsm.stk.Service; +import com.android.internal.telephony.test.SimulatedRadioControl; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * {@hide} @@ -58,7 +87,10 @@ public class GSMPhone extends PhoneBase { public static final String CIPHERING_KEY = "ciphering_key"; // Key used to read/write current CLIR setting public static final String CLIR_KEY = "clir_key"; - + // Key used to read/write voice mail number + public static final String VM_NUMBER = "vm_number_key"; + // Key used to read/write the SIM IMSI used for storing the voice mail + public static final String VM_SIM_IMSI = "vm_sim_imsi_key"; //***** Instance Variables @@ -91,6 +123,7 @@ public class GSMPhone extends PhoneBase { private String mImei; private String mImeiSv; + private String mVmNumber; //***** Event Constants @@ -115,6 +148,7 @@ public class GSMPhone extends PhoneBase { static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 16; static final int EVENT_SET_CLIR_COMPLETE = 17; static final int EVENT_REGISTERED_TO_NETWORK = 18; + static final int EVENT_SET_VM_NUMBER_DONE = 19; //***** Constructors @@ -275,11 +309,13 @@ public class GSMPhone extends PhoneBase { break; case CONNECTED: + case DISCONNECTING: if ( mCT.state != Phone.State.IDLE - && !mSST.isConcurrentVoiceAndData()) + && !mSST.isConcurrentVoiceAndData()) { ret = DataState.SUSPENDED; - else + } else { ret = DataState.CONNECTED; + } break; case INITING: @@ -731,8 +767,10 @@ public class GSMPhone extends PhoneBase { if (handleInCallMmiCommands(newDialString)) { return null; } - - GsmMmiCode mmi = GsmMmiCode.newFromDialString(newDialString, this); + + // Only look at the Network portion for mmi + String networkPortion = PhoneNumberUtils.extractNetworkPortion(newDialString); + GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this); if (LOCAL_DEBUG) Log.d(LOG_TAG, "dialing w/ mmi '" + mmi + "'..."); @@ -802,11 +840,36 @@ public class GSMPhone extends PhoneBase { mSST.setRadioPower(power); } + private void storeVoiceMailNumber(String number) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(VM_NUMBER, number); + editor.commit(); + setVmSimImsi(getSubscriberId()); + } public String getVoiceMailNumber() { - return mSIMRecords.getVoiceMailNumber(); + // Read from the SIM. If its null, try reading from the shared preference area. + String number = mSIMRecords.getVoiceMailNumber(); + if (TextUtils.isEmpty(number)) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + number = sp.getString(VM_NUMBER, null); + } + return number; + } + + private String getVmSimImsi() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + return sp.getString(VM_SIM_IMSI, null); } + private void setVmSimImsi(String imsi) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(VM_SIM_IMSI, imsi); + editor.commit(); + } + public String getVoiceMailAlphaTag() { String ret; @@ -860,7 +923,11 @@ public class GSMPhone extends PhoneBase { public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, Message onComplete) { - mSIMRecords.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete); + + Message resp; + mVmNumber = voiceMailNumber; + resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete); + mSIMRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp); } private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { @@ -1301,6 +1368,13 @@ public class GSMPhone extends PhoneBase { } catch (SQLException e) { Log.e(LOG_TAG, "Can't store current operator", e); } + // Check if this is a different SIM than the previous one. If so unset the + // voice mail number. + String imsi = getVmSimImsi(); + if (imsi != null && !getSubscriberId().equals(imsi)) { + storeVoiceMailNumber(null); + setVmSimImsi(null); + } break; @@ -1380,7 +1454,21 @@ public class GSMPhone extends PhoneBase { onComplete.sendToTarget(); } break; + + case EVENT_SET_VM_NUMBER_DONE: + ar = (AsyncResult)msg.obj; + if (SimVmNotSupportedException.class.isInstance(ar.exception)) { + storeVoiceMailNumber(mVmNumber); + ar.exception = null; + } + onComplete = (Message) ar.userObj; + if (onComplete != null) { + AsyncResult.forMessage(onComplete, ar.result, ar.exception); + onComplete.sendToTarget(); + } + break; + case EVENT_GET_CALL_FORWARD_DONE: ar = (AsyncResult)msg.obj; if (ar.exception == null) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java index 7baaeca..59a9422 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java @@ -495,7 +495,7 @@ public class GsmAlphabet countGsmSeptets(char c) { try { - return countGsmSeptets(c, true); + return countGsmSeptets(c, false); } catch (EncodeException ex) { // This should never happen. return 0; @@ -535,7 +535,7 @@ public class GsmAlphabet countGsmSeptets(String s) { try { - return countGsmSeptets(s, true); + return countGsmSeptets(s, false); } catch (EncodeException ex) { // this should never happen return 0; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java index c85fb7c..ce6c186 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -197,7 +197,7 @@ public final class GsmMmiCode extends Handler implements MmiCode ret = new GsmMmiCode(phone); ret.dialingNumber = dialString; } - + return ret; } @@ -621,21 +621,21 @@ public final class GsmMmiCode extends Handler implements MmiCode throw new RuntimeException ("invalid action"); } - int isSettingUnconditionalVoice = ( - (reason == CommandsInterface.CF_REASON_UNCONDITIONAL) && - ((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) - ) ? 1 : 0; + int isSettingUnconditionalVoice = + ((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) && + (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) || + (serviceClass == CommandsInterface.SERVICE_CLASS_NONE))) ? 1 : 0; int isEnableDesired = - ((cfAction == CommandsInterface.CF_ACTION_ENABLE) - || (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; - - phone.mCM.setCallForward(cfAction, reason, - serviceClass, dialingNumber, time, - obtainMessage(EVENT_SET_CFF_COMPLETE, - isSettingUnconditionalVoice, - isEnableDesired, - this)); + ((cfAction == CommandsInterface.CF_ACTION_ENABLE) || + (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; + + Log.d(LOG_TAG, "is CF setCallForward"); + phone.mCM.setCallForward(cfAction, reason, serviceClass, + dialingNumber, time, obtainMessage( + EVENT_SET_CFF_COMPLETE, + isSettingUnconditionalVoice, + isEnableDesired, this)); } } else if (isServiceCodeCallBarring(sc)) { // sia = password @@ -711,6 +711,10 @@ public final class GsmMmiCode extends Handler implements MmiCode } else if (pinLen < 4 || pinLen > 8 ) { // invalid length handlePasswordError(com.android.internal.R.string.invalidPin); + } else if (sc.equals(SC_PIN) && + phone.mSimCard.getState() == SimCard.State.PUK_REQUIRED ) { + // Sim is puk-locked + handlePasswordError(com.android.internal.R.string.needPuk); } else { // pre-checks OK if (sc.equals(SC_PIN)) { diff --git a/telephony/java/com/android/internal/telephony/gsm/MccTable.java b/telephony/java/com/android/internal/telephony/gsm/MccTable.java index 57aa012..8f6b22e 100644 --- a/telephony/java/com/android/internal/telephony/gsm/MccTable.java +++ b/telephony/java/com/android/internal/telephony/gsm/MccTable.java @@ -33,12 +33,23 @@ public final class MccTable int mcc; String iso; int smallestDigitsMnc; + String timezone; + String language; - MccEntry(int mnc, String iso, int smallestDigitsMCC) - { + MccEntry(int mnc, String iso, int smallestDigitsMCC) { + this(mnc, iso, smallestDigitsMCC, null); + } + + MccEntry(int mnc, String iso, int smallestDigitsMCC, String timezone) { + this(mnc, iso, smallestDigitsMCC, timezone, null); + } + + MccEntry(int mnc, String iso, int smallestDigitsMCC, String timezone, String language) { this.mcc = mnc; this.iso = iso; this.smallestDigitsMnc = smallestDigitsMCC; + this.timezone = timezone; + this.language = language; } public int compareTo(MccEntry o) @@ -66,6 +77,23 @@ public final class MccTable } /** + * Returns a default time zone ID for the given MCC. + * @param mcc Mobile Country Code + * @return default TimeZone ID, or null if not specified + */ + /* package */ static String defaultTimeZoneForMcc(int mcc) { + MccEntry entry; + + entry = entryForMcc(mcc); + + if (entry == null) { + return null; + } else { + return entry.timezone; + } + } + + /** * Given a GSM Mobile Country Code, returns * an ISO two-character country code if available. * Returns "" if unavailable. @@ -84,6 +112,22 @@ public final class MccTable } } + /** + * Given a GSM Mobile Country Code, returns + * an ISO 2-3 character language code if available. + * Returns null if unavailable. + */ + /* package */ static String defaultLanguageForMcc(int mcc) { + MccEntry entry; + + entry = entryForMcc(mcc); + + if (entry == null) { + return null; + } else { + return entry.language; + } + } /** * Given a GSM Mobile Country Code, returns @@ -125,7 +169,7 @@ public final class MccTable */ table.add(new MccEntry(202,"gr",2)); //Greece - table.add(new MccEntry(204,"nl",2)); //Netherlands (Kingdom of the) + table.add(new MccEntry(204,"nl",2,"Europe/Amsterdam")); //Netherlands (Kingdom of the) table.add(new MccEntry(206,"be",2)); //Belgium table.add(new MccEntry(208,"fr",2)); //France table.add(new MccEntry(212,"mc",2)); //Monaco (Principality of) @@ -139,11 +183,11 @@ public final class MccTable table.add(new MccEntry(225,"va",2)); //Vatican City State table.add(new MccEntry(226,"ro",2)); //Romania table.add(new MccEntry(228,"ch",2)); //Switzerland (Confederation of) - table.add(new MccEntry(230,"cz",2)); //Czech Republic + table.add(new MccEntry(230,"cz",2,"Europe/Prague")); //Czech Republic table.add(new MccEntry(231,"sk",2)); //Slovak Republic - table.add(new MccEntry(232,"at",2)); //Austria - table.add(new MccEntry(234,"gb",2)); //United Kingdom of Great Britain and Northern Ireland - table.add(new MccEntry(235,"gb",2)); //United Kingdom of Great Britain and Northern Ireland + table.add(new MccEntry(232,"at",2,"Europe/Vienna","de")); //Austria + table.add(new MccEntry(234,"gb",2,"Europe/London","en")); //United Kingdom of Great Britain and Northern Ireland + table.add(new MccEntry(235,"gb",2,"Europe/London","en")); //United Kingdom of Great Britain and Northern Ireland table.add(new MccEntry(238,"dk",2)); //Denmark table.add(new MccEntry(240,"se",2)); //Sweden table.add(new MccEntry(242,"no",2)); //Norway @@ -155,8 +199,8 @@ public final class MccTable table.add(new MccEntry(255,"ua",2)); //Ukraine table.add(new MccEntry(257,"by",2)); //Belarus (Republic of) table.add(new MccEntry(259,"md",2)); //Moldova (Republic of) - table.add(new MccEntry(260,"pl",2)); //Poland (Republic of) - table.add(new MccEntry(262,"de",2)); //Germany (Federal Republic of) + table.add(new MccEntry(260,"pl",2,"Europe/Warsaw")); //Poland (Republic of) + table.add(new MccEntry(262,"de",2,"Europe/Berlin","de")); //Germany (Federal Republic of) table.add(new MccEntry(266,"gi",2)); //Gibraltar table.add(new MccEntry(268,"pt",2)); //Portugal table.add(new MccEntry(270,"lu",2)); //Luxembourg @@ -177,13 +221,13 @@ public final class MccTable table.add(new MccEntry(295,"li",2)); //Liechtenstein (Principality of) table.add(new MccEntry(302,"ca",2)); //Canada table.add(new MccEntry(308,"pm",2)); //Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise) - table.add(new MccEntry(310,"us",3)); //United States of America - table.add(new MccEntry(311,"us",3)); //United States of America - table.add(new MccEntry(312,"us",3)); //United States of America - table.add(new MccEntry(313,"us",3)); //United States of America - table.add(new MccEntry(314,"us",3)); //United States of America - table.add(new MccEntry(315,"us",3)); //United States of America - table.add(new MccEntry(316,"us",3)); //United States of America + table.add(new MccEntry(310,"us",3,"","en")); //United States of America + table.add(new MccEntry(311,"us",3,"","en")); //United States of America + table.add(new MccEntry(312,"us",3,"","en")); //United States of America + table.add(new MccEntry(313,"us",3,"","en")); //United States of America + table.add(new MccEntry(314,"us",3,"","en")); //United States of America + table.add(new MccEntry(315,"us",3,"","en")); //United States of America + table.add(new MccEntry(316,"us",3,"","en")); //United States of America table.add(new MccEntry(330,"pr",2)); //Puerto Rico table.add(new MccEntry(332,"vi",2)); //United States Virgin Islands table.add(new MccEntry(334,"mx",3)); //Mexico @@ -254,12 +298,12 @@ public final class MccTable table.add(new MccEntry(470,"bd",2)); //Bangladesh (People's Republic of) table.add(new MccEntry(472,"mv",2)); //Maldives (Republic of) table.add(new MccEntry(502,"my",2)); //Malaysia - table.add(new MccEntry(505,"au",2)); //Australia + table.add(new MccEntry(505,"au",2,"Australia/Sydney","en")); //Australia table.add(new MccEntry(510,"id",2)); //Indonesia (Republic of) table.add(new MccEntry(514,"tl",2)); //Democratic Republic of Timor-Leste table.add(new MccEntry(515,"ph",2)); //Philippines (Republic of the) table.add(new MccEntry(520,"th",2)); //Thailand - table.add(new MccEntry(525,"sg",2)); //Singapore (Republic of) + table.add(new MccEntry(525,"sg",2,"Singapore","en")); //Singapore (Republic of) table.add(new MccEntry(528,"bn",2)); //Brunei Darussalam table.add(new MccEntry(530,"nz",2)); //New Zealand table.add(new MccEntry(534,"mp",2)); //Northern Mariana Islands (Commonwealth of the) diff --git a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java index 79e48f9..6428f70 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java @@ -19,10 +19,12 @@ package com.android.internal.telephony.gsm; import android.os.*; import android.database.Cursor; import android.provider.Telephony; +import android.text.util.Regex; import android.util.EventLog; import android.util.Log; import java.util.ArrayList; +import com.android.internal.telephony.Phone; /** * {@hide} @@ -204,6 +206,9 @@ public class PdpConnection extends Handler { } } else if (state == PdpState.ACTIVATING) { receivedDisconnectReq = true; + } else { + // state == INACTIVE. Nothing to do, so notify immediately. + notifyDisconnect(msg); } } @@ -395,6 +400,8 @@ public class PdpConnection extends Handler { // Don't bother reporting success if there's already a // pending disconnect request, since DataConnectionTracker // has already updated its state. + // Set ACTIVE so that disconnect does the right thing. + state = PdpState.ACTIVE; disconnect(onDisconnect); } else { String[] response = ((String[]) ar.result); @@ -416,10 +423,16 @@ public class PdpConnection extends Handler { if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1])) { // Work around a race condition where QMI does not fill in DNS: // Deactivate PDP and let DataConnectionTracker retry. - EventLog.writeEvent(EVENT_LOG_BAD_DNS_ADDRESS, dnsServers[0]); - phone.mCM.deactivateDefaultPDP(cid, - obtainMessage(EVENT_FORCE_RETRY)); - break; + // Do not apply the race condition workaround for MMS APN + // if Proxy is an IP-address. + // Otherwise, the default APN will not be restored anymore. + if (!apn.types[0].equals(Phone.APN_TYPE_MMS) + || !isIpAddress(apn.mmsProxy)) { + EventLog.writeEvent(EVENT_LOG_BAD_DNS_ADDRESS, dnsServers[0]); + phone.mCM.deactivateDefaultPDP(cid, + obtainMessage(EVENT_FORCE_RETRY)); + break; + } } } @@ -470,4 +483,10 @@ public class PdpConnection extends Handler { break; } } + + private boolean isIpAddress(String address) { + if (address == null) return false; + + return Regex.IP_ADDRESS_PATTERN.matcher(apn.mmsProxy).matches(); + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/RIL.java b/telephony/java/com/android/internal/telephony/gsm/RIL.java index 6739fba..45000ba 100644 --- a/telephony/java/com/android/internal/telephony/gsm/RIL.java +++ b/telephony/java/com/android/internal/telephony/gsm/RIL.java @@ -16,32 +16,32 @@ package com.android.internal.telephony.gsm; +import static com.android.internal.telephony.gsm.RILConstants.*; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.BroadcastReceiver; -import com.android.internal.telephony.*; -import android.os.Parcel; -import java.io.IOException; -import android.os.Message; -import android.os.Handler; -import android.net.LocalSocketAddress; import android.net.LocalSocket; -import com.android.internal.os.HandlerThread; -import android.os.HandlerInterface; -import java.util.ArrayList; -import java.util.Collections; -import java.io.InputStream; +import android.net.LocalSocketAddress; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.telephony.PhoneNumberUtils; import android.telephony.gsm.SmsManager; import android.telephony.gsm.SmsMessage; -import android.util.Log; +import android.telephony.NeighboringCellInfo; import android.util.Config; -import android.os.AsyncResult; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; +import android.util.Log; -import static com.android.internal.telephony.gsm.RILConstants.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; /** * {@hide} @@ -160,7 +160,7 @@ class RILRequest ex = CommandException.fromRilErrno(error); - if (RIL.RILJ_LOG) Log.d(LOG_TAG, serialString() + "< " + if (RIL.RILJ_LOGD) Log.d(LOG_TAG, serialString() + "< " + RIL.requestToString(mRequest) + " error: " + ex); @@ -186,15 +186,15 @@ class RILRequest public final class RIL extends BaseCommands implements CommandsInterface { static final String LOG_TAG = "RILJ"; - private static final boolean DBG = true; - static final boolean RILJ_LOG = true; + private static final boolean DBG = false; + static final boolean RILJ_LOGD = Config.LOGD; + static final boolean RILJ_LOGV = DBG ? Config.LOGD : Config.LOGV; static int WAKE_LOCK_TIMEOUT = 5000; //***** Instance Variables LocalSocket mSocket; - HandlerThread mSenderThread; - Handler mSenderH; + HandlerThread mSenderThread; RILSender mSender; Thread mReceiverThread; RILReceiver mReceiver; @@ -239,8 +239,12 @@ public final class RIL extends BaseCommands implements CommandsInterface } }; - class RILSender implements HandlerInterface,Runnable + class RILSender extends Handler implements Runnable { + public RILSender(Looper looper) { + super(looper); + } + // Only allocated once byte[] dataLength = new byte[4]; @@ -341,7 +345,7 @@ public final class RIL extends BaseCommands implements CommandsInterface // TODO should we clean up mRequestList and mRequestPending synchronized (mWakeLock) { if (mWakeLock.isHeld()) { - if (DBG) { + if (RILJ_LOGD) { synchronized (mRequestsList) { int count = mRequestsList.size(); Log.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " + @@ -512,7 +516,8 @@ public final class RIL extends BaseCommands implements CommandsInterface Log.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed", ex); } catch (Throwable tr) { - Log.e(LOG_TAG, "Uncaught exception read length=" + length, tr); + Log.e(LOG_TAG, "Uncaught exception read length=" + length + + "Exception:" + tr.toString()); } Log.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL @@ -559,9 +564,13 @@ public final class RIL extends BaseCommands implements CommandsInterface mRequestMessagesPending = 0; mContext = context; - mSender = new RILSender(); - mSenderThread = new HandlerThread(mSender, mSender, "RILSender"); - mSenderH = mSenderThread.getHandler(); + + mSenderThread = new HandlerThread("RILSender"); + mSenderThread.start(); + + Looper looper = mSenderThread.getLooper(); + mSender = new RILSender(looper); + mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver"); mReceiverThread.start(); @@ -593,7 +602,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -603,7 +612,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(1); rr.mp.writeString(pin); @@ -616,7 +625,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(2); rr.mp.writeString(puk); @@ -630,7 +639,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN2, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(1); rr.mp.writeString(pin); @@ -643,7 +652,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK2, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(2); rr.mp.writeString(puk); @@ -657,7 +666,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(2); rr.mp.writeString(oldPin); @@ -671,7 +680,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN2, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(2); rr.mp.writeString(oldPin2); @@ -685,7 +694,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(3); rr.mp.writeString(facility); @@ -700,7 +709,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeInt(1); rr.mp.writeString(netpin); @@ -713,7 +722,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -723,7 +732,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_PDP_CONTEXT_LIST, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -736,7 +745,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(address); rr.mp.writeInt(clirMode); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -746,7 +755,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> getIMSI:RIL_REQUEST_GET_IMSI " + RIL_REQUEST_GET_IMSI + " " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> getIMSI:RIL_REQUEST_GET_IMSI " + RIL_REQUEST_GET_IMSI + " " + requestToString(rr.mRequest)); send(rr); } @@ -756,7 +765,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -766,7 +775,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEISV, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -775,11 +784,11 @@ public final class RIL extends BaseCommands implements CommandsInterface public void hangupConnection (int gsmIndex, Message result) { - if (RILJ_LOG) riljLog("hangupConnection: gsmIndex=" + gsmIndex); + if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex); RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + gsmIndex); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + gsmIndex); rr.mp.writeInt(1); rr.mp.writeInt(gsmIndex); @@ -793,7 +802,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -805,7 +814,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain( RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -817,7 +826,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain( RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -828,7 +837,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_CONFERENCE, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -840,7 +849,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_SEPARATE_CONNECTION, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + gsmIndex); rr.mp.writeInt(1); @@ -855,7 +864,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_ANSWER, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -866,7 +875,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_UDUB, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -877,7 +886,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -888,7 +897,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -899,7 +908,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_LAST_PDP_FAIL_CAUSE, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -910,7 +919,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_MUTE, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + enableMute); rr.mp.writeInt(1); @@ -925,7 +934,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_MUTE, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -936,7 +945,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIGNAL_STRENGTH, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -947,7 +956,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_REGISTRATION_STATE, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -958,7 +967,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_GPRS_REGISTRATION_STATE, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -969,7 +978,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_OPERATOR, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -980,7 +989,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_DTMF, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeString(Character.toString(c)); @@ -992,7 +1001,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_DTMF_START, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeString(Character.toString(c)); @@ -1004,7 +1013,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_DTMF_STOP, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1020,7 +1029,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(smscPDU); rr.mp.writeString(pdu); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1032,9 +1041,9 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(index); - if (Config.LOGD) { - if (RILJ_LOG) riljLog(rr.serialString() + "> " - + requestToString(rr.mRequest) + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + " " + index); } @@ -1051,8 +1060,8 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(pdu); rr.mp.writeString(smsc); - if (Config.LOGD) { - if (RILJ_LOG) riljLog(rr.serialString() + "> " + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + status); } @@ -1091,7 +1100,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(user); rr.mp.writeString(password); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + apn); send(rr); @@ -1106,7 +1115,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeString(Integer.toString(cid)); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + cid); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + cid); send(rr); } @@ -1120,7 +1129,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(on ? 1 : 0); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1134,10 +1143,8 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(enable ? 1 : 0); - if (Config.LOGD) { - if (RILJ_LOG) riljLog(rr.serialString() + "> " + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - } send(rr); } @@ -1151,7 +1158,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(success ? 1 : 0); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1172,7 +1179,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(data); rr.mp.writeString(pin2); - if (RILJ_LOG) riljLog(rr.serialString() + "> simIO: " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> simIO: " + requestToString(rr.mRequest) + " 0x" + Integer.toHexString(command) + " 0x" + Integer.toHexString(fileid) + " " + p1 + "," + p2 + "," + p3); @@ -1186,7 +1193,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CLIR, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1202,7 +1209,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(clirMode); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + clirMode); send(rr); @@ -1217,7 +1224,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(serviceClass); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + serviceClass); send(rr); @@ -1233,7 +1240,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(enable ? 1 : 0); rr.mp.writeInt(serviceClass); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + enable + ", " + serviceClass); send(rr); @@ -1246,7 +1253,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1258,7 +1265,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + operatorNumeric); rr.mp.writeString(operatorNumeric); @@ -1273,7 +1280,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1285,7 +1292,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1304,7 +1311,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(number); rr.mp.writeInt (timeSeconds); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + action + " " + cfReason + " " + serviceClass + timeSeconds); @@ -1325,7 +1332,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeString(number); rr.mp.writeInt (0); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + cfReason + " " + serviceClass); send(rr); @@ -1337,7 +1344,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_QUERY_CLIP, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1349,7 +1356,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_BASEBAND_VERSION, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1360,7 +1367,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_QUERY_FACILITY_LOCK, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); // count strings rr.mp.writeInt(3); @@ -1381,7 +1388,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_FACILITY_LOCK, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); // count strings rr.mp.writeInt(4); @@ -1402,7 +1409,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_SEND_USSD, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + ussdString); rr.mp.writeString(ussdString); @@ -1415,7 +1422,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_CANCEL_USSD, response); - if (RILJ_LOG) riljLog(rr.serialString() + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); @@ -1427,7 +1434,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_RESET_RADIO, result); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1437,7 +1444,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_RAW, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + "[" + SimUtils.bytesToHexString(data) + "]"); rr.mp.writeByteArray(data); @@ -1451,7 +1458,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_STRINGS, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeStringArray(strings); @@ -1471,7 +1478,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(bandMode); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + bandMode); send(rr); @@ -1489,7 +1496,7 @@ public final class RIL extends BaseCommands implements CommandsInterface = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1501,7 +1508,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeString(contents); send(rr); @@ -1514,7 +1521,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeString(contents); send(rr); @@ -1530,7 +1537,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); int[] param = new int[1]; param[0] = accept ? 1 : 0; @@ -1548,7 +1555,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(networkType); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " : " + networkType); send(rr); @@ -1561,7 +1568,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1573,7 +1580,7 @@ public final class RIL extends BaseCommands implements CommandsInterface RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, response); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @@ -1586,7 +1593,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(enable ? 1 : 0); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ": " + enable); send(rr); @@ -1600,7 +1607,7 @@ public final class RIL extends BaseCommands implements CommandsInterface rr.mp.writeInt(1); rr.mp.writeInt(on ? 1 : 0); - if (RILJ_LOG) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ": " + on); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ": " + on); send(rr); } @@ -1640,7 +1647,7 @@ public final class RIL extends BaseCommands implements CommandsInterface * (eg, if it or the runtime crashed) without the RIL * and/or radio knowing. */ - if (DBG) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF"); + if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF"); setRadioPower(false, null); return; } @@ -1665,9 +1672,9 @@ public final class RIL extends BaseCommands implements CommandsInterface mWakeLock.acquire(); mRequestMessagesPending++; - mSenderH.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); - Message msg = mSenderH.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); - mSenderH.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT); + mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); + Message msg = mSender.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); + mSender.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT); } } @@ -1678,7 +1685,7 @@ public final class RIL extends BaseCommands implements CommandsInterface if (mWakeLock.isHeld() && (mRequestMessagesPending == 0) && (mRequestsList.size() == 0)) { - mSenderH.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); + mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); mWakeLock.release(); } } @@ -1689,7 +1696,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { Message msg; - msg = mSenderH.obtainMessage(EVENT_SEND, rr); + msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); @@ -1837,7 +1844,7 @@ public final class RIL extends BaseCommands implements CommandsInterface case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break; case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break; - case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseStrings(p); break; + case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break; case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break; default: @@ -1857,7 +1864,7 @@ public final class RIL extends BaseCommands implements CommandsInterface return; } - if (RILJ_LOG) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + " " + retToString(rr.mRequest, ret)); if (rr.mResult != null) { @@ -1916,6 +1923,14 @@ public final class RIL extends BaseCommands implements CommandsInterface sb.append("[").append(dc).append("] "); } s = sb.toString(); + } else if (req == RIL_REQUEST_GET_NEIGHBORING_CELL_IDS) { + ArrayList<NeighboringCellInfo> cells; + cells = (ArrayList<NeighboringCellInfo>) ret; + sb = new StringBuilder(" "); + for (NeighboringCellInfo cell : cells) { + sb.append(cell).append(" "); + } + s = sb.toString(); } else { s = ret.toString(); } @@ -1959,8 +1974,8 @@ public final class RIL extends BaseCommands implements CommandsInterface throw new RuntimeException("Unrecognized unsol response: " + response); //break; (implied) }} catch (Throwable tr) { - Log.e(LOG_TAG, "Exception processing unsol response: " - + response, tr); + Log.e(LOG_TAG, "Exception processing unsol response: " + response + + "Exception:" + tr.toString()); return; } @@ -1969,22 +1984,22 @@ public final class RIL extends BaseCommands implements CommandsInterface /* has bonus radio state int */ setRadioStateFromRILInt(p.readInt()); - if (RILJ_LOG) riljLog("[UNSL]< RADIO_STATE_CHANGED " +mState); + if (RILJ_LOGD) unsljLogMore(response, mState.toString()); break; case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: - if (RILJ_LOG) riljLog("[UNSL]< CALL_STATE_CHANGED"); + if (RILJ_LOGD) unsljLog(response); mCallStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); break; case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: - if (RILJ_LOG) riljLog("[UNSL]< NETWORK_STATE_CHANGED"); + if (RILJ_LOGD) unsljLog(response); mNetworkStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); break; case RIL_UNSOL_RESPONSE_NEW_SMS: { - if (RILJ_LOG) riljLog("[UNSL]< NEW_SMS"); + if (RILJ_LOGD) unsljLog(response); // FIXME this should move up a layer String a[] = new String[2]; @@ -2001,9 +2016,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; } case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< NEW_SMS_STATUS_REPORT " + ret); - } + if (RILJ_LOGD) unsljLogRet(response, ret); if (mSmsStatusRegistrant != null) { mSmsStatusRegistrant.notifyRegistrant( @@ -2011,7 +2024,7 @@ public final class RIL extends BaseCommands implements CommandsInterface } break; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: - if (RILJ_LOG) riljLog("[UNSL]< NEW_SMS_ON_SIM" + retToString(response, ret)); + if (RILJ_LOGD) unsljLogRet(response, ret); int[] smsIndex = (int[])ret; @@ -2021,7 +2034,7 @@ public final class RIL extends BaseCommands implements CommandsInterface notifyRegistrant(new AsyncResult(null, smsIndex, null)); } } else { - if (RILJ_LOG) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length " + if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length " + smsIndex.length); } break; @@ -2033,23 +2046,23 @@ public final class RIL extends BaseCommands implements CommandsInterface resp[0] = ((String[])ret)[0]; resp[1] = null; } - if (RILJ_LOG) riljLog("[UNSL]< ON_USSD " + resp[0]); + if (RILJ_LOGD) unsljLogMore(response, resp[0]); if (mUSSDRegistrant != null) { mUSSDRegistrant.notifyRegistrant( new AsyncResult (null, resp, null)); } break; case RIL_UNSOL_NITZ_TIME_RECEIVED: - if (RILJ_LOG) riljLog("[UNSL]< NITZ_TIME_RECEIVED " + retToString(response, ret)); + if (RILJ_LOGD) unsljLogRet(response, ret); - // has bonus int containing time_t that the NITZ + // has bonus long containing milliseconds since boot that the NITZ // time was received - int nitzReceiveTime = p.readInt(); + long nitzReceiveTime = p.readLong(); Object[] result = new Object[2]; result[0] = ret; - result[1] = Integer.valueOf(nitzReceiveTime); + result[1] = Long.valueOf(nitzReceiveTime); if (mNITZTimeRegistrant != null) { @@ -2064,8 +2077,7 @@ public final class RIL extends BaseCommands implements CommandsInterface case RIL_UNSOL_SIGNAL_STRENGTH: // Note this is set to "verbose" because it happens // frequently - if (Config.LOGV) Log.v(LOG_TAG, "[UNSL]< SIGNAL_STRENGTH " - + retToString(response, ret)); + if (RILJ_LOGV) unsljLogvRet(response, ret); if (mSignalStrengthRegistrant != null) { mSignalStrengthRegistrant.notifyRegistrant( @@ -2073,17 +2085,14 @@ public final class RIL extends BaseCommands implements CommandsInterface } break; case RIL_UNSOL_PDP_CONTEXT_LIST_CHANGED: - if (RILJ_LOG) riljLog("[UNSL]< PDP_CONTEXT_CHANGED " + retToString(response, ret)); + if (RILJ_LOGD) unsljLogRet(response, ret); mPDPRegistrants .notifyRegistrants(new AsyncResult(null, ret, null)); break; case RIL_UNSOL_SUPP_SVC_NOTIFICATION: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< SUPP_SVC_NOTIFICATION " - + retToString(response, ret)); - } + if (RILJ_LOGD) unsljLogRet(response, ret); if (mSsnRegistrant != null) { mSsnRegistrant.notifyRegistrant( @@ -2092,9 +2101,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_STK_SESSION_END: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< STK_SESSION_END"); - } + if (RILJ_LOGD) unsljLog(response); if (mStkSessionEndRegistrant != null) { mStkSessionEndRegistrant.notifyRegistrant( @@ -2103,10 +2110,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_STK_PROACTIVE_COMMAND: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< STK_PROACTIVE_COMMAND " - + retToString(response, ret)); - } + if (RILJ_LOGD) unsljLogRet(response, ret); if (mStkProCmdRegistrant != null) { mStkProCmdRegistrant.notifyRegistrant( @@ -2115,10 +2119,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_STK_EVENT_NOTIFY: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< STK_EVENT_NOTIFY " - + retToString(response, ret)); - } + if (RILJ_LOGD) unsljLogRet(response, ret); if (mStkEventRegistrant != null) { mStkEventRegistrant.notifyRegistrant( @@ -2127,10 +2128,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_STK_CALL_SETUP: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< STK_CALL_SETUP " - + retToString(response, ret)); - } + if (RILJ_LOGD) unsljLogRet(response, ret); if (mStkCallSetUpRegistrant != null) { mStkCallSetUpRegistrant.notifyRegistrant( @@ -2139,9 +2137,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_SIM_SMS_STORAGE_FULL: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< SIM_SMS_STORAGE_FULL"); - } + if (RILJ_LOGD) unsljLog(response); if (mSimSmsFullRegistrant != null) { mSimSmsFullRegistrant.notifyRegistrant(); @@ -2149,9 +2145,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_SIM_REFRESH: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< SIM_REFRESH " + retToString(response, ret)); - } + if (RILJ_LOGD) unsljLogRet(response, ret); if (mSimRefreshRegistrant != null) { mSimRefreshRegistrant.notifyRegistrant( @@ -2160,9 +2154,7 @@ public final class RIL extends BaseCommands implements CommandsInterface break; case RIL_UNSOL_CALL_RING: - if (Config.LOGD) { - if (RILJ_LOG) riljLog("[UNSL]< CALL_RING "); - } + if (RILJ_LOGD) unsljLog(response); if (mRingRegistrant != null) { mRingRegistrant.notifyRegistrant(); @@ -2410,6 +2402,29 @@ public final class RIL extends BaseCommands implements CommandsInterface return ret; } + private Object + responseCellList(Parcel p) + { + int num; + ArrayList<NeighboringCellInfo> response; + NeighboringCellInfo cell; + + num = p.readInt(); + response = new ArrayList<NeighboringCellInfo>(num); + + for (int i = 0 ; i < num ; i++) { + try { + int rssi = p.readInt(); + int cid = Integer.valueOf(p.readString(), 16); + cell = new NeighboringCellInfo(rssi, cid); + response.add(cell); + } catch ( Exception e) { + } + } + + return response; + } + static String requestToString(int request) @@ -2434,7 +2449,7 @@ public final class RIL extends BaseCommands implements CommandsInterface case RIL_REQUEST_HANGUP: return "HANGUP"; case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "HANGUP_WAITING_OR_BACKGROUND"; case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "HANGUP_FOREGROUND_RESUME_BACKGROUND"; - case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE"; + case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE"; case RIL_REQUEST_CONFERENCE: return "CONFERENCE"; case RIL_REQUEST_UDUB: return "UDUB"; case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE"; @@ -2486,21 +2501,74 @@ public final class RIL extends BaseCommands implements CommandsInterface case RIL_REQUEST_DELETE_SMS_ON_SIM: return "DELETE_SMS_ON_SIM"; case RIL_REQUEST_SET_BAND_MODE: return "SET_BAND_MODE"; case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "QUERY_AVAILABLE_BAND_MODE"; - case RIL_REQUEST_STK_GET_PROFILE: return "RIL_REQUEST_STK_GET_PROFILE"; - case RIL_REQUEST_STK_SET_PROFILE: return "RIL_REQUEST_STK_SET_PROFILE"; - case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND"; - case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE"; - case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM"; - case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "RIL_REQUEST_EXPLICIT_CALL_TRANSFER"; - case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE"; - case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE"; - case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "RIL_REQUEST_GET_NEIGHBORING_CELL_IDS"; - case RIL_REQUEST_SET_LOCATION_UPDATES: return "RIL_REQUEST_SET_LOCATION_UPDATES"; + case RIL_REQUEST_STK_GET_PROFILE: return "REQUEST_STK_GET_PROFILE"; + case RIL_REQUEST_STK_SET_PROFILE: return "REQUEST_STK_SET_PROFILE"; + case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "REQUEST_STK_SEND_ENVELOPE_COMMAND"; + case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "REQUEST_STK_SEND_TERMINAL_RESPONSE"; + case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM"; + case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER"; + case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "REQUEST_SET_PREFERRED_NETWORK_TYPE"; + case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "REQUEST_GET_PREFERRED_NETWORK_TYPE"; + case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "REQUEST_GET_NEIGHBORING_CELL_IDS"; + case RIL_REQUEST_SET_LOCATION_UPDATES: return "REQUEST_SET_LOCATION_UPDATES"; default: return "<unknown request>"; } } + static String + responseToString(int request) + { +/* + cat libs/telephony/ril_unsol_commands.h \ + | egrep "^ *{RIL_" \ + | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/' +*/ + switch(request) { + case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED"; + case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED"; + case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED"; + case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS"; + case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT"; + case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM"; + case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD"; + case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST"; + case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED"; + case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH"; + case RIL_UNSOL_PDP_CONTEXT_LIST_CHANGED: return "UNSOL_PDP_CONTEXT_LIST_CHANGED"; + case RIL_UNSOL_SUPP_SVC_NOTIFICATION: return "UNSOL_SUPP_SVC_NOTIFICATION"; + case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END"; + case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND"; + case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY"; + case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP"; + case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL"; + case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; + case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; + default: return "<unknown reponse>"; + } + } + private void riljLog(String msg) { Log.d(LOG_TAG, msg); } + + private void riljLogv(String msg) { + Log.v(LOG_TAG, msg); + } + + private void unsljLog(int response) { + riljLog("[UNSL]< " + responseToString(response)); + } + + private void unsljLogMore(int response, String more) { + riljLog("[UNSL]< " + responseToString(response) + " " + more); + } + + private void unsljLogRet(int response, Object ret) { + riljLog("[UNSL]< " + responseToString(response) + " " + retToString(response, ret)); + } + + private void unsljLogvRet(int response, Object ret) { + riljLogv("[UNSL]< " + responseToString(response) + " " + retToString(response, ret)); + } + } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index ebbf501..75f56a3 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -16,14 +16,21 @@ package com.android.internal.telephony.gsm; +import android.app.AlarmManager; +import android.content.Context; import android.os.AsyncResult; import android.os.RegistrantList; import android.os.Registrant; import android.os.Handler; import android.os.Message; +import android.os.SystemProperties; import android.telephony.gsm.SmsMessage; import android.util.Log; import java.util.ArrayList; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import java.util.Locale; +import android.content.res.Configuration; import static com.android.internal.telephony.TelephonyProperties.*; import com.android.internal.telephony.SimCard; @@ -149,6 +156,8 @@ public final class SIMRecords extends Handler implements SimConstants private static final int EVENT_SIM_REFRESH = 31; private static final int EVENT_GET_CFIS_DONE = 32; + private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; + //***** Constructor SIMRecords(GSMPhone phone) @@ -199,7 +208,6 @@ public final class SIMRecords extends Handler implements SimConstants adnCache.reset(); - phone.setSystemProperty(PROPERTY_LINE1_VOICE_MAIL_WAITING, null); phone.setSystemProperty(PROPERTY_SIM_OPERATOR_NUMERIC, null); phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ALPHA, null); phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ISO_COUNTRY, null); @@ -309,7 +317,7 @@ public final class SIMRecords extends Handler implements SimConstants Message onComplete) { if (isVoiceMailFixed) { AsyncResult.forMessage((onComplete)).exception = - new SimException("Voicemail number is fixed by operator"); + new SimVmFixedException("Voicemail number is fixed by operator"); onComplete.sendToTarget(); return; } @@ -331,9 +339,9 @@ public final class SIMRecords extends Handler implements SimConstants EF_EXT1, 1, null, obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete)); - } else { + }else { AsyncResult.forMessage((onComplete)).exception = - new SimException("Update SIM voice mailbox error"); + new SimVmNotSupportedException("Update SIM voice mailbox error"); onComplete.sendToTarget(); } } @@ -369,9 +377,6 @@ public final class SIMRecords extends Handler implements SimConstants countVoiceMessages = countWaiting; - phone.setSystemProperty(PROPERTY_LINE1_VOICE_MAIL_WAITING, - (countVoiceMessages != 0) ? "true" : "false"); - phone.notifyMessageWaitingIndicator(); try { @@ -437,9 +442,6 @@ public final class SIMRecords extends Handler implements SimConstants callForwardingEnabled = enable; - phone.setSystemProperty(PROPERTY_LINE1_VOICE_CALL_FORWARDING, - (callForwardingEnabled ? "true" : "false")); - phone.notifyCallForwardingIndicator(); try { @@ -528,6 +530,73 @@ public final class SIMRecords extends Handler implements SimConstants } } + /** + * If the timezone is not already set, set it based on the MCC of the SIM. + * @param mcc Mobile Country Code of the SIM + */ + private void setTimezoneFromMccIfNeeded(int mcc) { + String timezone = SystemProperties.get(TIMEZONE_PROPERTY); + if (timezone == null || timezone.length() == 0) { + String zoneId = MccTable.defaultTimeZoneForMcc(mcc); + + if (zoneId != null && zoneId.length() > 0) { + // Set time zone based on MCC + AlarmManager alarm = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + alarm.setTimeZone(zoneId); + } + } + } + + /** + * If the locale is not already set, set it based on the MCC of the SIM. + * @param mcc Mobile Country Code of the SIM + */ + private void setLocaleFromMccIfNeeded(int mcc) { + String language = SystemProperties.get("persist.sys.language"); + String country = SystemProperties.get("persist.sys.country"); + Log.d(LOG_TAG,"setLocaleFromMcc"); + if((language == null || language.length() == 0) && (country == null || country.length() == 0)) { + try { + language = MccTable.defaultLanguageForMcc(mcc); + country = MccTable.countryCodeForMcc(mcc).toUpperCase(); + // try to find a good match + String[] locales = phone.getContext().getAssets().getLocales(); + final int N = locales.length; + String bestMatch = null; + for(int i = 0; i < N; i++) { + Log.d(LOG_TAG," trying "+locales[i]); + if(locales[i]!=null && locales[i].length() >= 2 && + locales[i].substring(0,2).equals(language)) { + if(locales[i].length() >= 5 && + locales[i].substring(3,5).equals(country)) { + bestMatch = locales[i]; + break; + } else if(bestMatch == null) { + bestMatch = locales[i]; + } + } + } + Log.d(LOG_TAG," got bestmatch = "+bestMatch); + if(bestMatch != null) { + IActivityManager am = ActivityManagerNative.getDefault(); + Configuration config = am.getConfiguration(); + + if(bestMatch.length() >= 5) { + config.locale = new Locale(bestMatch.substring(0,2), + bestMatch.substring(3,5)); + } else { + config.locale = new Locale(bestMatch.substring(0,2)); + } + config.userSetLocale = true; + am.updateConfiguration(config); + } + } catch (Exception e) { + // Intentionally left blank + } + } + } + //***** Overridden from Handler public void handleMessage(Message msg) { @@ -554,7 +623,7 @@ public final class SIMRecords extends Handler implements SimConstants ar = (AsyncResult)msg.obj; if (ar.exception != null) { - Log.e(LOG_TAG, "Exception querying IMSI", ar.exception); + Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception); break; } @@ -571,6 +640,10 @@ public final class SIMRecords extends Handler implements SimConstants phone.mSimCard.updateImsiConfiguration(imsi); phone.mSimCard.broadcastSimStateChangedIntent( SimCard.INTENT_VALUE_SIM_IMSI, null); + + int mcc = Integer.parseInt(imsi.substring(0, 3)); + setTimezoneFromMccIfNeeded(mcc); + setLocaleFromMccIfNeeded(mcc); break; case EVENT_GET_MBI_DONE: @@ -716,8 +789,6 @@ public final class SIMRecords extends Handler implements SimConstants countVoiceMessages = -1; } - phone.setSystemProperty(PROPERTY_LINE1_VOICE_MAIL_WAITING, - voiceMailWaiting ? "true" : "false"); phone.notifyMessageWaitingIndicator(); break; @@ -747,9 +818,6 @@ public final class SIMRecords extends Handler implements SimConstants countVoiceMessages = 0; } - phone.setSystemProperty(PROPERTY_LINE1_VOICE_MAIL_WAITING, - countVoiceMessages != 0 - ? "true" : "false"); phone.notifyMessageWaitingIndicator(); } break; @@ -831,9 +899,6 @@ public final class SIMRecords extends Handler implements SimConstants callForwardingEnabled = ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE); - phone.setSystemProperty(PROPERTY_LINE1_VOICE_CALL_FORWARDING, - (callForwardingEnabled ? "true" : "false")); - phone.notifyCallForwardingIndicator(); } break; @@ -1010,6 +1075,7 @@ public final class SIMRecords extends Handler implements SimConstants case EVENT_SIM_REFRESH: isRecordLoadResponse = false; ar = (AsyncResult)msg.obj; + if (DBG) log("Sim REFRESH with exception: " + ar.exception); if (ar.exception == null) { handleSimRefresh((int[])(ar.result)); } @@ -1032,9 +1098,6 @@ public final class SIMRecords extends Handler implements SimConstants // Refer TS 51.011 Section 10.3.46 for the content description callForwardingEnabled = ((data[1] & 0x01) != 0); - phone.setSystemProperty(PROPERTY_LINE1_VOICE_CALL_FORWARDING, - (callForwardingEnabled ? "true" : "false")); - phone.notifyCallForwardingIndicator(); break; @@ -1071,22 +1134,27 @@ public final class SIMRecords extends Handler implements SimConstants } } - private void handleSimRefresh(int[] result) { + private void handleSimRefresh(int[] result) { if (result == null || result.length == 0) { + if (DBG) log("handleSimRefresh without input"); return; } - + switch ((result[0])) { case CommandsInterface.SIM_REFRESH_FILE_UPDATED: - // result[1] contains the EFID of the updated file. + if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED"); + // result[1] contains the EFID of the updated file. int efid = result[1]; handleFileUpdate(efid); break; case CommandsInterface.SIM_REFRESH_INIT: + if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT"); // need to reload all files (that we care about) + adnCache.reset(); fetchSimRecords(); break; case CommandsInterface.SIM_REFRESH_RESET: + if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET"); phone.mCM.setRadioPower(false, null); /* Note: no need to call setRadioPower(true). Assuming the desired * radio power state is still ON (as tracked by ServiceStateTracker), @@ -1098,6 +1166,7 @@ public final class SIMRecords extends Handler implements SimConstants break; default: // unknown refresh operation + if (DBG) log("handleSimRefresh with unknown operation"); break; } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java index 258c058..daf4b9c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java @@ -33,6 +33,7 @@ import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.provider.Telephony; +import android.provider.Settings; import android.provider.Telephony.Sms.Intents; import android.telephony.gsm.SmsMessage; import android.telephony.gsm.SmsManager; @@ -56,7 +57,7 @@ final class SMSDispatcher extends Handler { private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; /** Default number of SMS sent in checking period without uesr permit */ - private static final int DEFAULT_SMS_MAX_ALLOWED = 100; + private static final int DEFAULT_SMS_MAX_COUNT = 100; /** Default timeout for SMS sent query */ private static final int DEFAULT_SMS_TIMOUEOUT = 6000; @@ -199,8 +200,14 @@ final class SMSDispatcher extends Handler { mResolver = mContext.getContentResolver(); mCm = phone.mCM; mSTracker = null; - mCounter = new SmsCounter(DEFAULT_SMS_MAX_ALLOWED, + + int check_period = Settings.Gservices.getInt(mResolver, + Settings.Gservices.SMS_OUTGOING_CEHCK_INTERVAL_MS, DEFAULT_SMS_CHECK_PERIOD); + int max_count = Settings.Gservices.getInt(mResolver, + Settings.Gservices.SMS_OUTGOING_CEHCK_MAX_COUNT, + DEFAULT_SMS_MAX_COUNT); + mCounter = new SmsCounter(max_count, check_period); mCm.setOnNewSMS(this, EVENT_NEW_SMS, null); mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); @@ -245,8 +252,7 @@ final class SMSDispatcher extends Handler { } if (ar.exception != null) { - Log.e(TAG, "Exception processing incoming SMS", - ar.exception); + Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); return; } diff --git a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java index 1c337f9..a4b8b47 100644 --- a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java @@ -18,7 +18,6 @@ package com.android.internal.telephony.gsm; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISMANUAL; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; @@ -45,8 +44,10 @@ import android.provider.Telephony.Intents; import android.telephony.gsm.GsmCellLocation; import android.telephony.ServiceState; import android.text.TextUtils; +import android.util.Config; import android.util.Log; import android.util.TimeUtils; +import android.util.EventLog; import java.util.Arrays; import java.util.Calendar; @@ -130,6 +131,12 @@ final class ServiceStateTracker extends Handler // SIMRecords object may not be instantiated yet. private boolean mNeedToRegForSimLoaded; + // Started the recheck process after finding gprs should registerd but not + private boolean mStartedGprsRegCheck = false; + // Already sent the event-log for no gprs register + private boolean mReportedGprsNoReg = false; + + // Keep track of SPN display rules, so we only broadcast intent if something changes. private String curSpn = null; private String curPlmn = null; @@ -143,6 +150,9 @@ final class ServiceStateTracker extends Handler // signal strength poll rate static final int POLL_PERIOD_MILLIS = 20 * 1000; + // waiting period before recheck gprs and voice registration + static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; + //***** Events static final int EVENT_RADIO_STATE_CHANGED = 1; @@ -163,7 +173,11 @@ final class ServiceStateTracker extends Handler static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19; static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20; static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; + static final int EVENT_CHECK_REPORT_GPRS = 22; + // Event Log Tags + private static final int EVENT_LOG_CGREG_FAIL = 50107; + private static final int EVENT_DATA_STATE_RADIO_OFF = 50108; //***** Time Zones @@ -460,7 +474,7 @@ final class ServiceStateTracker extends Handler ar = (AsyncResult) msg.obj; String nitzString = (String)((Object[])ar.result)[0]; - int nitzReceiveTime = ((Integer)((Object[])ar.result)[1]).intValue(); + long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); setTimeFromNITZString(nitzString, nitzReceiveTime); break; @@ -523,6 +537,23 @@ final class ServiceStateTracker extends Handler cm.setPreferredNetworkType(toggledNetworkType, message); break; + case EVENT_CHECK_REPORT_GPRS: + if (ss != null && !isGprsConsistant(gprsState, ss.getState())) { + + // Can't register data sevice while voice service is ok + // i.e. CREG is ok while CGREG is not + // possible a network or baseband side error + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(ss.getOperatorNumeric(), cid); + EventLog.writeEvent(EVENT_LOG_CGREG_FAIL, val); + mReportedGprsNoReg = true; + } + mStartedGprsRegCheck = false; + break; + } } @@ -561,6 +592,14 @@ final class ServiceStateTracker extends Handler ) { cm.setRadioPower(true, null); } else if (!mDesiredPowerState && cm.getRadioState().isOn()) { + DataConnectionTracker dcTracker = phone.mDataConnection; + if (! dcTracker.isDataConnectionAsDesired()) { + + EventLog.List val = new EventLog.List( + dcTracker.getStateInString(), + (dcTracker.getAnyDataEnabled() ? 1 : 0) ); + EventLog.writeEvent(EVENT_DATA_STATE_RADIO_OFF, val); + } // If it's on and available and we want it off.. cm.setRadioPower(false, null); } // Otherwise, we're in the desired state @@ -609,7 +648,7 @@ final class ServiceStateTracker extends Handler if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW && err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) { Log.e(LOG_TAG, - "RIL implementation has returned an error where it must succeed", + "RIL implementation has returned an error where it must succeed" + ar.exception); } } else try { @@ -882,8 +921,7 @@ final class ServiceStateTracker extends Handler // need adjust time to reflect default timezone setting long tzOffset; tzOffset = zone.getOffset(System.currentTimeMillis()); - SystemClock.setCurrentTimeMillis( - System.currentTimeMillis() - tzOffset); + setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); } else if (iso.equals("")){ // Country code not found. This is likely a test network. // Get a TimeZone based only on the NITZ parameters (best guess). @@ -896,10 +934,8 @@ final class ServiceStateTracker extends Handler mNeedFixZone = false; if (zone != null) { - Context context = phone.getContext(); if (getAutoTime()) { - AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarm.setTimeZone(zone.getID()); + setAndBroadcastNetworkSetTimeZone(zone.getID()); } saveNitzTimeZone(zone.getID()); } @@ -908,8 +944,6 @@ final class ServiceStateTracker extends Handler phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, ss.getRoaming() ? "true" : "false"); - phone.setSystemProperty(PROPERTY_OPERATOR_ISMANUAL, - ss.getIsManualSelection() ? "true" : "false"); updateSpnDisplay(); phone.notifyServiceStateChanged(ss); @@ -938,8 +972,35 @@ final class ServiceStateTracker extends Handler if (hasLocationChanged) { phone.notifyLocationChanged(); } + + if (! isGprsConsistant(gprsState, ss.getState())) { + if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { + mStartedGprsRegCheck = true; + + int check_period = Settings.Gservices.getInt( + phone.getContext().getContentResolver(), + Settings.Gservices.GPRS_REGISTER_CHECK_PERIOD_MS, + DEFAULT_GPRS_CHECK_PERIOD_MILLIS); + sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS), + check_period); + } + } else { + mReportedGprsNoReg = false; + } } - + + /** + * Check if GPRS got registred while voice is registered + * + * @param gprsState for GPRS registration state, i.e. CGREG in GSM + * @param serviceState for voice registration state, i.e. CREG in GSM + * @return false if device only register to voice but not gprs + */ + private boolean isGprsConsistant (int gprsState, int serviceState) { + return !((serviceState == ServiceState.STATE_IN_SERVICE) && + (gprsState != ServiceState.STATE_IN_SERVICE)); + } + /** * Returns a TimeZone object based only on parameters from the NITZ string. */ @@ -1162,13 +1223,14 @@ final class ServiceStateTracker extends Handler */ private - void setTimeFromNITZString (String nitz, int nitzReceiveTime) + void setTimeFromNITZString (String nitz, long nitzReceiveTime) { // "yy/mm/dd,hh:mm:ss(+/-)tz" // tz is in number of quarter-hours - Log.i(LOG_TAG, "setTimeFromNITZString: " + - nitz + "," + nitzReceiveTime); + long start = SystemClock.elapsedRealtime(); + Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime + + " start=" + start + " delay=" + (start - nitzReceiveTime)); try { /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone @@ -1257,57 +1319,57 @@ final class ServiceStateTracker extends Handler } if (zone != null) { - Context context = phone.getContext(); if (getAutoTime()) { - AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarm.setTimeZone(zone.getID()); + setAndBroadcastNetworkSetTimeZone(zone.getID()); } saveNitzTimeZone(zone.getID()); } - - long millisSinceNitzReceived - = System.currentTimeMillis() - (nitzReceiveTime * 1000L); - - if (millisSinceNitzReceived < 0) { - // Sanity check: something is wrong - Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " - + "backwards since NITZ time received, " - + nitz); - return; - } - - if (millisSinceNitzReceived > (1000L * 1000L)) { - // If the time is this far off, something is wrong - Log.i(LOG_TAG, "NITZ: not setting time, more than 1000 seconds " - + " have elapsed since time received, " - + nitz); - - return; - } - - // Note: with range checks above, cast to int is safe - c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); - + String ignore = SystemProperties.get("gsm.ignore-nitz"); if (ignore != null && ignore.equals("yes")) { - Log.i(LOG_TAG, - "Not setting clock because gsm.ignore-nitz is set"); + Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); return; } if (getAutoTime()) { - Log.i(LOG_TAG, "Setting time of day to " + c.getTime() + long millisSinceNitzReceived + = SystemClock.elapsedRealtime() - nitzReceiveTime; + + if (millisSinceNitzReceived < 0) { + // Sanity check: something is wrong + Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " + + "backwards since NITZ time was received, " + + nitz); + return; + } + + if (millisSinceNitzReceived > Integer.MAX_VALUE) { + // If the time is this far off, something is wrong > 24 days! + Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " + + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) + + " days"); + return; + } + + // Note: with range checks above, cast to int is safe + c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); + + Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() + " NITZ receive delay(ms): " + millisSinceNitzReceived + " gained(ms): " + (c.getTimeInMillis() - System.currentTimeMillis()) + " from " + nitz); - SystemClock.setCurrentTimeMillis(c.getTimeInMillis()); + setAndBroadcastNetworkSetTime(c.getTimeInMillis()); } SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); saveNitzTime(c.getTimeInMillis()); + if (Config.LOGV) { + long end = SystemClock.elapsedRealtime(); + Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); + } } catch (RuntimeException ex) { - Log.e(LOG_TAG, "Parsing NITZ time " + nitz, ex); + Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); } } @@ -1319,26 +1381,44 @@ final class ServiceStateTracker extends Handler return true; } } - + private void saveNitzTimeZone(String zoneId) { mSavedTimeZone = zoneId; - // Send out a sticky broadcast so the system can determine if - // the timezone was set by the carrier... - Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); - intent.putExtra("time-zone", zoneId); - phone.getContext().sendStickyBroadcast(intent); } - + private void saveNitzTime(long time) { mSavedTime = time; mSavedAtTime = SystemClock.elapsedRealtime(); - // Send out a sticky broadcast so the system can determine if - // the time was set by the carrier... + } + + /** + * Set the timezone and send out a sticky broadcast so the system can + * determine if the timezone was set by the carrier. + * + * @param zoneId timezone set by carrier + */ + private void setAndBroadcastNetworkSetTimeZone(String zoneId) { + AlarmManager alarm = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + alarm.setTimeZone(zoneId); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); + intent.putExtra("time-zone", zoneId); + phone.getContext().sendStickyBroadcast(intent); + } + + /** + * Set the time and Send out a sticky broadcast so the system can determine + * if the time was set by the carrier. + * + * @param time time set by network + */ + private void setAndBroadcastNetworkSetTime(long time) { + SystemClock.setCurrentTimeMillis(time); Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); intent.putExtra("time", time); phone.getContext().sendStickyBroadcast(intent); } - + private void revertToNitz() { if (Settings.System.getInt(phone.getContext().getContentResolver(), Settings.System.AUTO_TIME, 0) == 0) { @@ -1348,10 +1428,8 @@ final class ServiceStateTracker extends Handler + "' mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime); if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) { - AlarmManager alarm = - (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); - alarm.setTimeZone(mSavedTimeZone); - SystemClock.setCurrentTimeMillis(mSavedTime + setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); + setAndBroadcastNetworkSetTime(mSavedTime + (SystemClock.elapsedRealtime() - mSavedAtTime)); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SimException.java b/telephony/java/com/android/internal/telephony/gsm/SimException.java index 4a9d266..1c0daba 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimException.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimException.java @@ -31,3 +31,28 @@ public class SimException extends Exception super(s); } } + +final class SimVmFixedException extends SimException { + SimVmFixedException() + { + + } + + SimVmFixedException(String s) + { + super(s); + } +} + +final class SimVmNotSupportedException extends SimException { + SimVmNotSupportedException() + { + + } + + SimVmNotSupportedException(String s) + { + super(s); + } +} + diff --git a/telephony/java/com/android/internal/telephony/gsm/SimUtils.java b/telephony/java/com/android/internal/telephony/gsm/SimUtils.java index 5cd5a90..2eecdba 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimUtils.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimUtils.java @@ -326,4 +326,151 @@ public class SimUtils return ret; } + + /** + * Convert a TS 131.102 image instance of code scheme '11' into Bitmap + * @param data The raw data + * @param length The length of image body + * @return The bitmap + */ + public static Bitmap parseToBnW(byte[] data, int length){ + int valueIndex = 0; + int width = data[valueIndex++] & 0xFF; + int height = data[valueIndex++] & 0xFF; + int numOfPixels = width*height; + + int[] pixels = new int[numOfPixels]; + + int pixelIndex = 0; + int bitIndex = 7; + byte currentByte = 0x00; + while (pixelIndex < numOfPixels) { + // reassign data and index for every byte (8 bits). + if (pixelIndex % 8 == 0) { + currentByte = data[valueIndex++]; + bitIndex = 7; + } + pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01); + }; + + if (pixelIndex != numOfPixels) { + Log.e(LOG_TAG, "parse end and size error"); + } + return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); + } + + private static int bitToRGB(int bit){ + if(bit == 1){ + return Color.WHITE; + } else { + return Color.BLACK; + } + } + + /** + * a TS 131.102 image instance of code scheme '11' into color Bitmap + * + * @param data The raw data + * @param length the length of image body + * @param transparency with or without transparency + * @return The color bitmap + */ + public static Bitmap parseToRGB(byte[] data, int length, + boolean transparency) { + int valueIndex = 0; + int width = data[valueIndex++] & 0xFF; + int height = data[valueIndex++] & 0xFF; + int bits = data[valueIndex++] & 0xFF; + int colorNumber = data[valueIndex++] & 0xFF; + int clutOffset = ((data[valueIndex++] & 0xFF) << 8) + | data[valueIndex++]; + length = length - 6; + + int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); + if (true == transparency) { + colorIndexArray[colorNumber - 1] = Color.TRANSPARENT; + } + + int[] resultArray = null; + if (0 == (8 % bits)) { + resultArray = mapTo2OrderBitColor(data, valueIndex, + (width * height), colorIndexArray, bits); + } else { + resultArray = mapToNon2OrderBitColor(data, valueIndex, + (width * height), colorIndexArray, bits); + } + + return Bitmap.createBitmap(resultArray, width, height, + Bitmap.Config.RGB_565); + } + + private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex, + int length, int[] colorArray, int bits) { + if (0 != (8 % bits)) { + Log.e(LOG_TAG, "not event number of color"); + return mapToNon2OrderBitColor(data, valueIndex, length, colorArray, + bits); + } + + int mask = 0x01; + switch (bits) { + case 1: + mask = 0x01; + break; + case 2: + mask = 0x03; + break; + case 4: + mask = 0x0F; + break; + case 8: + mask = 0xFF; + break; + } + + int[] resultArray = new int[length]; + int resultIndex = 0; + int run = 8 / bits; + while (resultIndex < length) { + byte tempByte = data[valueIndex++]; + for (int runIndex = 0; runIndex < run; ++runIndex) { + int offset = run - runIndex - 1; + resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits)) + & mask]; + } + } + return resultArray; + } + + private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex, + int length, int[] colorArray, int bits) { + if (0 == (8 % bits)) { + Log.e(LOG_TAG, "not odd number of color"); + return mapTo2OrderBitColor(data, valueIndex, length, colorArray, + bits); + } + + int[] resultArray = new int[length]; + // TODO fix me: + return resultArray; + } + + private static int[] getCLUT(byte[] rawData, int offset, int number) { + if (null == rawData) { + return null; + } + + int[] result = new int[number]; + int endIndex = offset + (number * 3); // 1 color use 3 bytes + int valueIndex = offset; + int colorIndex = 0; + int alpha = 0xff << 24; + do { + result[colorIndex++] = alpha + | ((rawData[valueIndex++] & 0xFF) << 16) + | ((rawData[valueIndex++] & 0xFF) << 8) + | ((rawData[valueIndex++] & 0xFF)); + } while (valueIndex < endIndex); + return result; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java b/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java index a43e32f..c002729 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java @@ -17,241 +17,74 @@ package com.android.internal.telephony.gsm.stk; /** - * Interface for communication from Apps to STK Service + * Interface for communication between STK App and STK Telephony * * {@hide} */ public interface AppInterface { - /** - * STK state - */ - public enum State { - /** - * Idle state - */ - IDLE, - /** - * Idle but main menu exists. Menu selection should be done by calling {@code - * notifyMenuSelection()}. - */ - MAIN_MENU, - /** - * Waiting for a key input. Key input should be notified by calling - * {@code notifyInkey()}. - */ - GET_INKEY, - /** - * Waiting for a user input. Text input should be notified by calling - * {@code notifyInput()}. - */ - GET_INPUT, - /** - * Waiting for a user selection. The selection should be notified by - * calling {@code notifySelectedItem()}. - */ - SELECT_ITEM, - /** - * Waiting for the user to accept or reject a call. It should be - * notified by calling {@code acceptOrRejectCall()}. - */ - CALL_SETUP, - /** - * Waiting for user to confirm Display Text message. - * notified by calling {@code notifyDisplayTextEnded()}. - */ - DISPLAY_TEXT, - /** - * Waiting for user to confirm launching the browser. - * notified by calling {@code notifyLaunchedBrowserConfirmed()}. - */ - LAUNCH_BROWSER, - /** - * Waiting for the application to play the requested tone. - */ - PLAY_TONE - } - - /** - * Sets the {@link CommandListener CommandListener} object that is used for - * notifying of proactive commands or events from the SIM/RIL. - * - * @param l CommandListener object that handles the proactive commands and - * events from the SIM/RIL. - */ - void setCommandListener(CommandListener l); - /** - * Gets the current state of STK service. - * @return The current state. + /* + * Intent's actions which are broadcasted by the Telephony once a new STK + * proactive command, session end arrive. */ - State getState(); - - /** - * Gets the main menu that has been setup by the SIM. - * - * @return The main menu that has been setup by the SIM. It can be null. - */ - Menu getCurrentMenu(); - - /** - * Notifies the SIM of the menu selection among a set of menu options - * supplied by the SIM using SET UP MENU. - * - * @param menuId ID of the selected menu item. It can be between 1 and - * 255. - * @param helpRequired True if just help information is requested on a menu - * item rather than menu selection. False if the menu item - * is actually selected. - */ - void notifyMenuSelection(int menuId, boolean helpRequired); - - /** - * Notifies the SIM that a user activity has occurred. It is actually sent - * to the SIM when it has registered to be notified of this event via SET - * UP EVENT LIST command. - */ - void notifyUserActivity(); - - /** - * Notifies the SIM that the idle screen is available. It is actually sent - * to the SIM when it has registered to be notified of this event via SET - * UP EVENT LIST command. - */ - void notifyIdleScreenAvailable(); - - /** - * Notifies the SIM that the currently used language has changed. It is - * actually sent to the SIM when it has registered to be notified of this - * event via SET UP EVENT LIST command. - * - * @param langCode Language code of the currently selected language. - * Language code is defined in ISO 639. This must be a - * string of two characters. - */ - void notifyLanguageSelection(String langCode); - - /** - * Notifies the SIM that the browser is terminated. It is actually sent to - * the SIM when it has registered to be notified of this event via SET UP - * EVENT LIST command. - * - * @param isErrorTermination True if the cause is "Error Termination", - * false if the cause is "User Termination". - */ - void notifyBrowserTermination(boolean isErrorTermination); - - /** - * Notifies the SIM about the launch browser confirmation. This method - * should be called only after the application gets notified by {@code - * CommandListener.onLaunchBrowser()} or inside that method. - * - * @param userConfirmed True if user choose to confirm browser launch, - * False if user choose not to confirm browser launch. - */ - void notifyLaunchBrowser(boolean userConfirmed); - - /** - * Notifies the SIM that a tone had been played. This method should be called - * only after the application gets notified by {@code - * CommandListener.onPlayTone()} or inside that method. - * - */ - void notifyToneEnded(); - - /** - * Notifies the SIM that the user input a text. This method should be - * called only after the application gets notified by {@code - * CommandListener.onGetInput()} or inside that method. - * - * @param input The text string that the user has typed. - * @param helpRequired True if just help information is requested on a menu - * item rather than menu selection. False if the menu - * item is actually selected. - */ - void notifyInput(String input, boolean helpRequired); - - /** - * Notifies the SIM that the user input a key in Yes/No scenario. - * This method should be called only after the application gets notified by - * {@code CommandListener.onGetInkey()} or inside that method. - * - * @param yesNoResponse User's choice for Yes/No scenario. - * @param helpRequired True if just help information is requested on a menu - * item rather than menu selection. False if the menu - * item is actually selected. - */ - void notifyInkey(boolean yesNoResponse, boolean helpRequired); - - /** - * Notifies the SIM that the user input a key. This method should be called - * only after the application gets notified by {@code - * CommandListener.onGetInkey()} or inside that method. - * - * @param key The key that the user has typed. If the SIM required - * @param helpRequired True if just help information is requested on a menu - * item rather than menu selection. False if the menu - * item is actually selected. - */ - void notifyInkey(char key, boolean helpRequired); - - /** - * Notifies the SIM that no response was received from the user. - */ - void notifyNoResponse(); - - /** - * Send terminal response for backward move in the proactive SIM session - * requested by the user - * - * Only available when responding following proactive commands - * DISPLAY_TEXT(0x21), - * GET_INKEY(0x22), - * GET_INPUT(0x23), - * SET_UP_MENU(0x25); - * - * @return true if stk can send backward move response - */ - boolean backwardMove(); - - /** - * Send terminal response for proactive SIM session terminated by the user - * - * Only available when responding following proactive commands - * DISPLAY_TEXT(0x21), - * GET_INKEY(0x22), - * GET_INPUT(0x23), - * PLAY_TONE(0x20), - * SET_UP_MENU(0x25); - * - * @return true if stk can send terminate session response - */ - boolean terminateSession(); - - /** - * Notifies the SIM that the user selected an item. This method should be - * called only after the application gets notified by {@code - * CommandListener.onSelectItem()} or inside that method. - * - * @param id The menu item that the user has selected. - * @param wantsHelp Indicates if the user requested help for the id item. - */ - void notifySelectedItem(int id, boolean wantsHelp); + public static final String STK_CMD_ACTION = + "android.intent.action.stk.command"; + public static final String STK_SESSION_END_ACTION = + "android.intent.action.stk.session_end"; - /** - * Notifies the SIM that No response was received from the user for display - * text message dialog. - * - * * @param terminationCode indication for display text termination. Uses - * {@code ResultCode } values. - */ - public void notifyDisplayTextEnded(ResultCode terminationCode); + /* + * Callback function from app to telephony to pass a result code and user's + * input back to the SIM. + */ + void onCmdResponse(StkResponseMessage resMsg); + + /* + * Enumeration for representing "Type of Command" of proactive commands. + * Those are the only commands which are supported by the Telephony. Any app + * implementation should support those. + */ + public static enum CommandType { + DISPLAY_TEXT(0x21), + GET_INKEY(0x22), + GET_INPUT(0x23), + LAUNCH_BROWSER(0x15), + PLAY_TONE(0x20), + REFRESH(0x01), + SELECT_ITEM(0x24), + SEND_SS(0x11), + SEND_USSD(0x12), + SEND_SMS(0x13), + SEND_DTMF(0x14), + SET_UP_EVENT_LIST(0x05), + SET_UP_IDLE_MODE_TEXT(0x28), + SET_UP_MENU(0x25), + SET_UP_CALL(0x10); + + private int mValue; + + CommandType(int value) { + mValue = value; + } + + public int value() { + return mValue; + } - /** - * Notifies the SIM whether the user accepted the call or not. This method - * should be called only after the application gets notified by {@code - * CommandListener.onCallSetup()} or inside that method. - * - * @param accept True if the user has accepted the call, false if not. - */ - void acceptOrRejectCall(boolean accept); + /** + * Create a CommandType object. + * + * @param value Integer value to be converted to a CommandType object. + * @return CommandType object whose "Type of Command" value is {@code + * value}. If no CommandType object has that value, null is + * returned. + */ + public static CommandType fromInt(int value) { + for (CommandType e : CommandType.values()) { + if (e.mValue == value) { + return e; + } + } + return null; + } + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java b/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java index f5268e5..26c2175 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java @@ -72,20 +72,7 @@ class BerTlv { try { /* tag */ tag = data[curIndex++] & 0xff; - if (tag != BER_PROACTIVE_COMMAND_TAG) { - // If the buffer doesn't contain proactive command tag, but - // start with a command details tlv object ==> skip the length - // parsing and look for tlv objects. - ComprehensionTlv ctlv = ComprehensionTlv.decode(data, - curIndex--); - if (ctlv.getTag() == ComprehensionTlvTag.COMMAND_DETAILS.value()) { - tag = BER_UNKNOWN_TAG; - curIndex--; - } else { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } else { - + if (tag == BER_PROACTIVE_COMMAND_TAG) { /* length */ int temp = data[curIndex++] & 0xff; if (temp < 0x80) { @@ -101,8 +88,12 @@ class BerTlv { throw new ResultException( ResultCode.CMD_DATA_NOT_UNDERSTOOD); } + } else { + if (ComprehensionTlvTag.COMMAND_DETAILS.value() == (tag & ~0x80)) { + tag = BER_UNKNOWN_TAG; + curIndex = 0; + } } - } catch (IndexOutOfBoundsException e) { throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); } catch (ResultException e) { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java new file mode 100644 index 0000000..168361a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.os.Parcel; +import android.os.Parcelable; + +abstract class ValueObject { + abstract ComprehensionTlvTag getTag(); +} + +/** + * Class for Command Detailes object of proactive commands from SIM. + * {@hide} + */ +class CommandDetails extends ValueObject implements Parcelable { + public boolean compRequired; + public int commandNumber; + public int typeOfCommand; + public int commandQualifier; + + public ComprehensionTlvTag getTag() { + return ComprehensionTlvTag.COMMAND_DETAILS; + } + + CommandDetails() { + } + + public boolean compareTo(CommandDetails other) { + return (this.compRequired == other.compRequired && + this.commandNumber == other.commandNumber && + this.commandQualifier == other.commandQualifier && + this.typeOfCommand == other.typeOfCommand); + } + + public CommandDetails(Parcel in) { + compRequired = true; + commandNumber = in.readInt(); + typeOfCommand = in.readInt(); + commandQualifier = in.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(commandNumber); + dest.writeInt(typeOfCommand); + dest.writeInt(commandQualifier); + } + + public static final Parcelable.Creator<CommandDetails> CREATOR = + new Parcelable.Creator<CommandDetails>() { + public CommandDetails createFromParcel(Parcel in) { + return new CommandDetails(in); + } + + public CommandDetails[] newArray(int size) { + return new CommandDetails[size]; + } + }; + + public int describeContents() { + return 0; + } +} + +class DeviceIdentities extends ValueObject { + public int sourceId; + public int destinationId; + + ComprehensionTlvTag getTag() { + return ComprehensionTlvTag.DEVICE_IDENTITIES; + } +} + +// Container class to hold icon identifier value. +class IconId extends ValueObject { + int recordNumber; + boolean selfExplanatory; + + ComprehensionTlvTag getTag() { + return ComprehensionTlvTag.ICON_ID; + } +} + +// Container class to hold item icon identifier list value. +class ItemsIconId extends ValueObject { + int [] recordNumbers; + boolean selfExplanatory; + + ComprehensionTlvTag getTag() { + return ComprehensionTlvTag.ITEM_ICON_ID_LIST; + } +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandListener.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandListener.java deleted file mode 100644 index 5c9a3e9..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandListener.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm.stk; - -import android.graphics.Bitmap; - -import java.util.BitSet; -import java.util.List; - - -/** - * Interface for command notification from STK Service to Apps - * - * {@hide} - */ -public interface CommandListener { - - /** - * Call back function to be called when the session with the SIM ends. - * Application must go back to the main SIM Toolkit application screen when - * this is called. - */ - void onSessionEnd(); - - /** - * Call back function to be called when the SIM wants a call to be set up. - * Application must call {@code AppInterface.acceptOrRejectCall()} after - * this method returns or inside this method. - * - * @param confirmMsg User confirmation phase message. - * @param textAttrs List of text attributes to be applied. Can be null. - * @param callMsg Call set up phase message. - */ - void onCallSetup(String confirmMsg, List<TextAttribute> textAttrs, - String callMsg); - - /** - * Call back function to be called for handling DISPLAY_TEXT proactive - * commands. - * @param text A text to be displayed - * @param textAttrs List of text attributes to be applied. Can be null. - * @param isHighPriority High priority - * @param userClear Wait for user to clear message if true, clear - * message after a delay if false. - */ - void onDisplayText(String text, List<TextAttribute> textAttrs, - boolean isHighPriority, boolean userClear, boolean responseNeeded, - Bitmap icon); - - /** - * Call back function to be called for handling SET_UP_MENU proactive - * commands. The menu can be retrieved by calling {@code - * AppInterface.getMainMenu}. - * - * @param menu application main menu. - */ - void onSetUpMenu(Menu menu); - - /** - * Call back function to be called for handling GET_INKEY proactive - * commands. - * Application must call {@code AppInterface.notifyInkey()} after this - * method returns or inside this method. - * - * @param text A text to be used as a prompt. - * @param textAttrs List of text attributes to be applied. Can be null. - * @param yesNo "Yes/No" response is requested if true. When this is - * true, {@code digitOnly} and {@code ucs2} are ignored. - * @param digitOnly Digits (0 to 9, *, # and +) only if true. Alphabet set - * if false. - * @param ucs2 UCS2 alphabet if true, SMS default alphabet if false. - * @param immediateResponse An immediate digit response (0 to 9, * and #) - * is required if true. User response shall be displayed - * and the terminal may allow alteration and/or - * confirmation if false. - * @param helpAvailable Help information available. - */ - void onGetInkey(String text, List<TextAttribute> textAttrs, boolean yesNo, boolean digitOnly, - boolean ucs2, boolean immediateResponse, boolean helpAvailable); - /** - * Call back function to be called for handling GET_INPUT proactive - * commands. Application must call {@code AppInterface.notifyInput()} after - * this method returns or inside this method. - * - * @param text A text to be used as a prompt - * @param defaultText A text to be used as a default input - * @param minLen Mininum length of response (0 indicates there is no mininum - * length requirement). - * @param maxLen Maximum length of response (between 0 and 0xfe). - * @param noMaxLimit If true, there is no limit in maximum length of - * response. - * @param textAttrs List of text attributes to be applied. Can be null. - * @param digitOnly Digits (0 to 9, *, # and +) only if true. Alphabet set - * if false. - * @param ucs2 UCS2 alphabet if true, SMS default alphabet if false. - * @param echo Terminal may echo user input on the display if true. User - * input shall not be revealed in any way if false. - * @param helpAvailable Help information available. - */ - void onGetInput(String text, String defaultText, int minLen, int maxLen, - boolean noMaxLimit, List<TextAttribute> textAttrs, - boolean digitOnly, boolean ucs2, boolean echo, boolean helpAvailable); - - /** - * Call back function to be called for handling SELECT_ITEM proactive - * commands. - * Application must call {@code AppInterface.notifySelectedItem()} after - * this method returns or inside this method. - * - * @param menu Items menu. - * @param presentationType Presentation type of the choices. - */ - void onSelectItem(Menu menu, PresentationType presentationType); - - /** - * Call back function to be called for handling SET_UP_EVENT_LIST proactive - * commands. - * @param events BitSet object each bit of which represents an event - * that UICC wants the terminal to monitor. - * <ul> - * <li>0x00: MT call - * <li>0x01: Call connected - * <li>0x02: Call disconnected - * <li>0x03: Location status - * <li>0x04: User activity - * <li>0x05: Idle screen available - * <li>0x06: Card reader status - * <li>0x07: Language selection - * <li>0x08: Browser termination - * <li>0x09: Data available - * <li>0x0A: Channel status - * <li>0x0B: Access Technology Change - * <li>0x0C: Display parameters changed - * <li>0x0D: Local connection - * <li>0x0E: Network Search Mode Change - * <li>0x0F: Browsing status - * <li>0x10: Frames Information Change - * <li>0x11: reserved for 3GPP (I-WLAN Access Status) - * </ul> - * These values are defined in Service as UICC_EVENT_*. - * @throws ResultException must be BEYOND_TERMINAL_CAPABILITY - * if the ME is not able to successfully accept all events - */ - void onSetUpEventList(BitSet events) throws ResultException; - - /** - * Call back function to be called for handling LAUNCH_BROWSER proactive - * commands. - * - * @param useDefaultUrl If true, use the system default URL, otherwise use - * {@code url} as the URL. - * @param confirmMsg A text to be used as the user confirmation message. Can - * be null. - * @param confirmMsgAttrs List of text attributes to be applied to {code - * confirmMsgAttrs}. Can be null. - * @param mode Launch mode. - */ - void onLaunchBrowser(String url, String confirmMsg, - List<TextAttribute> confirmMsgAttrs, LaunchBrowserMode mode); - - /** - * Call back function to be called for handling PLAY_TONE proactive - * commands. - * - * @param tone Tone to be played - * @param text A text to be displayed. Can be null. - * @param textAttrs List of text attributes to be applied. Can be null. - * @param duration Time duration to play the tone. - * @throws ResultException - */ - void onPlayTone(Tone tone, String text, List<TextAttribute> textAttrs, - Duration duration) throws ResultException; -} diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java index d39ad7b..60e8148 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,102 +18,152 @@ package com.android.internal.telephony.gsm.stk; import android.graphics.Bitmap; -import java.util.List; - /** * Container class for proactive command parameters. * */ class CommandParams { - public CtlvCommandDetails cmdDet; + CommandDetails cmdDet; - CommandParams(CtlvCommandDetails cmdDet) { + CommandParams(CommandDetails cmdDet) { this.cmdDet = cmdDet; } + + AppInterface.CommandType getCommandType() { + return AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); + } + + boolean setIcon(Bitmap icon) { return true; } } -class CommonUIParams extends CommandParams { - String mText; - Bitmap mIcon; - boolean mIconSelfExplanatory; - TextAttribute mTextAttrs; +class DisplayTextParams extends CommandParams { + TextMessage textMsg; - CommonUIParams(CtlvCommandDetails cmdDet, String text, - TextAttribute textAttrs) { + DisplayTextParams(CommandDetails cmdDet, TextMessage textMsg) { super(cmdDet); + this.textMsg = textMsg; + } - mText = text; - mTextAttrs = textAttrs; - mIconSelfExplanatory = false; - mIcon = null; + boolean setIcon(Bitmap icon) { + if (icon != null && textMsg != null) { + textMsg.icon = icon; + return true; + } + return false; } +} + +class LaunchBrowserParams extends CommandParams { + TextMessage confirmMsg; + LaunchBrowserMode mode; + String url; - void setIcon(Bitmap icon) { - mIcon = icon; + LaunchBrowserParams(CommandDetails cmdDet, TextMessage confirmMsg, + String url, LaunchBrowserMode mode) { + super(cmdDet); + this.confirmMsg = confirmMsg; + this.mode = mode; + this.url = url; } - void setIconSelfExplanatory(boolean iconSelfExplanatory) { - mIconSelfExplanatory = iconSelfExplanatory; + boolean setIcon(Bitmap icon) { + if (icon != null && confirmMsg != null) { + confirmMsg.icon = icon; + return true; + } + return false; } } -class DisplayTextParams extends CommandParams { - String text = null; - Bitmap icon = null; - List<TextAttribute> textAttrs = null; - boolean immediateResponse = false; - boolean userClear = false; - boolean isHighPriority = false; - - DisplayTextParams(CtlvCommandDetails cmdDet) { +class PlayToneParams extends CommandParams { + TextMessage textMsg; + ToneSettings settings; + + PlayToneParams(CommandDetails cmdDet, TextMessage textMsg, + Tone tone, Duration duration, boolean vibrate) { super(cmdDet); + this.textMsg = textMsg; + this.settings = new ToneSettings(duration, tone, vibrate); + } + + boolean setIcon(Bitmap icon) { + if (icon != null && textMsg != null) { + textMsg.icon = icon; + return true; + } + return false; } } -class GetInkeyParams extends CommandParams { - boolean isYesNo; - boolean isUcs2; +class CallSetupParams extends CommandParams { + TextMessage confirmMsg; + TextMessage callMsg; - GetInkeyParams(CtlvCommandDetails cmdDet, boolean isYesNo, - boolean isUcs2) { + CallSetupParams(CommandDetails cmdDet, TextMessage confirmMsg, + TextMessage callMsg) { super(cmdDet); + this.confirmMsg = confirmMsg; + this.callMsg = callMsg; + } - this.isYesNo = isYesNo; - this.isUcs2 = isUcs2; + boolean setIcon(Bitmap icon) { + if (icon == null) { + return false; + } + if (confirmMsg != null && confirmMsg.icon == null) { + confirmMsg.icon = icon; + return true; + } else if (callMsg != null && callMsg.icon == null) { + callMsg.icon = icon; + return true; + } + return false; } } -class GetInputParams extends CommandParams { - boolean isUcs2; - boolean isPacked; +class SelectItemParams extends CommandParams { + Menu menu = null; + boolean loadTitleIcon = false; - GetInputParams(CtlvCommandDetails cmdDet, boolean isUcs2, - boolean isPacked) { + SelectItemParams(CommandDetails cmdDet, Menu menu, boolean loadTitleIcon) { super(cmdDet); + this.menu = menu; + this.loadTitleIcon = loadTitleIcon; + } - this.isUcs2 = isUcs2; - this.isPacked = isPacked; + boolean setIcon(Bitmap icon) { + if (icon != null && menu != null) { + if (loadTitleIcon && menu.titleIcon == null) { + menu.titleIcon = icon; + } else { + for (Item item : menu.items) { + if (item.icon != null) { + continue; + } + item.icon = icon; + break; + } + } + return true; + } + return false; } } -class SelectItemParams extends CommandParams { - Menu mMenu = null; - PresentationType mPresentationType; - int mIconLoadState = LOAD_NO_ICON; - - // loading icons state parameters. - static final int LOAD_NO_ICON = 0; - static final int LOAD_TITLE_ICON = 1; - static final int LOAD_ITEMS_ICONS = 2; - static final int LOAD_TITLE_ITEMS_ICONS = 3; - - SelectItemParams(CtlvCommandDetails cmdDet, Menu menu, - PresentationType presentationType, int iconLoadState) { +class GetInputParams extends CommandParams { + Input input = null; + + GetInputParams(CommandDetails cmdDet, Input input) { super(cmdDet); + this.input = input; + } - mMenu = menu; - mPresentationType = presentationType; - mIconLoadState = iconLoadState; + boolean setIcon(Bitmap icon) { + if (icon != null && input != null) { + input.icon = icon; + } + return true; } } + diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java new file mode 100644 index 0000000..2d2f9ae --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java @@ -0,0 +1,870 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.graphics.Bitmap; +import android.os.Handler; +import android.os.Message; + +import com.android.internal.telephony.gsm.GsmAlphabet; +import com.android.internal.telephony.gsm.SIMFileHandler; + +import java.util.Iterator; +import java.util.List; + +/** + * Factory class, used for decoding raw byte arrays, received from baseband, + * into a CommandParams object. + * + */ +class CommandParamsFactory extends Handler { + private static CommandParamsFactory sInstance = null; + private IconLoader mIconLoader; + private CommandParams mCmdParams = null; + private int mIconLoadState = LOAD_NO_ICON; + private Handler mCaller = null; + + // constants + static final int MSG_ID_LOAD_ICON_DONE = 1; + + // loading icons state parameters. + static final int LOAD_NO_ICON = 0; + static final int LOAD_SINGLE_ICON = 1; + static final int LOAD_MULTI_ICONS = 2; + + // Command Qualifier values for refresh command + static final int REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00; + static final int REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02; + static final int REFRESH_NAA_INIT = 0x03; + static final int REFRESH_UICC_RESET = 0x04; + + private static final String TAG = "CmdParamsFactory"; + + static synchronized CommandParamsFactory getInstance(Handler caller, + SIMFileHandler fh) { + if (sInstance != null) { + return sInstance; + } + if (fh != null) { + return new CommandParamsFactory(caller, fh); + } + return null; + } + + private CommandParamsFactory(Handler caller, SIMFileHandler fh) { + mCaller = caller; + mIconLoader = IconLoader.getInstance(this, fh); + } + + private CommandDetails processCommandDetails(List<ComprehensionTlv> ctlvs) { + CommandDetails cmdDet = null; + + if (ctlvs != null) { + // Search for the Command Details object. + ComprehensionTlv ctlvCmdDet = searchForTag( + ComprehensionTlvTag.COMMAND_DETAILS, ctlvs); + if (ctlvCmdDet != null) { + try { + cmdDet = ValueParser.retrieveCommandDetails(ctlvCmdDet); + } catch (ResultException e) { + StkLog.d(this, "Failed to procees command details"); + } + } + } + return cmdDet; + } + + void make(BerTlv berTlv) { + if (berTlv == null) { + return; + } + // reset global state parameters. + mCmdParams = null; + mIconLoadState = LOAD_NO_ICON; + // only proactive command messages are processed. + if (berTlv.getTag() != BerTlv.BER_PROACTIVE_COMMAND_TAG) { + sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + return; + } + boolean cmdPending = false; + List<ComprehensionTlv> ctlvs = berTlv.getComprehensionTlvs(); + // process command dtails from the tlv list. + CommandDetails cmdDet = processCommandDetails(ctlvs); + if (cmdDet == null) { + sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + return; + } + + // extract command type enumeration from the raw value stored inside + // the Command Details object. + AppInterface.CommandType cmdType = AppInterface.CommandType + .fromInt(cmdDet.typeOfCommand); + if (cmdType == null) { + sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + return; + } + + try { + switch (cmdType) { + case SET_UP_MENU: + cmdPending = processSelectItem(cmdDet, ctlvs); + break; + case SELECT_ITEM: + cmdPending = processSelectItem(cmdDet, ctlvs); + break; + case DISPLAY_TEXT: + cmdPending = processDisplayText(cmdDet, ctlvs); + break; + case SET_UP_IDLE_MODE_TEXT: + cmdPending = processSetUpIdleModeText(cmdDet, ctlvs); + break; + case GET_INKEY: + cmdPending = processGetInkey(cmdDet, ctlvs); + break; + case GET_INPUT: + cmdPending = processGetInput(cmdDet, ctlvs); + break; + case SEND_DTMF: + case SEND_SMS: + case SEND_SS: + case SEND_USSD: + cmdPending = processEventNotify(cmdDet, ctlvs); + break; + case SET_UP_CALL: + cmdPending = processSetupCall(cmdDet, ctlvs); + break; + case REFRESH: + processRefresh(cmdDet, ctlvs); + cmdPending = false; + break; + case LAUNCH_BROWSER: + cmdPending = processLaunchBrowser(cmdDet, ctlvs); + break; + case PLAY_TONE: + cmdPending = processPlayTone(cmdDet, ctlvs); + break; + default: + // unsupported proactive commands + mCmdParams = new CommandParams(cmdDet); + sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + return; + } + } catch (ResultException e) { + mCmdParams = new CommandParams(cmdDet); + sendCmdParams(e.result()); + return; + } + if (!cmdPending) { + sendCmdParams(ResultCode.OK); + } + } + + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ID_LOAD_ICON_DONE: + sendCmdParams(setIcons(msg.obj)); + break; + } + } + + private ResultCode setIcons(Object data) { + Bitmap[] icons = null; + int iconIndex = 0; + + if (data == null) { + return ResultCode.PRFRMD_ICON_NOT_DISPLAYED; + } + switch(mIconLoadState) { + case LOAD_SINGLE_ICON: + mCmdParams.setIcon((Bitmap) data); + break; + case LOAD_MULTI_ICONS: + icons = (Bitmap[]) data; + // set each item icon. + for (Bitmap icon : icons) { + mCmdParams.setIcon(icon); + } + break; + } + return ResultCode.OK; + } + + private void sendCmdParams(ResultCode resCode) { + Message msg = mCaller.obtainMessage(RilMessageDecoder.CMD_PARAMS_READY); + msg.arg1 = resCode.value(); + msg.obj = mCmdParams; + msg.sendToTarget(); + } + + /** + * Search for a COMPREHENSION-TLV object with the given tag from a list + * + * @param tag A tag to search for + * @param ctlvs List of ComprehensionTlv objects used to search in + * + * @return A ComprehensionTlv object that has the tag value of {@code tag}. + * If no object is found with the tag, null is returned. + */ + private ComprehensionTlv searchForTag(ComprehensionTlvTag tag, + List<ComprehensionTlv> ctlvs) { + Iterator<ComprehensionTlv> iter = ctlvs.iterator(); + return searchForNextTag(tag, iter); + } + + /** + * Search for the next COMPREHENSION-TLV object with the given tag from a + * list iterated by {@code iter}. {@code iter} points to the object next to + * the found object when this method returns. Used for searching the same + * list for similar tags, usually item id. + * + * @param tag A tag to search for + * @param iter Iterator for ComprehensionTlv objects used for search + * + * @return A ComprehensionTlv object that has the tag value of {@code tag}. + * If no object is found with the tag, null is returned. + */ + private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag, + Iterator<ComprehensionTlv> iter) { + int tagValue = tag.value(); + while (iter.hasNext()) { + ComprehensionTlv ctlv = iter.next(); + if (ctlv.getTag() == tagValue) { + return ctlv; + } + } + return null; + } + + /** + * Processes DISPLAY_TEXT proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @throws ResultException + */ + private boolean processDisplayText(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) + throws ResultException { + + StkLog.d(this, "process DisplayText"); + + TextMessage textMsg = new TextMessage(); + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, + ctlvs); + if (ctlv != null) { + textMsg.text = ValueParser.retrieveTextString(ctlv); + } + // If the tlv object doesn't exist or the it is a null object reply + // with command not understood. + if (textMsg.text == null) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + + ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs); + if (ctlv != null) { + textMsg.responseNeeded = false; + } + // parse icon identifier + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + textMsg.iconSelfExplanatory = iconId.selfExplanatory; + } + // parse tone duration + ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); + if (ctlv != null) { + textMsg.duration = ValueParser.retrieveDuration(ctlv); + } + + // Parse command qualifier parameters. + textMsg.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0; + textMsg.userClear = (cmdDet.commandQualifier & 0x80) != 0; + + mCmdParams = new DisplayTextParams(cmdDet, textMsg); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes SET_UP_IDLE_MODE_TEXT proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @throws ResultException + */ + private boolean processSetUpIdleModeText(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(this, "process SetUpIdleModeText"); + + TextMessage textMsg = new TextMessage(); + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, + ctlvs); + if (ctlv != null) { + textMsg.text = ValueParser.retrieveTextString(ctlv); + } + // load icons only when text exist. + if (textMsg.text != null) { + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + textMsg.iconSelfExplanatory = iconId.selfExplanatory; + } + } + + mCmdParams = new DisplayTextParams(cmdDet, textMsg); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes GET_INKEY proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @throws ResultException + */ + private boolean processGetInkey(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(this, "process GetInkey"); + + Input input = new Input(); + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, + ctlvs); + if (ctlv != null) { + input.text = ValueParser.retrieveTextString(ctlv); + } else { + throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); + } + // parse icon identifier + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + } + + input.minLen = 1; + input.maxLen = 1; + + input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; + input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; + input.yesNo = (cmdDet.commandQualifier & 0x04) != 0; + input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; + + mCmdParams = new GetInputParams(cmdDet, input); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes GET_INPUT proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @throws ResultException + */ + private boolean processGetInput(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(this, "process GetInput"); + + Input input = new Input(); + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, + ctlvs); + if (ctlv != null) { + input.text = ValueParser.retrieveTextString(ctlv); + } else { + throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); + } + + ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs); + if (ctlv != null) { + try { + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + input.minLen = rawValue[valueIndex] & 0xff; + input.maxLen = rawValue[valueIndex + 1] & 0xff; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } else { + throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); + } + + ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs); + if (ctlv != null) { + input.defaultText = ValueParser.retrieveTextString(ctlv); + } + // parse icon identifier + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + } + + input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; + input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; + input.echo = (cmdDet.commandQualifier & 0x04) == 0; + input.packed = (cmdDet.commandQualifier & 0x08) != 0; + input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; + + mCmdParams = new GetInputParams(cmdDet, input); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes REFRESH proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + */ + private boolean processRefresh(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) { + + StkLog.d(this, "process Refresh"); + + // REFRESH proactive command is rerouted by the baseband and handled by + // the telephony layer. IDLE TEXT should be removed for a REFRESH command + // with "initialization" or "reset" + switch (cmdDet.commandQualifier) { + case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE: + case REFRESH_NAA_INIT_AND_FILE_CHANGE: + case REFRESH_NAA_INIT: + case REFRESH_UICC_RESET: + mCmdParams = new DisplayTextParams(cmdDet, null); + break; + } + return false; + } + + /** + * Processes SELECT_ITEM proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @throws ResultException + */ + private boolean processSelectItem(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(this, "process SelectItem"); + + Menu menu = new Menu(); + IconId titleIconId = null; + ItemsIconId itemsIconId = null; + Iterator<ComprehensionTlv> iter = ctlvs.iterator(); + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, + ctlvs); + if (ctlv != null) { + menu.title = ValueParser.retrieveAlphaId(ctlv); + } + + while (true) { + ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter); + if (ctlv != null) { + menu.items.add(ValueParser.retrieveItem(ctlv)); + } else { + break; + } + } + + // We must have at least one menu item. + if (menu.items.size() == 0) { + throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); + } + + ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs); + if (ctlv != null) { + // STK items are listed 1...n while list start at 0, need to + // subtract one. + menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1; + } + + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + mIconLoadState = LOAD_SINGLE_ICON; + titleIconId = ValueParser.retrieveIconId(ctlv); + menu.titleIconSelfExplanatory = titleIconId.selfExplanatory; + } + + ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs); + if (ctlv != null) { + mIconLoadState = LOAD_MULTI_ICONS; + itemsIconId = ValueParser.retrieveItemsIconId(ctlv); + menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory; + } + + boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0; + if (presentTypeSpecified) { + if ((cmdDet.commandQualifier & 0x02) == 0) { + menu.presentationType = PresentationType.DATA_VALUES; + } else { + menu.presentationType = PresentationType.NAVIGATION_OPTIONS; + } + } + menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0; + menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; + + mCmdParams = new SelectItemParams(cmdDet, menu, titleIconId != null); + + // Load icons data if needed. + switch(mIconLoadState) { + case LOAD_NO_ICON: + return false; + case LOAD_SINGLE_ICON: + mIconLoader.loadIcon(titleIconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + break; + case LOAD_MULTI_ICONS: + int[] recordNumbers = itemsIconId.recordNumbers; + if (titleIconId != null) { + // Create a new array for all the icons (title and items). + recordNumbers = new int[itemsIconId.recordNumbers.length + 1]; + recordNumbers[0] = titleIconId.recordNumber; + System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers, + 1, itemsIconId.recordNumbers.length); + } + mIconLoader.loadIcons(recordNumbers, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + break; + } + return true; + } + + /** + * Processes EVENT_NOTIFY message from baseband. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + */ + private boolean processEventNotify(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(this, "process EventNotify"); + + TextMessage textMsg = new TextMessage(); + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, + ctlvs); + if (ctlv != null) { + textMsg.text = ValueParser.retrieveAlphaId(ctlv); + } else { + throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); + } + + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + textMsg.iconSelfExplanatory = iconId.selfExplanatory; + } + + textMsg.responseNeeded = false; + mCmdParams = new DisplayTextParams(cmdDet, textMsg); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes SET_UP_EVENT_LIST proactive command from the SIM card. + * + * @param cmdDet Command Details object retrieved. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + */ + private boolean processSetUpEventList(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) { + + StkLog.d(this, "process SetUpEventList"); + // + // ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, + // ctlvs); + // if (ctlv != null) { + // try { + // byte[] rawValue = ctlv.getRawValue(); + // int valueIndex = ctlv.getValueIndex(); + // int valueLen = ctlv.getLength(); + // + // } catch (IndexOutOfBoundsException e) {} + // } + return true; + } + + /** + * Processes LAUNCH_BROWSER proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @throws ResultException + */ + private boolean processLaunchBrowser(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(this, "process LaunchBrowser"); + + TextMessage confirmMsg = new TextMessage(); + IconId iconId = null; + String url = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs); + if (ctlv != null) { + try { + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int valueLen = ctlv.getLength(); + if (valueLen > 0) { + url = GsmAlphabet.gsm8BitUnpackedToString(rawValue, + valueIndex, valueLen); + } else { + url = null; + } + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + + // parse alpha identifier. + ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); + if (ctlv != null) { + confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); + } + // parse icon identifier + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + confirmMsg.iconSelfExplanatory = iconId.selfExplanatory; + } + + // parse command qualifier value. + LaunchBrowserMode mode; + switch (cmdDet.commandQualifier) { + case 0x00: + default: + mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED; + break; + case 0x02: + mode = LaunchBrowserMode.USE_EXISTING_BROWSER; + break; + case 0x03: + mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER; + break; + } + + mCmdParams = new LaunchBrowserParams(cmdDet, confirmMsg, url, mode); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes PLAY_TONE proactive command from the SIM card. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required.t + * @throws ResultException + */ + private boolean processPlayTone(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + + StkLog.d(TAG, "process PlayTone"); + + Tone tone = null; + TextMessage textMsg = new TextMessage(); + Duration duration = null; + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs); + if (ctlv != null) { + // Nothing to do for null objects. + if (ctlv.getLength() > 0) { + try { + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int toneVal = rawValue[valueIndex]; + tone = Tone.fromInt(toneVal); + } catch (IndexOutOfBoundsException e) { + throw new ResultException( + ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + } + // parse alpha identifier + ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); + if (ctlv != null) { + textMsg.text = ValueParser.retrieveAlphaId(ctlv); + } + // parse tone duration + ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); + if (ctlv != null) { + duration = ValueParser.retrieveDuration(ctlv); + } + // parse icon identifier + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + iconId = ValueParser.retrieveIconId(ctlv); + textMsg.iconSelfExplanatory = iconId.selfExplanatory; + } + + boolean vibrate = (cmdDet.commandQualifier & 0x01) != 0x00; + + textMsg.responseNeeded = false; + mCmdParams = new PlayToneParams(cmdDet, textMsg, tone, duration, vibrate); + + if (iconId != null) { + mIconLoadState = LOAD_SINGLE_ICON; + mIconLoader.loadIcon(iconId.recordNumber, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } + + /** + * Processes SETUP_CALL proactive command from the SIM card. + * + * @param cmdDet Command Details object retrieved from the proactive command + * object + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + */ + private boolean processSetupCall(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + StkLog.d(TAG, "process SetupCall"); + + Iterator<ComprehensionTlv> iter = ctlvs.iterator(); + ComprehensionTlv ctlv = null; + // User confirmation phase message. + TextMessage confirmMsg = new TextMessage(); + // Call set up phase message. + TextMessage callMsg = new TextMessage(); + IconId confirmIconId = null; + IconId callIconId = null; + + // get confirmation message string. + ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); + if (ctlv != null) { + confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); + } + + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + confirmIconId = ValueParser.retrieveIconId(ctlv); + confirmMsg.iconSelfExplanatory = confirmIconId.selfExplanatory; + } + + // get call set up message string. + ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); + if (ctlv != null) { + callMsg.text = ValueParser.retrieveAlphaId(ctlv); + } + + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + callIconId = ValueParser.retrieveIconId(ctlv); + callMsg.iconSelfExplanatory = callIconId.selfExplanatory; + } + + mCmdParams = new CallSetupParams(cmdDet, confirmMsg, callMsg); + + if (confirmIconId != null || callIconId != null) { + mIconLoadState = LOAD_MULTI_ICONS; + int[] recordNumbers = new int[2]; + recordNumbers[0] = confirmIconId != null + ? confirmIconId.recordNumber : -1; + recordNumbers[1] = callIconId != null ? callIconId.recordNumber + : -1; + + mIconLoader.loadIcons(recordNumbers, this + .obtainMessage(MSG_ID_LOAD_ICON_DONE)); + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java index 3cf8ca6..833ff3c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java @@ -33,7 +33,7 @@ class ComprehensionTlv { private int mLength; private int mValueIndex; private byte[] mRawValue; - + /** * Constructor. Private on purpose. Use * {@link #decodeMany(byte[], int) decodeMany} or @@ -45,7 +45,7 @@ class ComprehensionTlv { * @param data Byte array containing the value * @param valueIndex Index in data at which the value starts */ - private ComprehensionTlv(int tag, boolean cr, int length, byte[] data, + protected ComprehensionTlv(int tag, boolean cr, int length, byte[] data, int valueIndex) { mTag = tag; mCr = cr; @@ -165,9 +165,9 @@ class ComprehensionTlv { } else { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } - + return new ComprehensionTlv(tag, cr, length, data, curIndex); - + } catch (IndexOutOfBoundsException e) { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java b/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java index 9ca8fb5..9d8cc97 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java @@ -16,13 +16,16 @@ package com.android.internal.telephony.gsm.stk; +import android.os.Parcel; +import android.os.Parcelable; + /** * Class for representing "Duration" object for STK. * * {@hide} */ -public class Duration { +public class Duration implements Parcelable { public int timeInterval; public TimeUnit timeUnit; @@ -49,4 +52,28 @@ public class Duration { this.timeInterval = timeInterval; this.timeUnit = timeUnit; } + + private Duration(Parcel in) { + timeInterval = in.readInt(); + timeUnit = TimeUnit.values()[in.readInt()]; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(timeInterval); + dest.writeInt(timeUnit.ordinal()); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<Duration> CREATOR = new Parcelable.Creator<Duration>() { + public Duration createFromParcel(Parcel in) { + return new Duration(in); + } + + public Duration[] newArray(int size) { + return new Duration[size]; + } + }; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java b/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java index ee91541..2219f58 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java @@ -27,6 +27,8 @@ import android.os.Looper; import android.os.Message; import android.util.Log; +import java.util.HashMap; + /** * Class for loading icons from the SIM card. Has two states: single, for loading * one icon. Multi, for loading icons list. @@ -34,17 +36,18 @@ import android.util.Log; */ class IconLoader extends Handler { // members - int mState = STATE_SINGLE_ICON; - ImageDescriptor mId = null; - Bitmap mCurrentIcon = null; - int mRecordNumber; - SIMFileHandler mSimFH = null; - Message mEndMsg = null; - byte[] mIconData = null; + private int mState = STATE_SINGLE_ICON; + private ImageDescriptor mId = null; + private Bitmap mCurrentIcon = null; + private int mRecordNumber; + private SIMFileHandler mSimFH = null; + private Message mEndMsg = null; + private byte[] mIconData = null; // multi icons state members - int[] mRecordNumbers = null; - int mCurrentRecordIndex = 0; - Bitmap[] mIcons = null; + private int[] mRecordNumbers = null; + private int mCurrentRecordIndex = 0; + private Bitmap[] mIcons = null; + private HashMap<Integer, Bitmap> mIconsCache = null; private static IconLoader sLoader = null; @@ -69,14 +72,16 @@ class IconLoader extends Handler { private IconLoader(Looper looper , SIMFileHandler fh) { super(looper); mSimFH = fh; + + mIconsCache = new HashMap<Integer, Bitmap>(50); } - static IconLoader getInstance(Handler caller , SIMFileHandler fh) { + static IconLoader getInstance(Handler caller, SIMFileHandler fh) { if (sLoader != null) { return sLoader; } if (fh != null) { - HandlerThread thread = new HandlerThread("Stk Icon Laoder"); + HandlerThread thread = new HandlerThread("Stk Icon Loader"); thread.start(); return new IconLoader(thread.getLooper(), fh); } @@ -104,7 +109,7 @@ class IconLoader extends Handler { mState = STATE_SINGLE_ICON; startLoadingIcon(recordNumber); } - + private void startLoadingIcon(int recordNumber) { // Reset the load variables. mId = null; @@ -112,6 +117,13 @@ class IconLoader extends Handler { mCurrentIcon = null; mRecordNumber = recordNumber; + // make sure the icon was not already loaded and saved in the local cache. + if (mIconsCache.containsKey(recordNumber)) { + mCurrentIcon = mIconsCache.get(recordNumber); + postIcon(); + return; + } + // start the first phase ==> loading Image Descriptor. readId(); } @@ -134,6 +146,7 @@ class IconLoader extends Handler { byte[] rawData = ((byte[]) ar.result); if (mId.codingScheme == ImageDescriptor.CODING_SCHEME_BASIC) { mCurrentIcon = parseToBnW(rawData, rawData.length); + mIconsCache.put(mRecordNumber, mCurrentIcon); postIcon(); } else if (mId.codingScheme == ImageDescriptor.CODING_SCHEME_COLOUR) { mIconData = rawData; @@ -145,6 +158,7 @@ class IconLoader extends Handler { byte [] clut = ((byte[]) ar.result); mCurrentIcon = parseToRGB(mIconData, mIconData.length, false, clut); + mIconsCache.put(mRecordNumber, mCurrentIcon); postIcon(); break; } @@ -181,6 +195,11 @@ class IconLoader extends Handler { // Start reading Image Descriptor from SIM card. private void readId() { + if (mRecordNumber < 0) { + mCurrentIcon = null; + postIcon(); + return; + } Message msg = this.obtainMessage(EVENT_READ_EF_IMG_RECOED_DONE); mSimFH.loadEFImgLinearFixed(mRecordNumber, msg); } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java b/telephony/java/com/android/internal/telephony/gsm/stk/Input.java new file mode 100644 index 0000000..1f0d971 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Input.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Container class for STK GET INPUT, GET IN KEY commands parameters. + * + */ +public class Input implements Parcelable { + public String text; + public String defaultText; + public Bitmap icon; + public int minLen; + public int maxLen; + public boolean ucs2; + public boolean packed; + public boolean digitOnly; + public boolean echo; + public boolean yesNo; + public boolean helpAvailable; + + Input() { + text = ""; + defaultText = null; + icon = null; + minLen = 0; + maxLen = 1; + ucs2 = false; + packed = false; + digitOnly = false; + echo = false; + yesNo = false; + helpAvailable = false; + } + + private Input(Parcel in) { + text = in.readString(); + defaultText = in.readString(); + icon = in.readParcelable(null); + minLen = in.readInt(); + maxLen = in.readInt(); + ucs2 = in.readInt() == 1 ? true : false; + packed = in.readInt() == 1 ? true : false; + digitOnly = in.readInt() == 1 ? true : false; + echo = in.readInt() == 1 ? true : false; + yesNo = in.readInt() == 1 ? true : false; + helpAvailable = in.readInt() == 1 ? true : false; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(text); + dest.writeString(defaultText); + dest.writeParcelable(icon, 0); + dest.writeInt(minLen); + dest.writeInt(maxLen); + dest.writeInt(ucs2 ? 1 : 0); + dest.writeInt(packed ? 1 : 0); + dest.writeInt(digitOnly ? 1 : 0); + dest.writeInt(echo ? 1 : 0); + dest.writeInt(yesNo ? 1 : 0); + dest.writeInt(helpAvailable ? 1 : 0); + } + + public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() { + public Input createFromParcel(Parcel in) { + return new Input(in); + } + + public Input[] newArray(int size) { + return new Input[size]; + } + }; + + boolean setIcon(Bitmap Icon) { return true; } +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Item.java b/telephony/java/com/android/internal/telephony/gsm/stk/Item.java index 0122c86..b2f338c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Item.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Item.java @@ -42,6 +42,7 @@ public class Item implements Parcelable { public Item(Parcel in) { id = in.readInt(); text = in.readString(); + icon = in.readParcelable(null); } public int describeContents() { @@ -51,6 +52,7 @@ public class Item implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(text); + dest.writeParcelable(icon, flags); } public static final Parcelable.Creator<Item> CREATOR = new Parcelable.Creator<Item>() { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java b/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java index 5f9de15..40a6b37 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,10 +23,15 @@ import android.os.Parcelable; import java.util.ArrayList; import java.util.List; +/** + * Container class for STK menu (SET UP MENU, SELECT ITEM) parameters. + * + */ public class Menu implements Parcelable { public List<Item> items; - public String title; public List<TextAttribute> titleAttrs; + public PresentationType presentationType; + public String title; public Bitmap titleIcon; public int defaultItem; public boolean softKeyPreferred; @@ -36,28 +41,22 @@ public class Menu implements Parcelable { public Menu() { // Create an empty list. - this.items = new ArrayList<Item>(); - this.title = null; - this.titleAttrs = null; - this.defaultItem = 0; - this.softKeyPreferred = false; - this.helpAvailable = false; - this.titleIconSelfExplanatory = false; - this.titleIcon = null; - } - - public Menu(List<Item> items, String title, List<TextAttribute> titleAttrs, - boolean softKeyPreferred, boolean helpAvailable, int defaultItem) { - this.items = items; - this.title = title; - this.titleAttrs = titleAttrs; - this.defaultItem = defaultItem; - this.softKeyPreferred = softKeyPreferred; - this.helpAvailable = helpAvailable; + items = new ArrayList<Item>(); + title = null; + titleAttrs = null; + defaultItem = 0; + softKeyPreferred = false; + helpAvailable = false; + titleIconSelfExplanatory = false; + itemsIconSelfExplanatory = false; + titleIcon = null; + // set default style to be navigation menu. + presentationType = PresentationType.NAVIGATION_OPTIONS; } private Menu(Parcel in) { title = in.readString(); + titleIcon = in.readParcelable(null); // rebuild items list. items = new ArrayList<Item>(); int size = in.readInt(); @@ -68,6 +67,9 @@ public class Menu implements Parcelable { defaultItem = in.readInt(); softKeyPreferred = in.readInt() == 1 ? true : false; helpAvailable = in.readInt() == 1 ? true : false; + titleIconSelfExplanatory = in.readInt() == 1 ? true : false; + itemsIconSelfExplanatory = in.readInt() == 1 ? true : false; + presentationType = PresentationType.values()[in.readInt()]; } public int describeContents() { @@ -76,6 +78,7 @@ public class Menu implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(title); + dest.writeParcelable(titleIcon, flags); // write items list to the parcel. int size = items.size(); dest.writeInt(size); @@ -85,6 +88,9 @@ public class Menu implements Parcelable { dest.writeInt(defaultItem); dest.writeInt(softKeyPreferred ? 1 : 0); dest.writeInt(helpAvailable ? 1 : 0); + dest.writeInt(titleIconSelfExplanatory ? 1 : 0); + dest.writeInt(itemsIconSelfExplanatory ? 1 : 0); + dest.writeInt(presentationType.ordinal()); } public static final Parcelable.Creator<Menu> CREATOR = new Parcelable.Creator<Menu>() { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java b/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java new file mode 100644 index 0000000..9afa063 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2006-2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import com.android.internal.telephony.gsm.EncodeException; +import com.android.internal.telephony.gsm.GsmAlphabet; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; + +abstract class ResponseData { + /** + * Format the data appropriate for TERMINAL RESPONSE and write it into + * the ByteArrayOutputStream object. + */ + public abstract void format(ByteArrayOutputStream buf); +} + +class SelectItemResponseData extends ResponseData { + // members + private int id; + + public SelectItemResponseData(int id) { + super(); + this.id = id; + } + + @Override + public void format(ByteArrayOutputStream buf) { + // Item identifier object + int tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value(); + buf.write(tag); // tag + buf.write(1); // length + buf.write(id); // identifier of item chosen + } +} + +class GetInkeyInputResponseData extends ResponseData { + // members + private boolean mIsUcs2; + private boolean mIsPacked; + private boolean mIsYesNo; + private boolean mYesNoResponse; + public String mInData; + + // GetInKey Yes/No response characters constants. + protected static final byte GET_INKEY_YES = 0x01; + protected static final byte GET_INKEY_NO = 0x00; + + public GetInkeyInputResponseData(String inData, boolean ucs2, boolean packed) { + super(); + this.mIsUcs2 = ucs2; + this.mIsPacked = packed; + this.mInData = inData; + this.mIsYesNo = false; + } + + public GetInkeyInputResponseData(boolean yesNoResponse) { + super(); + this.mIsUcs2 = false; + this.mIsPacked = false; + this.mInData = ""; + this.mIsYesNo = true; + this.mYesNoResponse = yesNoResponse; + } + + @Override + public void format(ByteArrayOutputStream buf) { + if (buf == null) { + return; + } + + // Text string object + int tag = 0x80 | ComprehensionTlvTag.TEXT_STRING.value(); + buf.write(tag); // tag + + byte[] data; + + if (mIsYesNo) { + data = new byte[1]; + data[0] = mYesNoResponse ? GET_INKEY_YES : GET_INKEY_NO; + } else if (mInData != null && mInData.length() > 0) { + try { + if (mIsUcs2) { + data = mInData.getBytes("UTF-16"); + } else if (mIsPacked) { + int size = mInData.length(); + + byte[] tempData = GsmAlphabet + .stringToGsm7BitPacked(mInData); + data = new byte[size]; + // Since stringToGsm7BitPacked() set byte 0 in the + // returned byte array to the count of septets used... + // copy to a new array without byte 0. + System.arraycopy(tempData, 1, data, 0, size); + } else { + data = GsmAlphabet.stringToGsm8BitPacked(mInData); + } + } catch (UnsupportedEncodingException e) { + data = new byte[0]; + } catch (EncodeException e) { + data = new byte[0]; + } + } else { + data = new byte[0]; + } + + // length - one more for data coding scheme. + buf.write(data.length + 1); + + // data coding scheme + if (mIsUcs2) { + buf.write(0x08); // UCS2 + } else if (mIsPacked) { + buf.write(0x00); // 7 bit packed + } else { + buf.write(0x04); // 8 bit unpacked + } + + for (byte b : data) { + buf.write(b); + } + } +} + + diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java b/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java index 6559c73..b96a524 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java @@ -171,7 +171,16 @@ public enum ResultCode { * Retrieves the actual result code that this object represents. * @return Actual result code */ - public int code() { + public int value() { return mCode; } + + public static ResultCode fromInt(int value) { + for (ResultCode r : ResultCode.values()) { + if (r.mCode == value) { + return r; + } + } + return null; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java b/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java new file mode 100644 index 0000000..746b1f1 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import com.android.internal.telephony.gsm.SIMFileHandler; +import com.android.internal.telephony.gsm.SimUtils; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +import java.util.concurrent.BlockingQueue; + +/** + * Class used for queuing raw ril messages, decoding them into CommanParams + * objects and sending the result back to the STK Service. + * + */ +class RilMessageDecoder extends Handler { + + // members + private final BlockingQueue<RilMessage> mInQueue; + private static RilMessageDecoder sInstance = null; + private CommandParamsFactory mCmdParamsFactory = null; + private RilMessage mCurrentRilMessage = null; + private Handler mCaller = null; + + // constants + static final int START = 1; + static final int CMD_PARAMS_READY = 2; + + static RilMessageDecoder getInstance(BlockingQueue<RilMessage> inQ, + Handler caller, SIMFileHandler fh) { + if (sInstance != null) { + return sInstance; + } + if (inQ != null) { + HandlerThread thread = new HandlerThread("Stk RIL Messages decoder"); + thread.start(); + return new RilMessageDecoder(thread.getLooper(), inQ, caller, fh); + } + return null; + } + + private RilMessageDecoder(Looper looper, BlockingQueue<RilMessage> inQ, + Handler caller, SIMFileHandler fh) { + super(looper); + mInQueue = inQ; + mCaller = caller; + mCmdParamsFactory = CommandParamsFactory.getInstance(this, fh); + } + + public void handleMessage(Message msg) { + switch(msg.what) { + case START: + start(); + break; + case CMD_PARAMS_READY: + mCurrentRilMessage.mResCode = ResultCode.fromInt(msg.arg1); + mCurrentRilMessage.mData = msg.obj; + sendCmdForExecution(); + break; + } + } + + private void start() { + boolean interrupted = false; + try { + while (true) { + try { + mCurrentRilMessage = mInQueue.take(); + StkLog.d(this, "Decoding new message"); + break; + } catch (InterruptedException e) { + interrupted = true; + // fall through and retry + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + if (mCurrentRilMessage != null) { + decodeMessage(mCurrentRilMessage); + } + } + } + + private void decodeMessage(RilMessage msg) { + switch(msg.mId) { + case Service.MSG_ID_SESSION_END: + case Service.MSG_ID_CALL_SETUP: + mCurrentRilMessage.mResCode = ResultCode.OK; + sendCmdForExecution(); + break; + case Service.MSG_ID_PROACTIVE_COMMAND: + case Service.MSG_ID_EVENT_NOTIFY: + case Service.MSG_ID_REFRESH: + byte[] rawData = null; + try { + rawData = SimUtils.hexStringToBytes((String) msg.mData); + } catch (Exception e) { + // zombie messages are dropped + getNextMessage(); + return; + } + try { + // Start asynch parsing of the command parameters. + mCmdParamsFactory.make(BerTlv.decode(rawData)); + } catch (ResultException e) { + // send to Service for proper RIL communication. + mCurrentRilMessage.mResCode = e.result(); + sendCmdForExecution(); + } + break; + } + } + + private void sendCmdForExecution() { + Message msg = mCaller.obtainMessage(Service.MSG_ID_RIL_MSG_DECODED, + new RilMessage(mCurrentRilMessage)); + msg.sendToTarget(); + getNextMessage(); + } + + private void getNextMessage() { + Message nextMsg = this.obtainMessage(START); + nextMsg.sendToTarget(); + } +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Service.java b/telephony/java/com/android/internal/telephony/gsm/stk/Service.java index e002202..aae9b30 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Service.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Service.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,41 +16,24 @@ package com.android.internal.telephony.gsm.stk; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.AsyncResult; import android.os.Handler; +import android.os.HandlerThread; import android.os.Message; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; import com.android.internal.telephony.gsm.CommandsInterface; -import com.android.internal.telephony.gsm.EncodeException; -import com.android.internal.telephony.gsm.GsmAlphabet; import com.android.internal.telephony.gsm.GsmSimCard; import com.android.internal.telephony.gsm.SIMFileHandler; import com.android.internal.telephony.gsm.SIMRecords; import com.android.internal.telephony.gsm.SimUtils; -import com.android.internal.telephony.gsm.stk.Duration.TimeUnit; import android.util.Config; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.RemoteViews; -import android.widget.TextView; -import android.widget.Toast; import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; /** * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If @@ -93,51 +76,12 @@ enum ComprehensionTlvTag { * * @return Actual tag value of this object */ - public int value() { - return mValue; - } -} - -/** - * Enumeration for representing "Type of Command" of proactive commands. If you - * want to create a CommandType object, call the static method {@link - * #fromInt(int) fromInt}. - * - * {@hide} - */ -enum CommandType { - DISPLAY_TEXT(0x21), - GET_INKEY(0x22), - GET_INPUT(0x23), - LAUNCH_BROWSER(0x15), - PLAY_TONE(0x20), - REFRESH(0x01), - SELECT_ITEM(0x24), - SEND_SS(0x11), - SEND_USSD(0x12), - SEND_SMS(0x13), - SEND_DTMF(0x14), - SET_UP_EVENT_LIST(0x05), - SET_UP_IDLE_MODE_TEXT(0x28), - SET_UP_MENU(0x25), - SET_UP_CALL(0x10); - - private int mValue; - - CommandType(int value) { - mValue = value; - } + public int value() { + return mValue; + } - /** - * Create a CommandType object. - * - * @param value Integer value to be converted to a CommandType object. - * @return CommandType object whose "Type of Command" value is {@code - * value}. If no CommandType object has that value, null is - * returned. - */ - public static CommandType fromInt(int value) { - for (CommandType e : CommandType.values()) { + public static ComprehensionTlvTag fromInt(int value) { + for (ComprehensionTlvTag e : ComprehensionTlvTag.values()) { if (e.mValue == value) { return e; } @@ -146,8 +90,26 @@ enum CommandType { } } +class RilMessage { + int mId; + Object mData; + ResultCode mResCode; + + RilMessage(int msgId, String rawData) { + mId = msgId; + mData = rawData; + } + + RilMessage(RilMessage other) { + this.mId = other.mId; + this.mData = other.mData; + this.mResCode = other.mResCode; + } +} + /** - * Main class that implements SIM Toolkit Service. + * Class that implements SIM Toolkit Telephony Service. Interacts with the RIL + * and application. * * {@hide} */ @@ -158,30 +120,26 @@ public class Service extends Handler implements AppInterface { private CommandsInterface mCmdIf; private SIMRecords mSimRecords; private Context mContext; - private GsmSimCard mSimCard; - private CommandListener mCmdListener; - private Object mCmdListenerLock = new Object(); - private CommandParams mCmdParams = null; - private CommandParams mNextCmdParams = null; - private State mState = State.IDLE; - private Menu mMainMenu = null; - private String mServiceName = ""; - private NotificationManager mNm = null; - private int mAppIndicator = APP_INDICATOR_PRE_BOOT; - private int mInstallIndicator = APP_INDICATOR_UNINSTALLED; - private IconLoader mIconLoader = null; - - private static final String TAG = "STK"; + private StkCmdMessage mCurrntCmd = null; + private StkCmdMessage mMenuCmd = null; + + private BlockingQueue<RilMessage> mRilMessagesQ = null; + private RilMessageDecoder mMsgDecoder = null; + + public static final String TAG = "STK"; // Service constants. - private static final int EVENT_SESSION_END = 1; - private static final int EVENT_PROACTIVE_COMMAND = 2; - private static final int EVENT_EVENT_NOTIFY = 3; - private static final int EVENT_CALL_SETUP = 4; + static final int MSG_ID_SESSION_END = 1; + static final int MSG_ID_PROACTIVE_COMMAND = 2; + static final int MSG_ID_EVENT_NOTIFY = 3; + static final int MSG_ID_CALL_SETUP = 4; + static final int MSG_ID_REFRESH = 5; + static final int MSG_ID_RESPONSE = 6; + + static final int MSG_ID_RIL_MSG_DECODED = 10; + // Events to signal SIM presence or absent in the device. - private static final int EVENT_SIM_LOADED = 12; - private static final int EVENT_SIM_ABSENT = 13; - static final int EVENT_LOAD_ICON_DONE = 14; + private static final int MSG_ID_SIM_LOADED = 20; private static final int DEV_ID_KEYPAD = 0x01; private static final int DEV_ID_DISPLAY = 0x02; @@ -190,64 +148,6 @@ public class Service extends Handler implements AppInterface { private static final int DEV_ID_TERMINAL = 0x82; private static final int DEV_ID_NETWORK = 0x83; - // Event value for Event List COMPREHENSION-TLV object - public static final int UICC_EVENT_MT_CALL = 0x00; - public static final int UICC_EVENT_CALL_CONNECTED = 0x01; - public static final int UICC_EVENT_CALL_DISCONNECTED = 0x02; - public static final int UICC_EVENT_LOCATION_STATUS = 0x03; - public static final int UICC_EVENT_USER_ACTIVITY = 0x04; - public static final int UICC_EVENT_IDLE_SCREEN_AVAILABLE = 0x05; - public static final int UICC_EVENT_CARD_READER_STATUS = 0x06; - public static final int UICC_EVENT_LANGUAGE_SELECTION = 0x07; - public static final int UICC_EVENT_BROWSER_TERMINATION = 0x08; - public static final int UICC_EVENT_DATA_AVAILABLE = 0x09; - public static final int UICC_EVENT_CHANNEL_STATUS = 0x0a; - public static final int UICC_EVENT_ACCESS_TECH_CHANGE = 0x0b; - public static final int UICC_EVENT_DISPLAY_PARAMS_CHANGE = 0x0c; - public static final int UICC_EVENT_LOCAL_CONNECTION = 0x0d; - public static final int UICC_EVENT_NETWORK_SEARCH_MODE_CHANGE = 0x0e; - public static final int UICC_EVENT_BROWSING_STATUS = 0x0f; - public static final int UICC_EVENT_FRAMES_INFO_CHANGE = 0x10; - public static final int UICC_EVENT_I_WLAN_ACESS_STATUS = 0x11; - - // Command Qualifier values - static final int REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00; - static final int REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02; - static final int REFRESH_NAA_INIT = 0x03; - static final int REFRESH_UICC_RESET = 0x04; - - // GetInKey Yes/No response characters constants. - private static final byte GET_INKEY_YES = 0x01; - private static final byte GET_INKEY_NO = 0x00; - - // Notification id used to display Idle Mode text in NotificationManager. - static final int STK_NOTIFICATION_ID = 333; - - private static String APP_PACKAGE_NAME = "com.android.stk"; - private static String APP_FULL_NAME = APP_PACKAGE_NAME + ".StkActivity"; - - // Application indicators constants - static final int APP_INDICATOR_PRE_BOOT = 0; - static final int APP_INDICATOR_UNINSTALLED = 1; - static final int APP_INDICATOR_INSTALLED_NORMAL = 2; - static final int APP_INDICATOR_INSTALLED_SPECIAL = 3; - private static final int APP_INDICATOR_LAUNCHED = 4; - // Use setAppIndication(APP_INSTALL_INDICATOR) to go back for the original - // install indication. - private static final int APP_INSTALL_INDICATOR = 5; - - // Container class to hold temporary icon identifier TLV object info. - class IconId { - int recordNumber; - boolean selfExplanatory; - } - - // Container class to hold temporary item icon identifier list TLV object info. - class ItemsIconId { - int [] recordNumbers; - boolean selfExplanatory; - } - /* Intentionally private for singleton */ private Service(CommandsInterface ci, SIMRecords sr, Context context, SIMFileHandler fh, GsmSimCard sc) { @@ -259,108 +159,200 @@ public class Service extends Handler implements AppInterface { mCmdIf = ci; mContext = context; - mCmdIf.setOnStkSessionEnd(this, EVENT_SESSION_END, null); - mCmdIf.setOnStkProactiveCmd(this, EVENT_PROACTIVE_COMMAND, null); - mCmdIf.setOnStkEvent(this, EVENT_EVENT_NOTIFY, null); - mCmdIf.setOnStkCallSetUp(this, EVENT_CALL_SETUP, null); - - mSimRecords = sr; + // Initialize a blocking queue to be used for ril messages, and a + // RilMessagesDecoder for decoding the messages into a CommandParams. + // Each CommandParams is put into a SynchronousQueue and pulled by the + // Service take() when its ready to handle the next command. + mRilMessagesQ = new LinkedBlockingQueue<RilMessage>(); + mMsgDecoder = RilMessageDecoder.getInstance(mRilMessagesQ, this, fh); - mSimCard = sc; - mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - mIconLoader = IconLoader.getInstance(this, fh); + // Register ril events handling. + mCmdIf.setOnStkSessionEnd(this, MSG_ID_SESSION_END, null); + mCmdIf.setOnStkProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null); + mCmdIf.setOnStkEvent(this, MSG_ID_EVENT_NOTIFY, null); + mCmdIf.setOnStkCallSetUp(this, MSG_ID_CALL_SETUP, null); + //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null); - // Register a receiver for install/unistall application. - StkAppStateReceiver receiver = new StkAppStateReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(StkAppInstaller.STK_APP_INSTALL_ACTION); - filter.addAction(StkAppInstaller.STK_APP_UNINSTALL_ACTION); - mContext.registerReceiver(receiver, filter); + mSimRecords = sr; // Register for SIM ready event. - mSimRecords.registerForRecordsLoaded(this, EVENT_SIM_LOADED, null); - mSimCard.registerForAbsent(this, EVENT_SIM_ABSENT, null); + mSimRecords.registerForRecordsLoaded(this, MSG_ID_SIM_LOADED, null); + // start decoding ril messages. + mMsgDecoder.obtainMessage(RilMessageDecoder.START).sendToTarget(); } - /** - * Used for retrieving the only Service object in the system. There is only - * one Service object. - * - * @param ci CommandsInterface object - * @param sr SIMRecords object - * @return The only Service object in the system - */ - public static Service getInstance(CommandsInterface ci, SIMRecords sr, - Context context, SIMFileHandler fh, GsmSimCard sc) { - if (sInstance == null) { - if (ci == null || sr == null || context == null || fh == null - || sc == null) { - return null; + private void handleRilMsg(RilMessage rilMsg) { + if (rilMsg == null) { + return; + } + + // dispatch messages + CommandParams cmdParams = null; + switch (rilMsg.mId) { + case MSG_ID_EVENT_NOTIFY: + if (rilMsg.mResCode == ResultCode.OK) { + cmdParams = (CommandParams) rilMsg.mData; + if (cmdParams != null) { + handleProactiveCommand(cmdParams); + } } - sInstance = new Service(ci, sr, context, fh, sc); + break; + case MSG_ID_PROACTIVE_COMMAND: + cmdParams = (CommandParams) rilMsg.mData; + if (cmdParams != null) { + if (rilMsg.mResCode == ResultCode.OK) { + handleProactiveCommand(cmdParams); + } else { + // for proactive commands that couldn't be decoded + // successfully respond with the code generated by the + // message decoder. + sendTerminalResponse(cmdParams.cmdDet, rilMsg.mResCode, + false, 0, null); + } + } + break; + case MSG_ID_REFRESH: + cmdParams = (CommandParams) rilMsg.mData; + if (cmdParams != null) { + handleProactiveCommand(cmdParams); + } + break; + case MSG_ID_SESSION_END: + handleSessionEnd(); + break; + case MSG_ID_CALL_SETUP: + // prior event notify command supplied all the information + // needed for set up call processing. + break; } - return sInstance; } /** - * Used for retrieving the only Service object in the system. There is only - * one Service object. + * Handles RIL_UNSOL_STK_PROACTIVE_COMMAND unsolicited command from RIL. + * Sends valid proactive command data to the application using intents. * - * @return The only Service object in the system */ - public static Service getInstance() { - return getInstance(null, null, null, null, null); - } + private void handleProactiveCommand(CommandParams cmdParams) { + StkLog.d(this, cmdParams.getCommandType().name()); - /** - * {@inheritDoc} - */ - public void setCommandListener(CommandListener l) { - synchronized (mCmdListenerLock) { - mCmdListener = l; - if (mCmdListener != null) { - setAppIndication(APP_INDICATOR_LAUNCHED); + StkCmdMessage cmdMsg = new StkCmdMessage(cmdParams); + switch (cmdParams.getCommandType()) { + case SET_UP_MENU: + if (removeMenu(cmdMsg.getMenu())) { + mMenuCmd = null; } else { - setAppIndication(APP_INSTALL_INDICATOR); + mMenuCmd = cmdMsg; + } + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, + null); + break; + case DISPLAY_TEXT: + // when application is not required to respond, send an immediate + // response. + if (!cmdMsg.geTextMessage().responseNeeded) { + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, + 0, null); } - } - } - - synchronized void setAppIndication(int indication) { - switch(indication) { - case APP_INDICATOR_PRE_BOOT: - case APP_INDICATOR_UNINSTALLED: - case APP_INDICATOR_INSTALLED_NORMAL: - case APP_INDICATOR_INSTALLED_SPECIAL: - case APP_INDICATOR_LAUNCHED: - mAppIndicator = indication; break; - case APP_INSTALL_INDICATOR: - mAppIndicator = mInstallIndicator; + case REFRESH: + // ME side only handles refresh commands which meant to remove IDLE + // MODE TEXT. + cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT + .value(); + break; + case SET_UP_IDLE_MODE_TEXT: + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, + 0, null); + break; + case LAUNCH_BROWSER: + case SELECT_ITEM: + case GET_INPUT: + case GET_INKEY: + case SEND_DTMF: + case SEND_SMS: + case SEND_SS: + case SEND_USSD: + case PLAY_TONE: + case SET_UP_CALL: + // nothing to do on telephony! break; default: - throw new NullPointerException("Trying to set wrong app indication"); + StkLog.d(this, "Unsupported command"); + return; } - } - - public synchronized int getAppIndication() { - return mAppIndicator; + mCurrntCmd = cmdMsg; + Intent intent = new Intent(AppInterface.STK_CMD_ACTION); + intent.putExtra("STK CMD", cmdMsg); + mContext.sendBroadcast(intent); } /** - * {@inheritDoc} + * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL. + * */ - public State getState() { - return mState; + private void handleSessionEnd() { + StkLog.d(this, "SESSION END"); + + mCurrntCmd = mMenuCmd; + Intent intent = new Intent(AppInterface.STK_SESSION_END_ACTION); + mContext.sendBroadcast(intent); } - /** - * {@inheritDoc} - */ - public void notifyMenuSelection(int menuId, boolean helpRequired) { - if (mState != State.MAIN_MENU) { + private void sendTerminalResponse(CommandDetails cmdDet, + ResultCode resultCode, boolean includeAdditionalInfo, + int additionalInfo, ResponseData resp) { + + if (cmdDet == null) { return; } + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + // command details + int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); + if (cmdDet.compRequired) { + tag |= 0x80; + } + buf.write(tag); + buf.write(0x03); // length + buf.write(cmdDet.commandNumber); + buf.write(cmdDet.typeOfCommand); + buf.write(cmdDet.commandQualifier); + + // device identities + tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); + buf.write(tag); + buf.write(0x02); // length + buf.write(DEV_ID_TERMINAL); // source device id + buf.write(DEV_ID_UICC); // destination device id + + // result + tag = 0x80 | ComprehensionTlvTag.RESULT.value(); + buf.write(tag); + int length = includeAdditionalInfo ? 2 : 1; + buf.write(length); + buf.write(resultCode.value()); + + // additional info + if (includeAdditionalInfo) { + buf.write(additionalInfo); + } + + // Fill optional data for each corresponding command + if (resp != null) { + resp.format(buf); + } + + byte[] rawData = buf.toByteArray(); + String hexString = SimUtils.bytesToHexString(rawData); + if (Config.LOGD) { + StkLog.d(this, "TERMINAL RESPONSE: " + hexString); + } + + mCmdIf.sendTerminalResponse(hexString, null); + } + + + private void sendMenuSelection(int menuId, boolean helpRequired) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); @@ -402,112 +394,6 @@ public class Service extends Handler implements AppInterface { mCmdIf.sendEnvelope(hexString, null); } - /** - * {@inheritDoc} - */ - public void notifyUserActivity() { - eventDownload(UICC_EVENT_USER_ACTIVITY, DEV_ID_TERMINAL, DEV_ID_UICC, - null, true); - } - - /** - * {@inheritDoc} - */ - public void notifyDisplayTextEnded(ResultCode terminationCode) { - if (mState != State.DISPLAY_TEXT) { - return; - } - ResultCode rc = ResultCode.OK; - - switch (terminationCode) { - case OK: - case BACKWARD_MOVE_BY_USER: - case NO_RESPONSE_FROM_USER: - rc = terminationCode; - break; - default: - Log.d(TAG, "Invalid termination code for Display Text"); - return; - } - sendTerminalResponse(mCmdParams.cmdDet, rc, false, 0, null); - } - - /** - * {@inheritDoc} - */ - public void notifyToneEnded() { - if (mState != State.PLAY_TONE) { - return; - } - - sendTerminalResponse(mCmdParams.cmdDet, ResultCode.OK, false, 0, - null); - } - - /** - * {@inheritDoc} - */ - public void notifyIdleScreenAvailable() { - eventDownload(UICC_EVENT_IDLE_SCREEN_AVAILABLE, DEV_ID_DISPLAY, - DEV_ID_UICC, null, true); - } - - /** - * {@inheritDoc} - */ - public void notifyLanguageSelection(String langCode) { - assert langCode.length() == 2 : "Language code must be two characters"; - - byte[] lang = GsmAlphabet.stringToGsm8BitPacked(langCode); - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - - // language - int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value(); - buf.write(tag); - buf.write(0x02); // length - buf.write(lang[0]); - buf.write(lang[1]); - - byte[] info = buf.toByteArray(); - - eventDownload(UICC_EVENT_LANGUAGE_SELECTION, DEV_ID_TERMINAL, - DEV_ID_UICC, info, false); - } - - /** - * {@inheritDoc} - */ - public void notifyBrowserTermination(boolean isErrorTermination) { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - - int cause = isErrorTermination ? 1 : 0; - - // browser termination cause - int tag = 0x80 | ComprehensionTlvTag.BROWSER_TERMINATION_CAUSE.value(); - buf.write(tag); - buf.write(0x01); // length - buf.write(cause); - - byte[] info = buf.toByteArray(); - - eventDownload(UICC_EVENT_BROWSER_TERMINATION, DEV_ID_TERMINAL, - DEV_ID_UICC, info, true); - } - - /** - * {@inheritDoc} - */ - public void notifyLaunchBrowser(boolean userConfirmed) { - - if (mState != State.LAUNCH_BROWSER) { - return; - } - - ResultCode rc = userConfirmed ? ResultCode.OK - : ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS; - sendTerminalResponse(mCmdParams.cmdDet, rc, false, 0, null); - } - private void eventDownload(int event, int sourceId, int destinationId, byte[] additionalInfo, boolean oneShot) { @@ -552,1916 +438,170 @@ public class Service extends Handler implements AppInterface { } /** - * {@inheritDoc} - */ - public void notifyInkey(char key, boolean helpRequired) { - if (mState != State.GET_INKEY) { - return; - } - - GetInkeyInputResponseData resp = null; - ResultCode result = ResultCode.OK; - GetInkeyParams request = (GetInkeyParams) mCmdParams; - if (helpRequired) { - result = ResultCode.HELP_INFO_REQUIRED; - } else { - resp = new GetInkeyInputResponseData(Character.toString(key), - request.isUcs2, false); - } - - sendTerminalResponse(request.cmdDet, result, false, 0, resp); - } - - /** - * {@inheritDoc} - */ - public void notifyInkey(boolean yesNoResponse, boolean helpRequired) { - if (mState != State.GET_INKEY) { - return; - } - - GetInkeyInputResponseData resp = null; - ResultCode result = ResultCode.OK; - GetInkeyParams cmdParams = (GetInkeyParams) mCmdParams; - if (!cmdParams.isYesNo) { - // Illegal use of this call. - return; - } - if (helpRequired) { - result = ResultCode.HELP_INFO_REQUIRED; - } else { - resp = new GetInkeyInputResponseData(yesNoResponse); - } - - sendTerminalResponse(cmdParams.cmdDet, result, false, 0, resp); - } - - /** - * {@inheritDoc} - */ - public void notifyInput(String input, boolean helpRequired) { - if (mState != State.GET_INPUT) { - return; - } - - GetInkeyInputResponseData resp = null; - GetInputParams cmdParams = (GetInputParams) mCmdParams; - ResultCode result = ResultCode.OK; - - if (helpRequired) { - result = ResultCode.HELP_INFO_REQUIRED; - } else { - resp = new GetInkeyInputResponseData(input, cmdParams.isUcs2, - cmdParams.isPacked); - } - sendTerminalResponse(cmdParams.cmdDet, result, false, 0, resp); - } - - /** - * {@inheritDoc} - */ - public void notifySelectedItem(int id, boolean helpRequired) { - if (mState != State.SELECT_ITEM) { - return; - } - - SelectItemResponseData resp = new SelectItemResponseData(id); - ResultCode result = helpRequired ? ResultCode.HELP_INFO_REQUIRED - : ResultCode.OK; - - sendTerminalResponse(mCmdParams.cmdDet, result, false, 0, resp); - } - - /** - * {@inheritDoc} - */ - public void notifyNoResponse() { - CtlvCommandDetails cmdDet = getCurrentCmdDet(); - if (cmdDet == null) { - // Unable to continue; - return; - } - sendTerminalResponse(cmdDet, ResultCode.NO_RESPONSE_FROM_USER, false, - 0, null); - } - - /** - * {@inheritDoc} - */ - public void acceptOrRejectCall(boolean accept) { - if (mState != State.CALL_SETUP) { - return; - } - mCmdIf.handleCallSetupRequestFromSim(accept, null); - } - - /** - * Indicates if STK is supported by the SIM card. + * Used for instantiating the Service from the GsmPhone constructor. + * + * @param ci CommandsInterface object + * @param sr SIMRecords object + * @param context phone app context + * @param fh SIM file handler + * @param sc GSM SIM card + * @return The only Service object in the system */ - public boolean isStkSupported() { - switch (getAppIndication()) { - case APP_INDICATOR_PRE_BOOT: - case APP_INDICATOR_UNINSTALLED: - return false; + public static Service getInstance(CommandsInterface ci, SIMRecords sr, + Context context, SIMFileHandler fh, GsmSimCard sc) { + if (sInstance == null) { + if (ci == null || sr == null || context == null || fh == null + || sc == null) { + return null; + } + HandlerThread thread = new HandlerThread("Stk Telephony service"); + thread.start(); + sInstance = new Service(ci, sr, context, fh, sc); } - - return true; + return sInstance; } /** - * Returns the unique service name for STK. + * Used by application to get an AppInterface object. + * + * @return The only Service object in the system */ - public String getServiceName() { - return mServiceName; + public static AppInterface getInstance() { + return getInstance(null, null, null, null, null); } /** * {@inheritDoc} */ public void handleMessage(Message msg) { - AsyncResult ar; switch (msg.what) { - case EVENT_SESSION_END: - ar = (AsyncResult) msg.obj; - handleSessionEnd(ar.result); - break; - case EVENT_PROACTIVE_COMMAND: - ar = (AsyncResult) msg.obj; - handleProactiveCommand((String) ar.result); + case MSG_ID_SESSION_END: + case MSG_ID_PROACTIVE_COMMAND: + case MSG_ID_EVENT_NOTIFY: + case MSG_ID_REFRESH: + StkLog.d(this, "ril message arrived"); + String data = null; + if (msg.obj != null) { + AsyncResult ar = (AsyncResult) msg.obj; + if (ar != null && ar.result != null) { + try { + data = (String) ar.result; + } catch (ClassCastException e) { + break; + } + } + } + mRilMessagesQ.add(new RilMessage(msg.what, data)); break; - case EVENT_EVENT_NOTIFY: - ar = (AsyncResult) msg.obj; - handleEventNotify((String) ar.result); + case MSG_ID_CALL_SETUP: + mRilMessagesQ.add(new RilMessage(msg.what, null)); break; - case EVENT_CALL_SETUP: - mState = State.CALL_SETUP; + case MSG_ID_SIM_LOADED: break; - case EVENT_SIM_LOADED: - case EVENT_SIM_ABSENT: - if (!isStkSupported()) { - setAppState(false); - } + case MSG_ID_RIL_MSG_DECODED: + handleRilMsg((RilMessage) msg.obj); break; - case EVENT_LOAD_ICON_DONE: - handleProactiveCommandIcons(msg.obj); + case MSG_ID_RESPONSE: + handleCmdResponse((StkResponseMessage) msg.obj); break; default: throw new AssertionError("Unrecognized STK command: " + msg.what); } } - - /** - * Send terminal response for backward move in the proactive SIM session - * requested by the user - * - * Only available when responding following proactive commands - * DISPLAY_TEXT(0x21), - * GET_INKEY(0x22), - * GET_INPUT(0x23), - * SET_UP_MENU(0x25); - * - * @return true if stk can send backward move response - * - */ - public boolean backwardMove() { - CtlvCommandDetails cmdDet = null; - - cmdDet = getCurrentCmdDet(); - - if (cmdDet == null) { - return false; - } - - sendTerminalResponse(cmdDet, ResultCode.BACKWARD_MOVE_BY_USER, false, - 0, null); - return true; - } - - /** - * Send terminal response for proactive SIM session terminated by the user - * - * Only available when responding following proactive commands - * DISPLAY_TEXT(0x21), - * GET_INKEY(0x22), - * GET_INPUT(0x23), - * PLAY_TONE(0x20), - * SET_UP_MENU(0x25); - * - * @return true if stk can send terminate session response - */ - public boolean terminateSession() { - CtlvCommandDetails cmdDet = null; - - cmdDet = getCurrentCmdDet(); - - if (cmdDet == null) { - return false; - } - - sendTerminalResponse(cmdDet, ResultCode.UICC_SESSION_TERM_BY_USER, - false, 0, null); - mState = State.MAIN_MENU; - return true; - } - - private CtlvCommandDetails getCurrentCmdDet() { - CtlvCommandDetails cmdDet = null; - if (mCmdParams != null) { - cmdDet = mCmdParams.cmdDet; + public synchronized void onCmdResponse(StkResponseMessage resMsg) { + if (resMsg == null) { + return; } - - return cmdDet; + // queue a response message. + Message msg = this.obtainMessage(MSG_ID_RESPONSE, resMsg); + msg.sendToTarget(); } - /** - * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL. - * - * @param data Null object. Do not use this. - */ - private void handleSessionEnd(Object data) { - if (Config.LOGD) { - Log.d(TAG, "handleSessionEnd begins"); + private boolean validateResponse(StkResponseMessage resMsg) { + if (mCurrntCmd != null) { + return (resMsg.cmdDet.compareTo(mCurrntCmd.mCmdDet)); } - switch (mInstallIndicator) { - case APP_INDICATOR_INSTALLED_NORMAL: - mState = State.MAIN_MENU; - break; - case APP_INDICATOR_INSTALLED_SPECIAL: - case APP_INDICATOR_UNINSTALLED: - mState = State.IDLE; - break; - default: - Log.d(TAG, "Can't set service state"); - } - synchronized (mCmdListenerLock) { - if (mCmdListener != null) { - mCmdListener.onSessionEnd(); - } - } - } - - class CtlvDeviceIdentities { - public int sourceId; - public int destinationId; - } - - abstract class ResponseData { - /** - * Format the data appropriate for TERMINAL RESPONSE and write it into - * the ByteArrayOutputStream object. - */ - public abstract void format(ByteArrayOutputStream buf); + return false; } - class GetInkeyInputResponseData extends ResponseData { - private boolean mIsUcs2; - private boolean mIsPacked; - private boolean mIsYesNo; - private boolean mYesNoResponse; - public String mInData; - - public GetInkeyInputResponseData(String inData, boolean ucs2, - boolean packed) { - super(); - this.mIsUcs2 = ucs2; - this.mIsPacked = packed; - this.mInData = inData; - this.mIsYesNo = false; - } - - public GetInkeyInputResponseData(boolean yesNoResponse) { - super(); - this.mIsUcs2 = false; - this.mIsPacked = false; - this.mInData = ""; - this.mIsYesNo = true; - this.mYesNoResponse = yesNoResponse; - } - - @Override - public void format(ByteArrayOutputStream buf) { - if (buf == null) { - return; - } - - // Text string object - int tag = 0x80 | ComprehensionTlvTag.TEXT_STRING.value(); - buf.write(tag); // tag - - byte[] data; - - if (mIsYesNo) { - data = new byte[1]; - data[0] = mYesNoResponse ? GET_INKEY_YES : GET_INKEY_NO; - } else if (mInData != null && mInData.length() > 0) { - try { - if (mIsUcs2) { - data = mInData.getBytes("UTF-16"); - } else if (mIsPacked) { - int size = mInData.length(); - - byte[] tempData = GsmAlphabet - .stringToGsm7BitPacked(mInData); - data = new byte[size]; - // Since stringToGsm7BitPacked() set byte 0 in the - // returned byte array to the count of septets used... - // copy to a new array without byte 0. - System.arraycopy(tempData, 1, data, 0, size); - } else { - data = GsmAlphabet.stringToGsm8BitPacked(mInData); - } - } catch (UnsupportedEncodingException e) { - data = new byte[0]; - } catch (EncodeException e) { - data = new byte[0]; - } - } else { - data = new byte[0]; - } - - // length - one more for data coding scheme. - buf.write(data.length + 1); - - // data coding scheme - if (mIsUcs2) { - buf.write(0x08); // UCS2 - } else if (mIsPacked) { - buf.write(0x00); // 7 bit packed - } else { - buf.write(0x04); // 8 bit unpacked - } - - for (byte b : data) { - buf.write(b); + private boolean removeMenu(Menu menu) { + try { + if (menu.items.size() == 1 && menu.items.get(0) == null) { + return true; } + } catch (NullPointerException e) { + StkLog.d(this, "Unable to get Menu's items size"); + return true; } + return false; } - class SelectItemResponseData extends ResponseData { - private int id; - - public SelectItemResponseData(int id) { - super(); - this.id = id; - } - - @Override - public void format(ByteArrayOutputStream buf) { - // Item identifier object - int tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value(); - buf.write(tag); // tag - buf.write(1); // length - buf.write(id); // identifier of item chosen - } - } - - /** - * Handles RIL_UNSOL_STK_PROACTIVE_COMMAND unsolicited command from RIL. - * This method parses the data transmitted from the SIM card, and handles - * the command according to the "Type of Command". Each proactive command is - * handled by a corresponding handleXXX() method. - * - * @param data String containing SAT/USAT proactive command in hexadecimal - * format starting with command tag - */ - private void handleProactiveCommand(String data) { - if (Config.LOGD) { - Log.d(TAG, "handleProactiveCommand begins"); - } - // If commands arrives before the SIM loaded/SIM absent events have - // arrived post a message for a delayed processing in 2 seconds. - if (getAppIndication() == APP_INDICATOR_PRE_BOOT) { - Message installMsg = this.obtainMessage(EVENT_PROACTIVE_COMMAND); - AsyncResult.forMessage(installMsg, data, null); - sendMessageDelayed(installMsg, 2000); + private void handleCmdResponse(StkResponseMessage resMsg) { + // make sure the response details match the last valid command. + if (!validateResponse(resMsg)) { return; } + ResponseData resp = null; + boolean helpRequired = false; + CommandDetails cmdDet = resMsg.getCmdDetails(); - CtlvCommandDetails cmdDet = null; - try { - byte[] rawData = SimUtils.hexStringToBytes(data); - BerTlv berTlv = BerTlv.decode(rawData); - - List<ComprehensionTlv> ctlvs = berTlv.getComprehensionTlvs(); - cmdDet = retrieveCommandDetails(ctlvs); - - CommandType cmdType = CommandType.fromInt(cmdDet.typeOfCommand); - if (cmdType == null) { - throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY); - } - - // SET UP MENU & SET up IDLE MODE TEXT commands should not trigger - // the special install & launch sequence. - if (cmdType != CommandType.SET_UP_MENU - && cmdType != CommandType.SET_UP_IDLE_MODE_TEXT) { - switch (getAppIndication()) { - case APP_INDICATOR_UNINSTALLED: - setAppState(true); - setAppIndication(APP_INDICATOR_INSTALLED_SPECIAL); - mInstallIndicator = APP_INDICATOR_INSTALLED_SPECIAL; - Message installMsg = this - .obtainMessage(EVENT_PROACTIVE_COMMAND); - AsyncResult.forMessage(installMsg, data, null); - sendMessageDelayed(installMsg, 20); - return; - case APP_INDICATOR_INSTALLED_SPECIAL: - case APP_INDICATOR_INSTALLED_NORMAL: - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName("com.android.stk", - "com.android.stk.StkActivity"); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); - Message launchMsg = this - .obtainMessage(EVENT_PROACTIVE_COMMAND); - AsyncResult.forMessage(launchMsg, data, null); - sendMessageDelayed(launchMsg, 2000); - return; - } - } - - CtlvDeviceIdentities devIds = retrieveDeviceIdentities(ctlvs); - boolean cmdPending = false; - boolean responseNeeded = false; - - switch (cmdType) { - case DISPLAY_TEXT: - cmdPending = processDisplayText(cmdDet, devIds, ctlvs); - responseNeeded = true; - break; + switch (resMsg.resCode) { + case HELP_INFO_REQUIRED: + helpRequired = true; + case OK: + case PRFRMD_WITH_PARTIAL_COMPREHENSION: + case PRFRMD_WITH_MISSING_INFO: + case PRFRMD_WITH_ADDITIONAL_EFS_READ: + case PRFRMD_ICON_NOT_DISPLAYED: + case PRFRMD_MODIFIED_BY_NAA: + case PRFRMD_LIMITED_SERVICE: + case PRFRMD_WITH_MODIFICATION: + case PRFRMD_NAA_NOT_ACTIVE: + case PRFRMD_TONE_NOT_PLAYED: + switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) { case SET_UP_MENU: - cmdPending = processSetUpMenu(cmdDet, devIds, ctlvs); - responseNeeded = true; - break; - case SET_UP_IDLE_MODE_TEXT: - cmdPending = processSetUpIdleModeText(cmdDet, devIds, ctlvs); - responseNeeded = true; - break; - case GET_INKEY: - cmdPending = processGetInkey(cmdDet, devIds, ctlvs); - break; - case GET_INPUT: - cmdPending = processGetInput(cmdDet, devIds, ctlvs); - break; - case REFRESH: - processRefresh(cmdDet, devIds, ctlvs); - responseNeeded = true; - break; + helpRequired = resMsg.resCode == ResultCode.HELP_INFO_REQUIRED; + sendMenuSelection(resMsg.usersMenuSelection, helpRequired); + return; case SELECT_ITEM: - cmdPending = processSelectItem(cmdDet, devIds, ctlvs); - break; - case LAUNCH_BROWSER: - cmdPending = processLaunchBrowser(cmdDet, devIds, ctlvs); - break; - case PLAY_TONE: - cmdPending = processPlayTone(cmdDet, devIds, ctlvs); + resp = new SelectItemResponseData(resMsg.usersMenuSelection); break; - default: - // This should never be reached! - throw new AssertionError( - "Add case statements for the newly added " - + "command types!"); - } - if (!cmdPending) { - callStkApp(cmdType); - } - if (responseNeeded) { - sendTerminalResponse(cmdDet, ResultCode.OK, false, 0, null); - } - } catch (ResultException e) { - sendTerminalResponse(cmdDet, e.result(), e.hasAdditionalInfo(), e - .additionalInfo(), null); - } - } - - private void handleProactiveCommandIcons(Object data) { - CommandType cmdType = CommandType - .fromInt(mNextCmdParams.cmdDet.typeOfCommand); - boolean needsResponse = false; - Bitmap[] icons = null; - int iconIndex = 0; - - switch (cmdType) { - case SET_UP_IDLE_MODE_TEXT: - ((CommonUIParams) mNextCmdParams).mIcon = (Bitmap) (data); - callStkApp(CommandType.SET_UP_IDLE_MODE_TEXT); - break; - case DISPLAY_TEXT: - ((DisplayTextParams) mNextCmdParams).icon = (Bitmap) (data); - callStkApp(CommandType.DISPLAY_TEXT); - break; - case SELECT_ITEM: - - SelectItemParams params = ((SelectItemParams) mNextCmdParams); - Menu menu = params.mMenu; - switch(params.mIconLoadState) { - case SelectItemParams.LOAD_TITLE_ICON: - menu.titleIcon = (Bitmap) data; - break; - case SelectItemParams.LOAD_ITEMS_ICONS: - icons = (Bitmap[]) data; - // set each item icon. - for (Item item : menu.items) { - item.icon = icons[iconIndex++]; - } - break; - case SelectItemParams.LOAD_TITLE_ITEMS_ICONS: - icons = (Bitmap[]) data; - // set title icon - menu.titleIcon = icons[iconIndex++]; - // set each item icon. - for (Item item : menu.items) { - item.icon = icons[iconIndex++]; - } - } - callStkApp(CommandType.SELECT_ITEM); - break; - default: - // This should never be reached! - throw new AssertionError("Add case statements for the newly added " - + "command types!"); - } - } - - private void callStkApp(CommandType cmdType) { - boolean needsResponse = false; - mCmdParams = mNextCmdParams; - - synchronized (mCmdListenerLock) { - switch (cmdType) { - case SET_UP_IDLE_MODE_TEXT: - if (mNm == null) { - break; - } - CommonUIParams i = (CommonUIParams) mCmdParams; - if (i.mText == null) { - mNm.cancel(STK_NOTIFICATION_ID); - } else { - Notification notification = new Notification(); - RemoteViews contentView = new RemoteViews( - mContext.getPackageName(), - com.android.internal.R.layout.status_bar_latest_event_content); - - // Set text and icon for the status bar. - notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit; - notification.tickerText = i.mText; - notification.flags |= Notification.FLAG_NO_CLEAR; - - // Set text and icon for the notification body. - if (!i.mIconSelfExplanatory) { - contentView.setTextViewText( - com.android.internal.R.id.text, i.mText); - } - if (i.mIcon != null) { - contentView.setImageViewBitmap( - com.android.internal.R.id.icon, i.mIcon); - } else { - contentView - .setImageViewResource( - com.android.internal.R.id.icon, - com.android.internal.R.drawable.stat_notify_sim_toolkit); + case GET_INPUT: + case GET_INKEY: + Input input = mCurrntCmd.geInput(); + if (!input.yesNo) { + // when help is requested there is no need to send the text + // string object. + if (!helpRequired) { + resp = new GetInkeyInputResponseData(resMsg.usersInput, + input.ucs2, input.packed); } - notification.contentView = contentView; - - mNm.notify(STK_NOTIFICATION_ID, notification); + } else { + resp = new GetInkeyInputResponseData( + resMsg.usersYesNoSelection); } - case SET_UP_MENU: - needsResponse = true; - break; - case SELECT_ITEM: - mState = State.SELECT_ITEM; - SelectItemParams s = (SelectItemParams) mCmdParams; - mCmdListener.onSelectItem(s.mMenu, s.mPresentationType); - needsResponse = false; break; case DISPLAY_TEXT: - mState = State.DISPLAY_TEXT; - DisplayTextParams d = (DisplayTextParams) mCmdParams; - mCmdListener.onDisplayText(d.text, d.textAttrs, d.isHighPriority, - d.userClear, !d.immediateResponse, d.icon); - - needsResponse = d.immediateResponse; - break; - default: - // This should never be reached! - throw new AssertionError( - "Add case statements for the newly added " - + "command types!"); - } - } - - if (needsResponse) { - sendTerminalResponse(mCmdParams.cmdDet, ResultCode.OK, false, 0, null); - } - } - - private void sendTerminalResponse(CtlvCommandDetails cmdDet, - ResultCode resultCode, boolean includeAdditionalInfo, - int additionalInfo, ResponseData resp) { - - if (cmdDet == null) { - return; - } - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - - // command details - int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); - if (cmdDet.compRequired) { - tag |= 0x80; - } - buf.write(tag); - buf.write(0x03); // length - buf.write(cmdDet.commandNumber); - buf.write(cmdDet.typeOfCommand); - buf.write(cmdDet.commandQualifier); - - // device identities - tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); - buf.write(tag); - buf.write(0x02); // length - buf.write(DEV_ID_TERMINAL); // source device id - buf.write(DEV_ID_UICC); // destination device id - - // result - tag = 0x80 | ComprehensionTlvTag.RESULT.value(); - buf.write(tag); - int length = includeAdditionalInfo ? 2 : 1; - buf.write(length); - buf.write(resultCode.code()); - - // additional info - if (includeAdditionalInfo) { - buf.write(additionalInfo); - } - - // Fill optional data for each corresponding command - if (resp != null) { - resp.format(buf); - } - - byte[] rawData = buf.toByteArray(); - String hexString = SimUtils.bytesToHexString(rawData); - if (Config.LOGD) { - Log.d(TAG, "TERMINAL RESPONSE: " + hexString); - } - - mCmdIf.sendTerminalResponse(hexString, null); - } - - /** - * Search for a COMPREHENSION-TLV object with the given tag from a list - * - * @param tag A tag to search for - * @param ctlvs List of ComprehensionTlv objects used to search in - * - * @return A ComprehensionTlv object that has the tag value of {@code tag}. - * If no object is found with the tag, null is returned. - */ - private ComprehensionTlv searchForTag(ComprehensionTlvTag tag, - List<ComprehensionTlv> ctlvs) { - Iterator<ComprehensionTlv> iter = ctlvs.iterator(); - return searchForNextTag(tag, iter); - } - - /** - * Search for the next COMPREHENSION-TLV object with the given tag from a - * list iterated by {@code iter}. {@code iter} points to the object next to - * the found object when this method returns. Used for searching the same - * list for similar tags, usually item id. - * - * @param tag A tag to search for - * @param iter Iterator for ComprehensionTlv objects used for search - * - * @return A ComprehensionTlv object that has the tag value of {@code tag}. - * If no object is found with the tag, null is returned. - */ - private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag, - Iterator<ComprehensionTlv> iter) { - int tagValue = tag.value(); - while (iter.hasNext()) { - ComprehensionTlv ctlv = iter.next(); - if (ctlv.getTag() == tagValue) { - return ctlv; - } - } - return null; - } - - /** - * Search for a Command Details object from a list. - * - * @param ctlvs List of ComprehensionTlv objects used for search - * @return An CtlvCommandDetails object found from the objects. If no - * Command Details object is found, ResultException is thrown. - * @throws ResultException - */ - private CtlvCommandDetails retrieveCommandDetails( - List<ComprehensionTlv> ctlvs) throws ResultException { - - ComprehensionTlv ctlv = searchForTag( - ComprehensionTlvTag.COMMAND_DETAILS, ctlvs); - if (ctlv != null) { - CtlvCommandDetails cmdDet = new CtlvCommandDetails(); - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - try { - cmdDet.compRequired = ctlv.isComprehensionRequired(); - cmdDet.commandNumber = rawValue[valueIndex] & 0xff; - cmdDet.typeOfCommand = rawValue[valueIndex + 1] & 0xff; - cmdDet.commandQualifier = rawValue[valueIndex + 2] & 0xff; - return cmdDet; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } else { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - } - - /** - * Search for a Device Identities object from a list. - * - * @param ctlvs List of ComprehensionTlv objects used for search - * @return An CtlvDeviceIdentities object found from the objects. If no - * Command Details object is found, ResultException is thrown. - * @throws ResultException - */ - private CtlvDeviceIdentities retrieveDeviceIdentities( - List<ComprehensionTlv> ctlvs) throws ResultException { - - ComprehensionTlv ctlv = searchForTag( - ComprehensionTlvTag.DEVICE_IDENTITIES, ctlvs); - if (ctlv != null) { - CtlvDeviceIdentities devIds = new CtlvDeviceIdentities(); - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - try { - devIds.sourceId = rawValue[valueIndex] & 0xff; - devIds.destinationId = rawValue[valueIndex + 1] & 0xff; - return devIds; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - } else { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - } - - /** - * Processes SETUP_CALL proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - */ - private void processSetupCall(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) { - if (Config.LOGD) { - Log.d(TAG, "processSetupCall begins"); - } - - // User confirmation phase message. - String confirmMsg = null; - // Call set up phase message. - String callMsg = null; - List<TextAttribute> textAttrs = null; - Iterator<ComprehensionTlv> iter = ctlvs.iterator(); - ComprehensionTlv ctlv = null; - - try { - // get confirmation message string. - ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); - if (ctlv != null) { - confirmMsg = retrieveAlphaId(ctlv); - } else { - // No message to show. - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - // get call set up message string. - ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); - if (ctlv != null) { - callMsg = retrieveAlphaId(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - textAttrs = retrieveTextAttribute(ctlv); - } - } catch (ResultException e) { - // Unable to process command. Send terminal response when service is - // in call state. - while (mState != State.CALL_SETUP) { - Thread.yield(); - } - sendTerminalResponse(cmdDet, ResultCode.REQUIRED_VALUES_MISSING, - false, 0, null); - return; - } - - synchronized (mCmdListenerLock) { - if (mCmdListener != null) { - mCmdListener.onCallSetup(confirmMsg, textAttrs, callMsg); - } - } - } - - /** - * Processes DISPLAY_TEXT proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processDisplayText(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processDisplayText begins"); - } - - DisplayTextParams params = new DisplayTextParams(cmdDet); - IconId iconId = null; - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, - ctlvs); - if (ctlv != null) { - params.text = retrieveTextString(ctlv); - } - // If the tlv object doesn't exist or the it is a null object reply - // with command not understood. - if (params.text == null) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - params.textAttrs = retrieveTextAttribute(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs); - if (ctlv != null) { - params.immediateResponse = true; - } - - ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); - if (ctlv != null) { - iconId = retrieveIconId(ctlv); - } - - // Parse command qualifier parameters. - params.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0; - params.userClear = (cmdDet.commandQualifier & 0x80) != 0; - - mNextCmdParams = params; - - // If there's no icon to load call stk application. - if (iconId != null) { - mIconLoader.loadIcon(iconId.recordNumber, this - .obtainMessage(EVENT_LOAD_ICON_DONE)); - return true; - } - return false; - } - - /** - * Processes SET_UP_MENU proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs Iterator for ComprehensionTlv objects following Command - * Details object and Device Identities object within the proactive - * command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processSetUpMenu(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processSetUpMenu begins"); - } - - Menu menu = new Menu(); - boolean first = true; - boolean removeExistingMenu = false; - Iterator<ComprehensionTlv> iter = ctlvs.iterator(); - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, - ctlvs); - if (ctlv != null) { - menu.title = retrieveAlphaId(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - menu.titleAttrs = retrieveTextAttribute(ctlv); - } - - while (true) { - ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter); - if (ctlv != null) { - Item item = retrieveItem(ctlv); - // If the first item is a "null" object, it means that - // the existing menu should be removed. - if (first && item == null) { - removeExistingMenu = true; - break; - } - menu.items.add(retrieveItem(ctlv)); - first = false; - } else { - break; - } - } - - // Extract command details. - menu.softKeyPreferred = (cmdDet.commandQualifier & 0x01) != 0; - menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; - - // We must have at least one menu item. - if (menu.items.size() == 0 && !removeExistingMenu) { - if (Config.LOGD) { - Log.d(TAG, "processSetUpMenu: Need at least one menu item"); - } - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - mCmdParams = new CommandParams(cmdDet); - if (removeExistingMenu) { - mState = State.IDLE; - mMainMenu = null; - setAppState(false); - } else { - Menu currentMenu = mMainMenu; - mState = State.MAIN_MENU; - mMainMenu = menu; - if (!isStkSupported()) { - setAppState(true); - setAppIndication(APP_INDICATOR_INSTALLED_NORMAL); - mInstallIndicator = APP_INDICATOR_INSTALLED_NORMAL; - } - } - return true; - } - - private void setAppState(boolean installed) { - if (installed) { - StkAppInstaller.installApp(mContext); - } else { - setAppIndication(APP_INDICATOR_UNINSTALLED); - mInstallIndicator = APP_INDICATOR_UNINSTALLED; - StkAppInstaller.unInstallApp(mContext); - } - } - - public Menu getCurrentMenu() { - Menu menu = null; - switch(mState) { - case MAIN_MENU: - menu = mMainMenu; - break; - case SELECT_ITEM: - menu = ((SelectItemParams) mCmdParams).mMenu; - break; - } - return menu; - } - - /** - * Processes SET_UP_IDLE_MODE_TEXT proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processSetUpIdleModeText(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processSetUpIdleModeText begins"); - } - - if (mNm == null) { - throw new ResultException(ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS); - } - - String text = null; - IconId iconId = null; - List<TextAttribute> textAttrs = null; - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, - ctlvs); - if (ctlv != null) { - text = retrieveTextString(ctlv); - } else { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - - ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); - if (ctlv != null) { - iconId = retrieveIconId(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - textAttrs = retrieveTextAttribute(ctlv); - } - - CommonUIParams params = new CommonUIParams(cmdDet, text, null); - mNextCmdParams = params; - if (iconId != null) { - params.mIconSelfExplanatory = iconId.selfExplanatory; - mIconLoader.loadIcon(iconId.recordNumber, this - .obtainMessage(EVENT_LOAD_ICON_DONE)); - return true; - } - return false; - } - - /** - * Processes GET_INKEY proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processGetInkey(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processGetInkey begins"); - } - - String text = null; - List<TextAttribute> textAttrs = null; - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, - ctlvs); - if (ctlv != null) { - text = retrieveTextString(ctlv); - } else { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - textAttrs = retrieveTextAttribute(ctlv); - } - - boolean digitOnly = (cmdDet.commandQualifier & 0x01) == 0; - boolean ucs2 = (cmdDet.commandQualifier & 0x02) != 0; - boolean yesNo = (cmdDet.commandQualifier & 0x04) != 0; - boolean immediateResponse = (cmdDet.commandQualifier & 0x08) != 0; - boolean helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; - - synchronized (mCmdListenerLock) { - if (mCmdListener != null) { - mCmdParams = new GetInkeyParams(cmdDet, yesNo, ucs2); - mState = State.GET_INKEY; - - mCmdListener.onGetInkey(text, textAttrs, yesNo, digitOnly, - ucs2, immediateResponse, helpAvailable); - return true; - } else { - // '0' means "No specific cause can be given" - throw new ResultException( - ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, 0); - } - } - } - - /** - * Processes GET_INPUT proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processGetInput(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processGetInput begins"); - } - - String text = null; - String defaultText = null; - int minLen, maxLen; - List<TextAttribute> textAttrs = null; - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, - ctlvs); - if (ctlv != null) { - text = retrieveTextString(ctlv); - } else { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - - ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs); - if (ctlv != null) { - try { - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - minLen = rawValue[valueIndex] & 0xff; - maxLen = rawValue[valueIndex + 1] & 0xff; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } else { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - textAttrs = retrieveTextAttribute(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs); - if (ctlv != null) { - defaultText = retrieveTextString(ctlv); - } - - boolean digitOnly = (cmdDet.commandQualifier & 0x01) == 0; - boolean ucs2 = (cmdDet.commandQualifier & 0x02) != 0; - boolean echo = (cmdDet.commandQualifier & 0x04) == 0; - boolean packed = (cmdDet.commandQualifier & 0x08) != 0; - boolean helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; - - synchronized (mCmdListenerLock) { - if (mCmdListener != null) { - mCmdParams = new GetInputParams(cmdDet, ucs2, packed); - mState = State.GET_INPUT; - - boolean noMaxLimit = maxLen == 0xff; - mCmdListener.onGetInput(text, defaultText, minLen, maxLen, - noMaxLimit, textAttrs, digitOnly, ucs2, echo, - helpAvailable); - return true; - } else { - // '0' means "No specific cause can be given" - throw new ResultException( - ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, 0); - } - } - } - - /** - * Processes REFRESH proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @throws ResultException - */ - private void processRefresh(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processRefresh begins"); - } - - // REFRESH proactive command is rerouted by the baseband and handled by - // the telephony layer. IDLE TEXT should be removed for a REFRESH command - // with "initialization" or "reset" - - if (mNm == null) { - throw new ResultException(ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS); - } - - boolean removeIdleText = false; - - switch (cmdDet.commandQualifier) { - case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE: - case REFRESH_NAA_INIT_AND_FILE_CHANGE: - case REFRESH_NAA_INIT: - case REFRESH_UICC_RESET: - removeIdleText = true; - } - if (removeIdleText) { - mNm.cancel(STK_NOTIFICATION_ID); - } - } - - /** - * Processes SELECT_ITEM proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processSelectItem(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processSelectItem begins"); - } - - Menu menu = new Menu(); - IconId titleIconId = null; - ItemsIconId itemsIconId = null; - int iconLoadState = SelectItemParams.LOAD_NO_ICON; - PresentationType presentType = PresentationType.NOT_SPECIFIED; - Iterator<ComprehensionTlv> iter = ctlvs.iterator(); - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, - ctlvs); - if (ctlv != null) { - menu.title = retrieveAlphaId(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - menu.titleAttrs = retrieveTextAttribute(ctlv); - } - - while (true) { - ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter); - if (ctlv != null) { - menu.items.add(retrieveItem(ctlv)); - } else { + case LAUNCH_BROWSER: break; - } - } - - // We must have at least one menu item. - if (menu.items.size() == 0) { - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - - ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs); - if (ctlv != null) { - // STK items are listed 1...n while list start at 0, need to - // subtract one. - menu.defaultItem = retrieveItemId(ctlv) - 1; - } - - ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); - if (ctlv != null) { - iconLoadState = SelectItemParams.LOAD_TITLE_ICON; - titleIconId = retrieveIconId(ctlv); - menu.titleIconSelfExplanatory = titleIconId.selfExplanatory; - } - - ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs); - if (ctlv != null) { - if (iconLoadState == SelectItemParams.LOAD_TITLE_ICON) { - iconLoadState = SelectItemParams.LOAD_TITLE_ITEMS_ICONS; - } else { - iconLoadState = SelectItemParams.LOAD_ITEMS_ICONS; - } - itemsIconId = retrieveItemsIconId(ctlv); - menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory; - } - - boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0; - if (presentTypeSpecified) { - if ((cmdDet.commandQualifier & 0x02) == 0) { - presentType = PresentationType.DATA_VALUES; - } else { - presentType = PresentationType.NAVIGATION_OPTIONS; - } - } - menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0; - menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; - - mNextCmdParams = new SelectItemParams(cmdDet, menu, presentType, - iconLoadState); - - // Load icons data if needed. - switch(iconLoadState) { - case SelectItemParams.LOAD_NO_ICON: - return false; - case SelectItemParams.LOAD_TITLE_ICON: - mIconLoader.loadIcon(titleIconId.recordNumber, this - .obtainMessage(EVENT_LOAD_ICON_DONE)); - break; - case SelectItemParams.LOAD_ITEMS_ICONS: - mIconLoader.loadIcons(itemsIconId.recordNumbers, this - .obtainMessage(EVENT_LOAD_ICON_DONE)); - break; - case SelectItemParams.LOAD_TITLE_ITEMS_ICONS: - // Create a new array for all the icons (title and items). - int[] recordNumbers = new int[itemsIconId.recordNumbers.length + 1]; - recordNumbers[0] = titleIconId.recordNumber; - System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers, 1, - itemsIconId.recordNumbers.length); - mIconLoader.loadIcons(recordNumbers, this - .obtainMessage(EVENT_LOAD_ICON_DONE)); - break; - } - return true; - } - - /** - * Processes EVENT_NOTIFY message from baseband. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - */ - synchronized private void processEventNotify(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) { - - if (Config.LOGD) { - Log.d(TAG, "processEventNotify begins"); - } - - String text = null; - List<TextAttribute> textAttrs = null; - - try { - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, - ctlvs); - if (ctlv != null) { - text = retrieveAlphaId(ctlv); - } else { - // No message to show. - throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - textAttrs = retrieveTextAttribute(ctlv); - } - } catch (ResultException e) { - // Unable to process command. - return; - } - - Toast toast = Toast.makeText(mContext.getApplicationContext(), text, - Toast.LENGTH_LONG); - toast.setGravity(Gravity.BOTTOM, 0, 0); - toast.show(); - } - - /** - * Processes SET_UP_EVENT_LIST proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - */ - private boolean processSetUpEventList(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) { - - if (Config.LOGD) { - Log.d(TAG, "processSetUpEventList begins"); - } - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, - ctlvs); - if (ctlv != null) { - try { - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int valueLen = ctlv.getLength(); - - } catch (IndexOutOfBoundsException e) {} - } - return true; - } - - /** - * Processes LAUNCH_BROWSER proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required. - * @throws ResultException - */ - private boolean processLaunchBrowser(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processLaunchBrowser begins"); - } - - String url = null; - String confirmMsg = null; - List<TextAttribute> confirmMsgAttrs = null; - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs); - if (ctlv != null) { - try { - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int valueLen = ctlv.getLength(); - if (valueLen > 0) { - url = GsmAlphabet.gsm8BitUnpackedToString(rawValue, - valueIndex, valueLen); - } else { - url = null; + case SET_UP_CALL: + mCmdIf.handleCallSetupRequestFromSim(resMsg.usersConfirm, null); + if (!resMsg.usersConfirm) { + resMsg.resCode = ResultCode.USER_NOT_ACCEPT; } - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } - } - - ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); - if (ctlv != null) { - confirmMsg = retrieveAlphaId(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - confirmMsgAttrs = retrieveTextAttribute(ctlv); - } - - LaunchBrowserMode mode; - switch (cmdDet.commandQualifier) { - case 0x00: - default: - mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED; - break; - case 0x02: - mode = LaunchBrowserMode.USE_EXISTING_BROWSER; break; - case 0x03: - mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER; + case NO_RESPONSE_FROM_USER: + case UICC_SESSION_TERM_BY_USER: + case BACKWARD_MOVE_BY_USER: + resp = null; break; - } - - synchronized (mCmdListenerLock) { - if (mCmdListener != null) { - mCmdParams = new CommandParams(cmdDet); - mState = State.LAUNCH_BROWSER; - - mCmdListener.onLaunchBrowser(url, confirmMsg, confirmMsgAttrs, - mode); - return true; - } else { - // '0' means "No specific cause can be given" - throw new ResultException( - ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, 0); - } - } - } - - /** - * Processes PLAY_TONE proactive command from the SIM card. - * - * @param cmdDet Command Details object retrieved from the proactive command - * object - * @param devIds Device Identities object retrieved from the proactive - * command object - * @param ctlvs List of ComprehensionTlv objects following Command Details - * object and Device Identities object within the proactive command - * @return true if the command is processing is pending and additional - * asynchronous processing is required.t - * @throws ResultException - */ - private boolean processPlayTone(CtlvCommandDetails cmdDet, - CtlvDeviceIdentities devIds, List<ComprehensionTlv> ctlvs) - throws ResultException { - - if (Config.LOGD) { - Log.d(TAG, "processPlayTone begins"); - } - - Tone tone = null; - String text = null; - List<TextAttribute> textAttrs = null; - Duration duration = null; - - ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs); - if (ctlv != null) { - // Nothing to do for null objects. - if (ctlv.getLength() > 0) { - try { - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int toneVal = rawValue[valueIndex]; - tone = Tone.fromInt(toneVal); - } catch (IndexOutOfBoundsException e) { - throw new ResultException( - ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } - } - ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); - if (ctlv != null) { - text = retrieveAlphaId(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.TEXT_ATTRIBUTE, ctlvs); - if (ctlv != null) { - textAttrs = retrieveTextAttribute(ctlv); - } - - ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); - if (ctlv != null) { - duration = retrieveDuration(ctlv); - } - - synchronized (mCmdListenerLock) { - if (mCmdListener != null) { - mState = State.PLAY_TONE; - mCmdParams = new CommandParams(cmdDet); - mCmdListener.onPlayTone(tone, text, textAttrs, duration); - return true; - } else { - // '0' means "No specific cause can be given" - throw new ResultException( - ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, 0); - } - } - } - - /** - * Retrieves text from the Text COMPREHENSION-TLV object, and decodes it - * into a {@link java.lang.String}. - * - * @param ctlv A Text COMPREHENSION-TLV object - * @return A {@link java.lang.String} object decoded from the Text object - * @throws ResultException - */ - private String retrieveTextString(ComprehensionTlv ctlv) - throws ResultException { - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - byte codingScheme = 0x00; - String text = null; - int textLen = ctlv.getLength(); - - // In case the text length is 0, return a null string. - if (textLen == 0) { - return text; - } else { - // one byte is coding scheme - textLen -= 1; - } - - try { - codingScheme = (byte) (rawValue[valueIndex] & 0x0c); - - if (codingScheme == 0x00) { // GSM 7-bit packed - text = GsmAlphabet.gsm7BitPackedToString(rawValue, - valueIndex + 1, (textLen * 8) / 7); - } else if (codingScheme == 0x04) { // GSM 8-bit unpacked - text = GsmAlphabet.gsm8BitUnpackedToString(rawValue, - valueIndex + 1, textLen); - } else if (codingScheme == 0x08) { // UCS2 - text = new String(rawValue, valueIndex + 1, textLen, "UTF-16"); - } else { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - - return text; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } catch (UnsupportedEncodingException e) { - // This should never happen. - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } - - /** - * Retrieves Duration information from the Duration COMPREHENSION-TLV - * object. - * - * @param ctlv A Text Attribute COMPREHENSION-TLV object - * @return A Duration object - * @throws ResultException - */ - private Duration retrieveDuration(ComprehensionTlv ctlv) - throws ResultException { - int timeInterval = 0; - TimeUnit timeUnit = TimeUnit.SECOND; - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - - try { - timeUnit = TimeUnit.values()[(rawValue[valueIndex] & 0xff)]; - timeInterval = rawValue[valueIndex + 1] & 0xff; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - return new Duration(timeInterval, timeUnit); - } - - /** - * Retrieves Item information from the COMPREHENSION-TLV object. - * - * @param ctlv A Text Attribute COMPREHENSION-TLV object - * @return An Item - * @throws ResultException - */ - private Item retrieveItem(ComprehensionTlv ctlv) throws ResultException { - Item item = null; - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int length = ctlv.getLength(); - - if (length != 0) { - int textLen = length - 1; - - try { - int id = rawValue[valueIndex] & 0xff; - String text = SimUtils.adnStringFieldToString(rawValue, - valueIndex + 1, textLen); - item = new Item(id, text); - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } - - return item; - } - - /** - * Retrieves Item id information from the COMPREHENSION-TLV object. - * - * @param ctlv A Text Attribute COMPREHENSION-TLV object - * @return An Item id - * @throws ResultException - */ - private int retrieveItemId(ComprehensionTlv ctlv) throws ResultException { - int id = 0; - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - - try { - id = rawValue[valueIndex] & 0xff; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - - return id; - } - - /** - * Retrieves icon id from an Icon Identifier COMPREHENSION-TLV object - * - * @param ctlv An Icon Identifier COMPREHENSION-TLV object - * @return IconId instance - * @throws ResultException - */ - private IconId retrieveIconId(ComprehensionTlv ctlv) throws ResultException { - IconId id = new IconId(); - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - try { - id.selfExplanatory = (rawValue[valueIndex++] & 0xff) == 0x00; - id.recordNumber = rawValue[valueIndex] & 0xff; - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - - return id; - } - - /** - * Retrieves item icons id from an Icon Identifier List COMPREHENSION-TLV object - * - * @param ctlv An Item Icon List Identifier COMPREHENSION-TLV object - * @return ItemsIconId instance - * @throws ResultException - */ - private ItemsIconId retrieveItemsIconId(ComprehensionTlv ctlv) - throws ResultException{ - Log.d(TAG, "retrieveIconIdList:"); - ItemsIconId id = new ItemsIconId(); - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int numOfItems = ctlv.getLength() - 1; - id.recordNumbers = new int[numOfItems]; - - try { - // get icon self-explanatory - id.selfExplanatory = (rawValue[valueIndex++] & 0xff) == 0x00; - - for (int index = 0; index < numOfItems;) { - id.recordNumbers[index++] = rawValue[valueIndex++]; - } - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - return id; - } - - /** - * Retrieves text attribute information from the Text Attribute - * COMPREHENSION-TLV object. - * - * @param ctlv A Text Attribute COMPREHENSION-TLV object - * @return A list of TextAttribute objects - * @throws ResultException - */ - private List<TextAttribute> retrieveTextAttribute(ComprehensionTlv ctlv) - throws ResultException { - ArrayList<TextAttribute> lst = new ArrayList<TextAttribute>(); - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int length = ctlv.getLength(); - - if (length != 0) { - // Each attribute is consisted of four bytes - int itemCount = length / 4; - - try { - for (int i = 0; i < itemCount; i++, valueIndex += 4) { - int start = rawValue[valueIndex] & 0xff; - int textLength = rawValue[valueIndex + 1] & 0xff; - int format = rawValue[valueIndex + 2] & 0xff; - int colorValue = rawValue[valueIndex + 3] & 0xff; - - int alignValue = format & 0x03; - TextAlignment align = TextAlignment.fromInt(alignValue); - - int sizeValue = (format >> 2) & 0x03; - FontSize size = FontSize.fromInt(sizeValue); - if (size == null) { - // Font size value is not defined. Use default. - size = FontSize.NORMAL; - } - - boolean bold = (format & 0x10) != 0; - boolean italic = (format & 0x20) != 0; - boolean underlined = (format & 0x40) != 0; - boolean strikeThrough = (format & 0x80) != 0; - - TextColor color = TextColor.fromInt(colorValue); - - TextAttribute attr = new TextAttribute(start, textLength, - align, size, bold, italic, underlined, - strikeThrough, color); - lst.add(attr); - } - - return lst; - - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } - return null; - } - - /** - * Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV - * object. - * - * @param ctlv An Alpha Identifier COMPREHENSION-TLV object - * @return String corresponding to the alpha identifier - * @throws ResultException - */ - private String retrieveAlphaId(ComprehensionTlv ctlv) - throws ResultException { - - byte[] rawValue = ctlv.getRawValue(); - int valueIndex = ctlv.getValueIndex(); - int length = ctlv.getLength(); - if (length != 0) { - try { - return SimUtils.adnStringFieldToString(rawValue, valueIndex, - length); - } catch (IndexOutOfBoundsException e) { - throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); - } - } - return null; - } - - /** - * Handles RIL_UNSOL_STK_EVENT_NOTIFY unsolicited command from RIL. - * - * @param data String containing SAT/USAT commands or responses sent by ME - * to SIM or commands handled by ME, in hexadecimal format starting - * with first byte of response data or command tag - */ - private void handleEventNotify(String data) { - if (Config.LOGD) { - Log.d(TAG, "handleEventNotify begins"); - } - byte[] rawData = null; - BerTlv berTlv = null; - CtlvCommandDetails cmdDet = null; - CtlvDeviceIdentities devIds = null; - CommandType cmdType = null; - - // Nothing to do for empty strings. - if (data.length() == 0) { - return; - } - rawData = SimUtils.hexStringToBytes(data); - try { - berTlv = BerTlv.decode(rawData); - } catch (ResultException e) { - // Can't parse command buffer. - return; - } - - // Extract command details & Device identities tlv objects list. - List<ComprehensionTlv> ctlvs = berTlv.getComprehensionTlvs(); - try { - cmdDet = retrieveCommandDetails(ctlvs); - devIds = retrieveDeviceIdentities(ctlvs); - } catch (ResultException e) { - if (Config.LOGD) { - Log.d(TAG, "invlaid command details/device identities"); - } - return; - } - - // Check to see if we support this command. - cmdType = CommandType.fromInt(cmdDet.typeOfCommand); - if (cmdType == null) { + default: return; } - - // There are two scenarios for EVENT_NOTIFY messages: - // 1. A proactive command which is partially handled by the baseband and - // requires UI processing from the application. This messages will be - // tagged with PROACTIVE COMMAND tag. - // 2. A notification for an action completed by the baseband. This - // messages will be tagged with UNKNOWN tag and the command type inside - // the Command details object should indicate which action was completed. - if (berTlv.getTag() == BerTlv.BER_PROACTIVE_COMMAND_TAG) { - switch (cmdType) { - case SEND_SS: - case SEND_USSD: - case SEND_SMS: - case SEND_DTMF: - processEventNotify(cmdDet, devIds, ctlvs); - break; - case SET_UP_EVENT_LIST: - processSetUpEventList(cmdDet, devIds, ctlvs); - break; - case SET_UP_CALL: - processSetupCall(cmdDet, devIds, ctlvs); - break; - default: - // nada - break; - } - } + sendTerminalResponse(cmdDet, resMsg.resCode, false, 0, resp); + mCurrntCmd = null; } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkAppInstaller.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkAppInstaller.java deleted file mode 100644 index 07e3e56..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkAppInstaller.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm.stk; - -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Log; - -/** - * Application installer for SIM Toolkit. - * - */ -public class StkAppInstaller { - // Application state actions: install, uninstall used by StkAppStateReceiver. - static final String STK_APP_INSTALL_ACTION = "com.android.stk.action.INSTALL"; - static final String STK_APP_UNINSTALL_ACTION = "com.android.stk.action.UNINSTALL"; - - public static void installApp(Context context) { - setAppState(context, PackageManager.COMPONENT_ENABLED_STATE_ENABLED); - } - - public static void unInstallApp(Context context) { - setAppState(context, PackageManager.COMPONENT_ENABLED_STATE_DISABLED); - } - - private static void setAppState(Context context, int state) { - if (context == null) { - return; - } - PackageManager pm = context.getPackageManager(); - if (pm == null) { - return; - } - // check that STK app package is known to the PackageManager - ComponentName cName = new ComponentName("com.android.stk", - "com.android.stk.StkActivity"); - - try { - pm.setComponentEnabledSetting(cName, state, - PackageManager.DONT_KILL_APP); - } catch (Exception e) { - Log.w("StkAppInstaller", "Could not change STK app state"); - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkAppStateReceiver.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkAppStateReceiver.java deleted file mode 100644 index 778ca2e..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkAppStateReceiver.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm.stk; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import com.android.internal.telephony.gsm.stk.Service; -import android.util.Log; - -/** - * This class implements a Broadcast receiver. It waits for an intent sent by - * the STK service and install/uninstall the STK application. If no intent is - * received when the device finished booting, the application is then unistalled. - */ -public class StkAppStateReceiver extends BroadcastReceiver { - - private static final String TAG = "StkAppStateReceiver"; - - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "onReceive"); - - String action = intent.getAction(); - Service stkService = Service.getInstance(); - if (stkService == null) { - return; - } - if (action.equals(StkAppInstaller.STK_APP_INSTALL_ACTION)) { - stkService.setAppIndication(Service.APP_INDICATOR_INSTALLED_NORMAL); - StkAppInstaller.installApp(context); - } else if (action.equals(StkAppInstaller.STK_APP_UNINSTALL_ACTION)) { - stkService.setAppIndication(Service.APP_INDICATOR_UNINSTALLED); - StkAppInstaller.unInstallApp(context); - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java new file mode 100644 index 0000000..62a778e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Class used to pass STK messages from telephony to application. Application + * should call getXXX() to get commands's specific values. + * + */ +public class StkCmdMessage implements Parcelable { + // members + CommandDetails mCmdDet; + private TextMessage mTextMsg; + private Menu mMenu; + private Input mInput; + private BrowserSettings mBrowserSettings = null; + private ToneSettings mToneSettings = null; + private CallSettings mCallSettings = null; + + /* + * Container for Launch Browser command settings. + */ + public class BrowserSettings { + public String url; + public LaunchBrowserMode mode; + } + + /* + * Container for Call Setup command settings. + */ + public class CallSettings { + public TextMessage confirmMsg; + public TextMessage callMsg; + } + + StkCmdMessage(CommandParams cmdParams) { + mCmdDet = cmdParams.cmdDet; + switch(getCmdType()) { + case SET_UP_MENU: + case SELECT_ITEM: + mMenu = ((SelectItemParams) cmdParams).menu; + break; + case DISPLAY_TEXT: + case SET_UP_IDLE_MODE_TEXT: + case SEND_DTMF: + case SEND_SMS: + case SEND_SS: + case SEND_USSD: + mTextMsg = ((DisplayTextParams) cmdParams).textMsg; + break; + case GET_INPUT: + case GET_INKEY: + mInput = ((GetInputParams) cmdParams).input; + break; + case LAUNCH_BROWSER: + mTextMsg = ((LaunchBrowserParams) cmdParams).confirmMsg; + mBrowserSettings = new BrowserSettings(); + mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).url; + mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mode; + break; + case PLAY_TONE: + PlayToneParams params = (PlayToneParams) cmdParams; + mToneSettings = params.settings; + mTextMsg = params.textMsg; + break; + case SET_UP_CALL: + mCallSettings = new CallSettings(); + mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).confirmMsg; + mCallSettings.callMsg = ((CallSetupParams) cmdParams).callMsg; + break; + } + } + + public StkCmdMessage(Parcel in) { + mCmdDet = in.readParcelable(null); + mTextMsg = in.readParcelable(null); + mMenu = in.readParcelable(null); + mInput = in.readParcelable(null); + switch (getCmdType()) { + case LAUNCH_BROWSER: + mBrowserSettings = new BrowserSettings(); + mBrowserSettings.url = in.readString(); + mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()]; + break; + case PLAY_TONE: + mToneSettings = in.readParcelable(null); + break; + case SET_UP_CALL: + mCallSettings = new CallSettings(); + mCallSettings.confirmMsg = in.readParcelable(null); + mCallSettings.callMsg = in.readParcelable(null); + break; + } + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mCmdDet, 0); + dest.writeParcelable(mTextMsg, 0); + dest.writeParcelable(mMenu, 0); + dest.writeParcelable(mInput, 0); + switch(getCmdType()) { + case LAUNCH_BROWSER: + dest.writeString(mBrowserSettings.url); + dest.writeInt(mBrowserSettings.mode.ordinal()); + break; + case PLAY_TONE: + dest.writeParcelable(mToneSettings, 0); + break; + case SET_UP_CALL: + dest.writeParcelable(mCallSettings.confirmMsg, 0); + dest.writeParcelable(mCallSettings.callMsg, 0); + break; + } + } + + public static final Parcelable.Creator<StkCmdMessage> CREATOR = new Parcelable.Creator<StkCmdMessage>() { + public StkCmdMessage createFromParcel(Parcel in) { + return new StkCmdMessage(in); + } + + public StkCmdMessage[] newArray(int size) { + return new StkCmdMessage[size]; + } + }; + + public int describeContents() { + return 0; + } + + /* external API to be used by application */ + public AppInterface.CommandType getCmdType() { + return AppInterface.CommandType.fromInt(mCmdDet.typeOfCommand); + } + + public Menu getMenu() { + return mMenu; + } + + public Input geInput() { + return mInput; + } + + public TextMessage geTextMessage() { + return mTextMsg; + } + + public BrowserSettings getBrowserSettings() { + return mBrowserSettings; + } + + public ToneSettings getToneSettings() { + return mToneSettings; + } + + public CallSettings getCallSettings() { + return mCallSettings; + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CtlvCommandDetails.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java index 6389537..f6e5685 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CtlvCommandDetails.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,26 @@ package com.android.internal.telephony.gsm.stk; +import android.util.Log; -/** - * Class for Command Detailes object of proactive commands from SIM. - * {@hide} - */ -public class CtlvCommandDetails { - public boolean compRequired; - public int commandNumber; - public int typeOfCommand; - public int commandQualifier; +public abstract class StkLog { + static final boolean DEBUG = true; + + public static void d(Object caller, String msg) { + if (!DEBUG) { + return; + } + + String className = caller.getClass().getName(); + Log.d("STK", className.substring(className.lastIndexOf('.') + 1) + ": " + + msg); + } + + public static void d(String caller, String msg) { + if (!DEBUG) { + return; + } + + Log.d("STK", caller + ": " + msg); + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java new file mode 100644 index 0000000..04a52e6 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +public class StkResponseMessage { + CommandDetails cmdDet = null; + ResultCode resCode = ResultCode.OK; + int usersMenuSelection = 0; + String usersInput = null; + boolean usersYesNoSelection = false; + boolean usersConfirm = false; + + public StkResponseMessage(StkCmdMessage cmdMsg) { + this.cmdDet = cmdMsg.mCmdDet; + } + + public void setResultCode(ResultCode resCode) { + this.resCode = resCode; + } + + public void setMenuSelection(int selection) { + this.usersMenuSelection = selection; + } + + public void setInput(String input) { + this.usersInput = input; + } + + public void setYesNo(boolean yesNo) { + usersYesNoSelection = yesNo; + } + + public void setConfirmation(boolean confirm) { + usersConfirm = confirm; + } + + CommandDetails getCmdDetails() { + return cmdDet; + } + }
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java b/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java new file mode 100644 index 0000000..3b6a09a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; + +public class TextMessage implements Parcelable { + public String title = ""; + public String text = null; + public Bitmap icon = null; + public boolean iconSelfExplanatory = false; + public boolean isHighPriority = false; + public boolean responseNeeded = true; + public boolean userClear = false; + public Duration duration = null; + + TextMessage() { + } + + private TextMessage(Parcel in) { + title = in.readString(); + text = in.readString(); + icon = in.readParcelable(null); + iconSelfExplanatory = in.readInt() == 1 ? true : false; + isHighPriority = in.readInt() == 1 ? true : false; + responseNeeded = in.readInt() == 1 ? true : false; + userClear = in.readInt() == 1 ? true : false; + duration = in.readParcelable(null); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(title); + dest.writeString(text); + dest.writeParcelable(icon, 0); + dest.writeInt(iconSelfExplanatory ? 1 : 0); + dest.writeInt(isHighPriority ? 1 : 0); + dest.writeInt(responseNeeded ? 1 : 0); + dest.writeInt(userClear ? 1 : 0); + dest.writeParcelable(duration, 0); + } + + public static final Parcelable.Creator<TextMessage> CREATOR = new Parcelable.Creator<TextMessage>() { + public TextMessage createFromParcel(Parcel in) { + return new TextMessage(in); + } + + public TextMessage[] newArray(int size) { + return new TextMessage[size]; + } + }; +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java b/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java index c96f164..b64e777 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.gsm.stk; +import android.os.Parcel; +import android.os.Parcelable; /** * Enumeration for representing the tone values for use with PLAY TONE @@ -23,7 +25,7 @@ package com.android.internal.telephony.gsm.stk; * * {@hide} */ -public enum Tone { +public enum Tone implements Parcelable { // Standard supervisory tones /** @@ -163,4 +165,26 @@ public enum Tone { } return null; } + + Tone(Parcel in) { + mValue = in.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(ordinal()); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<Tone> CREATOR = new Parcelable.Creator<Tone>() { + public Tone createFromParcel(Parcel in) { + return Tone.values()[in.readInt()]; + } + + public Tone[] newArray(int size) { + return new Tone[size]; + } + }; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java b/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java new file mode 100644 index 0000000..bbc925e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Container class for PlayTone commands parameters. + * + */ +public class ToneSettings implements Parcelable { + public Duration duration; + public Tone tone; + public boolean vibrate; + + public ToneSettings(Duration duration, Tone tone, boolean vibrate) { + this.duration = duration; + this.tone = tone; + this.vibrate = vibrate; + } + + private ToneSettings(Parcel in) { + duration = in.readParcelable(null); + tone = in.readParcelable(null); + vibrate = in.readInt() == 1; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(duration, 0); + dest.writeParcelable(tone, 0); + dest.writeInt(vibrate ? 1 : 0); + } + + public static final Parcelable.Creator<ToneSettings> CREATOR = new Parcelable.Creator<ToneSettings>() { + public ToneSettings createFromParcel(Parcel in) { + return new ToneSettings(in); + } + + public ToneSettings[] newArray(int size) { + return new ToneSettings[size]; + } + }; +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java b/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java new file mode 100644 index 0000000..cd02a8b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2006-2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.internal.telephony.gsm.stk; + +import android.util.Log; + +import com.android.internal.telephony.gsm.GsmAlphabet; +import com.android.internal.telephony.gsm.SimUtils; +import com.android.internal.telephony.gsm.stk.Duration.TimeUnit; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +abstract class ValueParser { + + private static final String TAG = "ValueParser"; + + /** + * Search for a Command Details object from a list. + * + * @param ctlvs List of ComprehensionTlv objects used for search + * @return An CtlvCommandDetails object found from the objects. If no + * Command Details object is found, ResultException is thrown. + * @throws ResultException + */ + static CommandDetails retrieveCommandDetails(ComprehensionTlv ctlv) + throws ResultException { + + CommandDetails cmdDet = new CommandDetails(); + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + try { + cmdDet.compRequired = ctlv.isComprehensionRequired(); + cmdDet.commandNumber = rawValue[valueIndex] & 0xff; + cmdDet.typeOfCommand = rawValue[valueIndex + 1] & 0xff; + cmdDet.commandQualifier = rawValue[valueIndex + 2] & 0xff; + return cmdDet; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + + /** + * Search for a Device Identities object from a list. + * + * @param ctlvs List of ComprehensionTlv objects used for search + * @return An CtlvDeviceIdentities object found from the objects. If no + * Command Details object is found, ResultException is thrown. + * @throws ResultException + */ + static DeviceIdentities retrieveDeviceIdentities(ComprehensionTlv ctlv) + throws ResultException { + + DeviceIdentities devIds = new DeviceIdentities(); + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + try { + devIds.sourceId = rawValue[valueIndex] & 0xff; + devIds.destinationId = rawValue[valueIndex + 1] & 0xff; + return devIds; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); + } + } + + /** + * Retrieves Duration information from the Duration COMPREHENSION-TLV + * object. + * + * @param ctlv A Text Attribute COMPREHENSION-TLV object + * @return A Duration object + * @throws ResultException + */ + static Duration retrieveDuration(ComprehensionTlv ctlv) throws ResultException { + int timeInterval = 0; + TimeUnit timeUnit = TimeUnit.SECOND; + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + + try { + timeUnit = TimeUnit.values()[(rawValue[valueIndex] & 0xff)]; + timeInterval = rawValue[valueIndex + 1] & 0xff; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + return new Duration(timeInterval, timeUnit); + } + + /** + * Retrieves Item information from the COMPREHENSION-TLV object. + * + * @param ctlv A Text Attribute COMPREHENSION-TLV object + * @return An Item + * @throws ResultException + */ + static Item retrieveItem(ComprehensionTlv ctlv) throws ResultException { + Item item = null; + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int length = ctlv.getLength(); + + if (length != 0) { + int textLen = length - 1; + + try { + int id = rawValue[valueIndex] & 0xff; + String text = SimUtils.adnStringFieldToString(rawValue, + valueIndex + 1, textLen); + item = new Item(id, text); + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + + return item; + } + + /** + * Retrieves Item id information from the COMPREHENSION-TLV object. + * + * @param ctlv A Text Attribute COMPREHENSION-TLV object + * @return An Item id + * @throws ResultException + */ + static int retrieveItemId(ComprehensionTlv ctlv) throws ResultException { + int id = 0; + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + + try { + id = rawValue[valueIndex] & 0xff; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + + return id; + } + + /** + * Retrieves icon id from an Icon Identifier COMPREHENSION-TLV object + * + * @param ctlv An Icon Identifier COMPREHENSION-TLV object + * @return IconId instance + * @throws ResultException + */ + static IconId retrieveIconId(ComprehensionTlv ctlv) throws ResultException { + IconId id = new IconId(); + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + try { + id.selfExplanatory = (rawValue[valueIndex++] & 0xff) == 0x00; + id.recordNumber = rawValue[valueIndex] & 0xff; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + + return id; + } + + /** + * Retrieves item icons id from an Icon Identifier List COMPREHENSION-TLV + * object + * + * @param ctlv An Item Icon List Identifier COMPREHENSION-TLV object + * @return ItemsIconId instance + * @throws ResultException + */ + static ItemsIconId retrieveItemsIconId(ComprehensionTlv ctlv) + throws ResultException { + Log.d(TAG, "retrieveIconIdList:"); + ItemsIconId id = new ItemsIconId(); + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int numOfItems = ctlv.getLength() - 1; + id.recordNumbers = new int[numOfItems]; + + try { + // get icon self-explanatory + id.selfExplanatory = (rawValue[valueIndex++] & 0xff) == 0x00; + + for (int index = 0; index < numOfItems;) { + id.recordNumbers[index++] = rawValue[valueIndex++]; + } + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + return id; + } + + /** + * Retrieves text attribute information from the Text Attribute + * COMPREHENSION-TLV object. + * + * @param ctlv A Text Attribute COMPREHENSION-TLV object + * @return A list of TextAttribute objects + * @throws ResultException + */ + static List<TextAttribute> retrieveTextAttribute(ComprehensionTlv ctlv) + throws ResultException { + ArrayList<TextAttribute> lst = new ArrayList<TextAttribute>(); + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int length = ctlv.getLength(); + + if (length != 0) { + // Each attribute is consisted of four bytes + int itemCount = length / 4; + + try { + for (int i = 0; i < itemCount; i++, valueIndex += 4) { + int start = rawValue[valueIndex] & 0xff; + int textLength = rawValue[valueIndex + 1] & 0xff; + int format = rawValue[valueIndex + 2] & 0xff; + int colorValue = rawValue[valueIndex + 3] & 0xff; + + int alignValue = format & 0x03; + TextAlignment align = TextAlignment.fromInt(alignValue); + + int sizeValue = (format >> 2) & 0x03; + FontSize size = FontSize.fromInt(sizeValue); + if (size == null) { + // Font size value is not defined. Use default. + size = FontSize.NORMAL; + } + + boolean bold = (format & 0x10) != 0; + boolean italic = (format & 0x20) != 0; + boolean underlined = (format & 0x40) != 0; + boolean strikeThrough = (format & 0x80) != 0; + + TextColor color = TextColor.fromInt(colorValue); + + TextAttribute attr = new TextAttribute(start, textLength, + align, size, bold, italic, underlined, + strikeThrough, color); + lst.add(attr); + } + + return lst; + + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + return null; + } + + + /** + * Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV + * object. + * + * @param ctlv An Alpha Identifier COMPREHENSION-TLV object + * @return String corresponding to the alpha identifier + * @throws ResultException + */ + static String retrieveAlphaId(ComprehensionTlv ctlv) throws ResultException { + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int length = ctlv.getLength(); + if (length != 0) { + try { + return SimUtils.adnStringFieldToString(rawValue, valueIndex, + length); + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + return null; + } + + /** + * Retrieves text from the Text COMPREHENSION-TLV object, and decodes it + * into a Java String. + * + * @param ctlv A Text COMPREHENSION-TLV object + * @return A Java String object decoded from the Text object + * @throws ResultException + */ + static String retrieveTextString(ComprehensionTlv ctlv) throws ResultException { + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + byte codingScheme = 0x00; + String text = null; + int textLen = ctlv.getLength(); + + // In case the text length is 0, return a null string. + if (textLen == 0) { + return text; + } else { + // one byte is coding scheme + textLen -= 1; + } + + try { + codingScheme = (byte) (rawValue[valueIndex] & 0x0c); + + if (codingScheme == 0x00) { // GSM 7-bit packed + text = GsmAlphabet.gsm7BitPackedToString(rawValue, + valueIndex + 1, (textLen * 8) / 7); + } else if (codingScheme == 0x04) { // GSM 8-bit unpacked + text = GsmAlphabet.gsm8BitUnpackedToString(rawValue, + valueIndex + 1, textLen); + } else if (codingScheme == 0x08) { // UCS2 + text = new String(rawValue, valueIndex + 1, textLen, "UTF-16"); + } else { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + + return text; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } catch (UnsupportedEncodingException e) { + // This should never happen. + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } +} diff --git a/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java b/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java index 6af5d87..25d2026 100644 --- a/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java +++ b/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java @@ -16,17 +16,18 @@ package com.android.internal.telephony.test; -import com.android.internal.os.HandlerThread; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; import java.io.UnsupportedEncodingException; - -import android.os.*; -import android.util.Log; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; -import java.net.InetSocketAddress; import java.util.List; // Also in ATChannel.java @@ -131,7 +132,7 @@ class InterpreterEx extends Exception } public class ModelInterpreter - implements Runnable, HandlerInterface, SimulatedRadioControl + implements Runnable, SimulatedRadioControl { static final int MAX_CALLS = 6; @@ -151,7 +152,7 @@ public class ModelInterpreter SimulatedGsmCallState simulatedCallState; - HandlerThread handlerThread; + HandlerThread mHandlerThread; int pausedResponseCount; Object pausedResponseMonitor = new Object(); @@ -186,15 +187,10 @@ public class ModelInterpreter init() { new Thread(this, "ModelInterpreter").start(); - - handlerThread - = new HandlerThread(this, - new Runnable() { - public void run() { - simulatedCallState = new SimulatedGsmCallState(); - } - }, - "ModelInterpreter"); + mHandlerThread = new HandlerThread("ModelInterpreter"); + mHandlerThread.start(); + Looper looper = mHandlerThread.getLooper(); + simulatedCallState = new SimulatedGsmCallState(looper); } //***** Runnable Implementation @@ -273,14 +269,6 @@ public class ModelInterpreter } } - //***** HandlerInterface Implementation - - public void - handleMessage(Message msg) - { - - } - //***** Instance Methods @@ -695,7 +683,10 @@ public class ModelInterpreter public void shutdown() { - handlerThread.getHandler().getLooper().quit(); + Looper looper = mHandlerThread.getLooper(); + if (looper != null) { + looper.quit(); + } try { in.close(); diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index 47fac46..33c1679 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -16,27 +16,25 @@ package com.android.internal.telephony.test; -import com.android.internal.os.HandlerThread; + import android.os.AsyncResult; -import android.os.HandlerInterface; +import android.os.HandlerThread; +import android.os.Looper; import android.os.Message; -import com.android.internal.telephony.ATParseEx; +import android.util.Log; + import com.android.internal.telephony.Phone; import com.android.internal.telephony.gsm.BaseCommands; -import com.android.internal.telephony.gsm.BaseCommands; import com.android.internal.telephony.gsm.CallFailCause; import com.android.internal.telephony.gsm.CommandException; import com.android.internal.telephony.gsm.CommandsInterface; -import com.android.internal.telephony.gsm.DriverCall; import com.android.internal.telephony.gsm.PDPContextState; -import com.android.internal.telephony.gsm.stk.CtlvCommandDetails; -import com.android.internal.telephony.gsm.stk.ResultCode; import com.android.internal.telephony.gsm.SuppServiceNotification; -import android.util.Log; + import java.util.ArrayList; public final class SimulatedCommands extends BaseCommands - implements CommandsInterface, HandlerInterface, SimulatedRadioControl + implements CommandsInterface, SimulatedRadioControl { private final static String LOG_TAG = "SIM"; @@ -64,7 +62,7 @@ public final class SimulatedCommands extends BaseCommands //***** Instance Variables SimulatedGsmCallState simulatedCallState; - HandlerThread handlerThread; + HandlerThread mHandlerThread; SimLockState mSimLockedState; boolean mSimLockEnabled; int mPinUnlockAttempts; @@ -88,15 +86,12 @@ public final class SimulatedCommands extends BaseCommands public SimulatedCommands() { super(null); // Don't log statistics - handlerThread - = new HandlerThread(this, - new Runnable() { - public void run() { - simulatedCallState = new SimulatedGsmCallState(); - } - }, - "SimulatedCommands"); - + mHandlerThread = new HandlerThread("SimulatedCommands"); + mHandlerThread.start(); + Looper looper = mHandlerThread.getLooper(); + + simulatedCallState = new SimulatedGsmCallState(looper); + setRadioState(RadioState.RADIO_OFF); mSimLockedState = INITIAL_LOCK_STATE; mSimLockEnabled = (mSimLockedState != SimLockState.NONE); @@ -1274,7 +1269,10 @@ public final class SimulatedCommands extends BaseCommands shutdown() { setRadioState(RadioState.RADIO_UNAVAILABLE); - handlerThread.getHandler().getLooper().quit(); + Looper looper = mHandlerThread.getLooper(); + if (looper != null) { + looper.quit(); + } } /** hangup all */ @@ -1313,14 +1311,6 @@ public final class SimulatedCommands extends BaseCommands } } - //***** HandlerInterface implementation - - public void - handleMessage(Message msg) - { - - } - //***** Private Methods private void unimplemented(Message result) diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java b/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java index bbc2bba..340d788 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java @@ -16,6 +16,7 @@ package com.android.internal.telephony.test; +import android.os.Looper; import android.os.Message; import android.os.Handler; import android.telephony.PhoneNumberUtils; @@ -162,6 +163,10 @@ class SimulatedGsmCallState extends Handler //***** Overridden from Handler + public SimulatedGsmCallState(Looper looper) { + super(looper); + } + public void handleMessage(Message msg) { @@ -176,7 +181,8 @@ class SimulatedGsmCallState extends Handler } //***** Public Methods - + + /** * Start the simulated phone ringing * true if succeeded, false if failed |